LCTF2018 PWN easy_heap
花了几天做了这道题,其实应该不用花那么久时间的,但是最近注意力和记忆力下降太严重了,花的时间也变长了很多
这篇文章的主要目的是是为了梳理思路,因为做稍微复杂题目的时候,需要不断在脑中思考程序的理想变化,而这样思考的时候又难免需要回想一下刚刚所想的,于是用文字写下来思路的方法可以弥补记忆力下降的缺陷。所以主要是让自己看的,可能很多名词表达的不清楚。
先说一个点,就是这道题似乎魔改了malloc函数,使chunk在从unsotedbin里被分配出来的时候,会清空fd指针,如果不这么魔改的话,就可以直接freechunk,然后再分配这个chunk,再使用Puts功能把data区域的指向main_arena的fd指针输出出来
直接说明程序的几个功能:
1.malloc:创建一个malloc,并把chunk的地址返回到一段内存里,之后的malloc放在前一个malloc+16处,先输入size,并把size放到存放malloc的位置+8处,然后输入content,放入malloc的data区域中,输入content结束的时候会在size+1处地方以及输入完后的+1处置0,输入完后的+1处置0的目的我个人猜测是为了防止直接free然后再直接malloc然后填入8个字符覆盖前8字节就可以读取bk指针中的内容
2.free:先清空了size,然后释放malloc,然后再置0
3.puts:直接输出某一个malloc的data中的内容
因为开了pie,我们先尝试泄露libc基地址,想来想去最简单直接的就是泄露fd或bk指针的main_arena了
这道题的漏洞点很明显,就是通过溢出一个0x00字节,控制下一个chunk的inuse位为0。
那控制下一个chunk的inuse位为0能干嘛
一般来说,这个off by null是在启动一个chunk的时候,紧接着可以修改下一个chunk的inuse,那么就相当于是设置了一个具有malloc启动时的功能(在这里是具有puts,输出内容的功能)并且被识别为释放状态且可以合并的chunk。还有一个点,就是这个chunk只是会被识别为释放状态,但是他不会被加入到bin当中,只有通过一些方法例如修改其他free chunk的fd指针或bk指针来让他加入到bin链当中,也不一定是这种方法,后面会讨论
如刚刚所说,offbynull最重要的两个点,第一:此chunk会被识别为free chunk。第二:此chunk虽然被识别为free chunk但却拥有malloc之后的功能。
于是我依据malloc的功能特点想到两个思路
修改data的功能:将此chunk链入bin当中,此时因为可以自定义fd指针,达到任意修改内存的功能
输出data内容的功能:通过某一种方法将此chunk链入非tcachebin和非fastbin的头表当中,使其fd或bk指针指向main_arena,然后将其输出出来。
这道题是输出的功能,所以我们现在要找到一种方法能将chunk链入bin表当中
unsortedbin分割块法:
为啥只有unsortedbin有这种方法呢,因为smallbin分割完
这种方法能利用最主要的就是当一个在bin链中的块被分割后,他的fd指针和bk指针会给被分割完后剩余的那个块
于是,我们就可以让此chunk和他第一个低地址的chunk合并,然后将第一个低地址的chunk分配出去,就会让他的fd指针和bk给此chunk继承,然后我们再将其输出出来根据偏移量达成泄露libc基地址的目的
但是注意以上步骤的几个要点,就是在合并的时候,我们先要让此chunk被识别为free chunk,然后再释放低地址的chunk,这样才会合并,如果先释放低地址的chunk的话,此chunk因为不是经过free函数被释放掉的,所以不会合并。接着因为unlink机制,所以会检测此chunk的fd指针也就是main_arena的bk指针是否指向此chunk以及此chunk的bk指针也就是main_arena的fd指针是否指向此chunk,都不满足,所以也绕不过去,那怎么办呢
我们再来理一下,因为unlink检测机制的原因,所以我们要让那个被寻找合并的chunk是正常被链入bin当中的,也就是由free函数正常释放的,那既然不能是此chunk,那就只能是低地址的那个chunk了。所以我们先释放低地址的那个chunk ,但是要怎么让他合并呢,其实也很简单,只要能更改此chunk的后面那个地址的chunk的prev_sieze为0x200,然后再释放后面那个地址的chunk,就能使其将前面的2个chunk合并,这样就达到了合并的效果并且被寻找合并的chunk是正常被链入bin链当中的。
那么如何把高地址的那个chunk的prev_size设置为0x200呢,也很简单,就是直接依次释放:低地址、此chunk、高地址,但是注意,需要把这三个chunk放入unsortedbin才能合并。这样就会导致高地址的那个chunk的prev_sieze为0x200。
然后根据刚刚的思路,我们要将此chunk分配出去,然后off by null,但是因为是合并的chunk,所以会先把低地址分配出来,然后才是此chunk。这样会导致在此chunk还没变成具有malloc启动时的功能并且被识别为释放状态的chunk的时候就提前获得了main_arena指针,这样是不行的。
我们要的理想结果应该是在三个chunk都是分配状态的情况下,先释放此chunk进入tcachebin,避免使其先合并(因为我们要的是三个chunk一瞬间通过0x200合并),然后释放低地址的chunk进入unsoredbin,使fd指针指向main_arena,但是不要急着释放高地址的chunk,因为此chunk在tcachebin的原因,所以就算现在释放高地址的chunk也不会合并,那么我们现在应该将此chunk分配出来,然后再将利用offbynull漏洞把此chunk重新识别为释放并且可合并的状态,接着就可以释放高地址的chunk,使其将3个chunk合并,最后再分配7个把tcachebin中的chunk清空,然后把这个合并的chunk的低地址chunk分割出来之后,fd指针就会进入此chunk,再将其输出出来,之后再计算偏移量,就可以达到泄露libc基地址的作用。
接下来就是拿shell,其实这道题就复杂在泄露libc基地址,剩下的就很简单了
这里因为开了relro所以不考虑修改got表,直接修改free_hook吧
刚刚我们做到把低地址的chunk分配出去后,合并的chunk就只由原本的此chunk和高地址chunk组成,但是此时id为0的位置记录着此chunk的地址,那么如果我们再分配一个,就会再次把此chunk的地址给到id=9的位置,并且都是被放入到tcachebin当中,所以我们可以利用double free
先释放id从1到8当中的任意一个chunk,因为在这之前位置已经满了,并且待会需要free 2次和malloc 3次,还需要再一次free来释放位置
然后再释放0和9,此时的tcachebin当中的链应该是这样的
接着malloc一个,然后顺带把free_hook写入fd指针,此时的tcachebin当中的链应该是这样的
然后再malloc一个,其实bin中应该只剩个__free_hook
最后再malloc一个,就可以写入内容到free_hook。
文章到此结束,因为主要是为了让自己唤醒记忆加上我懒,所以图很少