-
-
[原创]CTF2018第十一题分析(qwertyaa)
-
发表于: 2018-7-8 03:42 3455
-
终于在题目开始变难后写出了一题比较水的...
首先我们分析这个名为3pigs
的程序。
其中有三只猪和一只额外的飞猪、5种操作:
Bug也很明显:操作1中读入房内数据的大小限制是0x200个字节,比malloc
的空间多16个字节;操作5明显是个修改外部内容的功能;读入数据后,数据的后一字节会被置0(数据最后一字节为0x10除外);猪的名字采用strncpy
赋值,猪的名字结束处不会被强制赋值为0。
程序中的漏洞是很明显,对于我们攻击者(大灰狼)来说就像前两只小猪一样,但最后一只小猪的房子相比就相当结实了,这些漏洞似乎不怎么好利用啊。所以我作为一只不够格的大灰狼,必须要借助推土机(即各位大神的文章)。
另外,程序里面专门申请了一段空间画了个猪头,不知何意:
首先这里没什么东西是被放在堆里的,而我们可以修改的内容却仅限于堆,这使得我们不能获得这个开启了PIE的程序的基址,只能另谋出路。
找来找去,我发现在上一题和上上题pwn中,holing大佬的writeup中提到了house_of_orange,这种攻击只需要我们得到libc和堆基址即可。
//话说这种攻击方法似乎在libc-2.24中被加难了,在(似乎是未来的)libc-2.28中会彻底无效,还好服务器是16.04(libc-2.23)。
首先我们做几个操作:
先创建并free一些buf,这里的secret()是阻止两个unsorted_bin合并,0号房子是阻止unsorted_bin和top_chunk合并。
这样在1、2号猪原来申请的空间+0x8的位置(也就是现在的bk)就分别变成libc和堆上一个指针。
为了这个指针能被打印,我们可以再申请一个2号猪的房间,2号猪的名字恰好8个字节,printf输出时恰好可以输出后面的地址。
这里有一个问题是show操作只能进行一次,所以两个地址必须同时泄露,而只有一只猪的名字恰好长度为8。为了解决这个问题,我们可以先让2号猪申请到原1号猪的“尸体”上,这样在+0x7处留下一个'l'。接下来为了保证这里不被覆盖,我们不能让它变成unsorted_bin的fd末尾的0(64位系统内地址最高位常为0),所以我们free掉0号猪和这里的2好猪,让这个空间被top_chunk合并。具体程序如下:
这样我们就得到了libc+0x3C4B78和heap+0xeb0(具体值本地cat maps后得出,因为肯定是固定的)的地址。
同时,我们需要做一些整理,确保接下来申请的内存都在top_chunk中进行:
关于house_of_orange的讲解,我觉得这篇最为清晰。这里由于可以进行free,从而很方便的构造出unsorted_bin所以我们不需要house_of_orange中前半段关于top_chunk的操作(似乎也不能那么做,因为我们可写的地址有限),只需要后半段修改一个unsorted_bin和最后申请一个空间的操作。
house_of_orange里需要一个很大的空间,而我们这里可写的地址非常有限,所以大部分内容都要在create操作时写入,然后free掉这些房子。
内存布局如下(先create成上面的,再free后调用一次secret变成下面的):
其中需要修改的fd、bk在secret创建后才被libc修改为正确值,我们需要用editSecret来把它修改成我们希望的错误值,这里由于bk后紧跟的数字要改为2,所以不能让它被程序内读入函数置为0,由于64位系统内地址最高位常为0,我们恰好可以用p64(ilap-0x10)[:7]
解决问题。
这部分代码如下:
需要注意的是holing提到的
其实house of orange还有一个坑,只有当libc的低DWORD为负数的时候,才有效,具体是要让_IO_list_all中第一个元素(在main arena上)不过这个check,不然就会通过虚表call到main arena上面去,具体跟一下就知道了。。。
大概就是攻击不会都每次成功。
完整exp如下:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)