这是一道pwn题,需要利用程序的漏洞来getshell
然后读取存放在远程服务器上的flag
文件。
使用checksec club
可以查看到以下信息。(用python的pip工具安装pwntools包后就可以使用checksec了,安装命令为 pip install pwntools 下载慢可以找下清华园的镜像)
简单介绍一下含义:
小结: 需要找程序基址,GOT表可以利用。
用ida进行静态分析,可以看到流程很清晰,首先程序菜单如下:
分别来看下每个选项的做用
先看1号
功能get a box
IDA F5后代码如下,相关全局变量的做用已经在分析后rename了
可以看出,这个功能号就是用来申请一个堆空间,不过这里对大小有限制,分别由calcMinSiz
和calcMaxSize
来计算当前申请的堆空间是不是满足条件。
在看申请堆大小限制前先去看下这些全局变量的部局。
boxSize
数组有7个元素,第0跟第6个元素已经被设定了初始值,第1到第5为0 这5个元素就是用来记录申请到的box的大小。 而第0跟第6是用来限制堆大小的,等下看到calcMinSize
和calcMaxSize
就会明白。
boxBuff
没有特殊,第1到第5总共5个元素用来记录申请的box的堆地址。
boxUsed
没有特殊,第1到第5总共5个元素用来记录5种box类型哪些已经被使用了。
接着来看一下堆大小的限制。
由以上代码可以看出,申请的当前空间要比排它前面的大0x10比排在它后面的小0x10,又由于boxSize的第0与第6个元素被初始化为了8和0x1000所以我们可以申请的堆大小范围在0x18 ~ 0xFF0
之间。
get a box
中的代码功能分析完毕,并且没有发现漏洞存在。继续看下一个功能。
2号
功能destory a box
这里出现了一个新的全局变量isCanRelease
。
结合程序逻辑可以看出,只有small
与normal
类型的box可以被free。并且由于free后没有重置boxUsed
的值,所以每种类型的box只能使用一次,这里还有一个很明显的漏洞存在,free后的buffer指针没有被清空,所以可能导致double free
的利用。
3号
功能leave me a message in box
向box中写入数据,这里也有一个漏洞,写入次数比buffer多了一字节,所以可以用off-by-one
,我没有用这种方式来利用,所以这里给一个off-by-one
的相关资料
4号
功能show message in box
这里到没有什么多说的,很单纯的显示buffer内容,可以用来泄露地址。
5号
功能guess a random number
很好玩的小游戏,预测伪随机数,猜中了返回随机种子,猜错了会告诉你正确的随机数,这里的随机种子就是seed的全局变量的地址,由于程序开启了PIE
所以这里是获取程序基址的地方。
6号
功能exit
输入个名字,打印信息并退出,没有利用点。
小结: 可以通过猜随机数获取程序基址,利用free时不清空buffer指针来构造堆中数据和使用double free
。
这部分主要是给自己做个记录,第一次玩堆,看了很多文章,这几篇不错的记录一下备忘。
相关知识了解了以后,就要根据本题的实际情况来构造payload了
首先先通过猜伪随机数来获取程序的加载基址,怎么去预测伪随机数,这里有篇文章写的很好。Linux随机数分析
看过文章可以得到一个公式 r[i] = (r[i-3] + r[i-31]) & 0x7fffffff
获取基址的代码如下:
由于只有small
和normal
的box可以free,所以先创建normal
与big
,然后释放掉normal
,之后再创建little
与small
,并通过normal
的指针来构造fake chunk
,下面结合实际代码与内存来演示如何构造fake chunk
首先申请400与450大小的normal box
和big box
,释放normal box
,再申请150与200大小的little box
和small box
,这样normal box
与little box
的指针是同一个地址,又由于本程序中写入数据只看指针与box大小,所以此时的normal box
的指针可以同时操作little box
和small box
的内存,这样就可以来构造fake chunk
, 代码如下:
以上代码执行后内存数据如下:
开始布置fake chunk
的内存数据,就用上面的地址来描述,fake chunk
的prev size
和size
放在0x0000558DC84DA420
和0x0000558DC84DA428
处,0x0000558DC84DA430
处开始写入fd
与bk
,由于unlink
新增加的检测,所以这里需要分别写入一个指向fake chunk
头部的指针的地址减0x18和0x14,这样就能满足FD->bk == p && BK->fd == p
,p表示当前chunk
,FD
表示当前chunk
的fd
指向的chunk
, BK
表示当前chunk
的bk
指向的chunk
,所以这里选择全局变量boxBuff - 0x18
和boxBuff - 0x10
的地址来写入。
然后还要修改0x0000558DC84DA4B0
处的prev size
和0x0000558DC84DA4B8
处表示前chunk
是否空闲的最低位为0
这样当释放此处的堆时会因为向前合并而调用unlink
,所以这里根据实际情况,写入0x90
和0xD0
,构造fake chunk
后内存布局如下(A
为填充数据,不影响结果):
完整的利用脚本如下:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!