PWN-就是干
程序管理如下结构体
如果输入的content
长度超过15,则会另外申请一块内存存放content
,原来content
数组的部分会用来存放content
指针,
删除结构体时存在double free
漏洞,适当构造内存申请释放的顺序就可以构造出堆块重叠,可以修改整个info
结构体
因为程序开启了PIE保护,想要利用修改结构体中的函数指针来控制RIP
前需要先leak binary,因为函数指针原先指向
的是0xd6c和0xd52两个地址,PIE保护下,代码地址最后三位仍然是固定的的,所以只需要将函数指针的最低字节修改为0x2d,
就可以将函数指针定位到0xd2d
这个地址,这个地址的代码是call puts
,这样删除堆块时,free(content)
就会变成puts(content)
,
适当构造content的内容就可以将函数指针的至连带输出,进而计算出程序的基址
因为输入选项的时候可以在栈上填充大量的数据,因此通过修改函数指针来做ROP,用puts函数来leak libc
通过泄露出来的__libc_start_main函数地址在libc-database找到了对应的版本,但是其他函数的地址却和找到的版本对不上,libc-db上也找不到对应版本,
不过运气不错,远程的libc中的system函数偏移和在libc-database中找到的相差不远,略微调整远程就返回了sh not found
这样的错误信息,这样就确定system
函数地址正确
最后要执行system('/bin/sh')
,原本想要往内存里写个/bin/sh
或者把某个函数的GOT写成system,但是远程ROP执行read老是挂,无奈只好去找’/bin/sh’的地址,
通过system函数返回的sh: XX not found
来确定当前地址的内容,再拿本机libc作为参照,不断微调找到正确的地址成功getshell
exp如下
RE 前年400
IDA稍微动态跟了下找到了验证函数,验证if
里面一大串的条件,最后抠出来是一个22元一次方程组,那python
处理下然后用numpy
解一下,注意下浮点取整就可以了
RE normal
程序将输入分成了几个部分来验证,每一部分验证正确后将这一部分的输入作为xor key来将下一个部分的验证代码还原出来
第一部分先将大括号里的前四个字符按位拆成8个字节,然后打了好长的一个表,然后做各种变换,和简单处理过的输入异或,最后和结果比对
把打好的表和最后的变换抠出来,拿结果异或回去就行了。。。
第二部分将6个字符通过各种位运算和结果作比较,爆破下就好(刚开始自己犯蠢烦了出题人好久,惭愧惭愧)
第三部分坑点来了。。。不知是何原因程序在还原这一段代码时有大概率出错,IDA很难解析出正确的汇编代码,多次尝试后找到了验证逻辑,
将输入的字符高4位低4位互换(0x61变成0x16),异或0x11后和结果对比,这一部分没写代码手动解,结果是0f_RE_a
第四部分和第三部分同样的问题,只是出错概率更大,多次尝试后放弃调试,直接手动还原出代码放到IDA里面分析,发现是输入和常量对比,
然后就还原出了整个flag(上一部分最后那个a
到这一部分就不对了,而且这一部分的代码还是用这个a
异或出来的,略有点神奇)