本场比赛只有3个逆向,前两个还都是比较水的简单题,最后一题虽然玩脱了但还算是有意思,发出来学习交流下=-=
Interesting Pointer
分析
一堆变量初始化直接放过,可以看到它读入data文件,所有内容放到了buff中
接着判断strlen和ftell给出的文件结尾是否相等,相等则结束
也就意味着文件中必须存在\x00来截断strlen
注意文件读取几乎晚于所有变量初始化,因此这里存在溢出点
处理过程如下
依次调用3个func,将返回值放在buff1、2、3中,要求buff[3]的值为0
3个Func的功能依次为exchange(buff[x], buff[y]), calc1(buff[x], buff[y]), calc2(buff[x], buff[y])
exchange固定
return 1
calc1具体过程为:
return abs(x + y) - abs(x) - abs(y) + 2
calc2具体过程为:
return abs(x) + abs(y) - abs(x + y) + 2
而三次函数的参数和返回值分别是
x = 3
y = 4
buff[1] = exchange(buff[x], buff[y])
buff[2] = calc1(buff[1], buff[2])
buff[3] = calc2(buff[2], buff[3])
这里我把x和y分离出来写的原因是,中间读入可以覆盖掉初始化,从而控制exchange的两个参数
首先如果不溢出的话,calc2想要返回0可以通过0x7fffffff和0x80000001来实现
由|x|+|y|>=|x+y|,即|x|+|y|-|x+y|+2>=2可知正常情况下无解
但是|x|+|y|-|x+y|+2==0x1 00000000却是有解的,令x、y互为相反数,则|x|==|y|==0x7fffffff
而calc1却不可能制造出这两个返回值,因此无解
calc1也很容易返回0, 有且仅有令两个操作数其中之一为-1即可
是吗?
令x, y都为正数, x+y=0x80000001则|x+y|=0x7fffffff,|x|+|y|-|x+y|正好就是2
返回buff[3]为0后,通过之前保存的buff[3]和buff[5]来得出flag
这里其实可以大概猜出来,关键的两个变量就是buff[3]和buff[5]了
然而比赛的时候我没注意这里23333导致从别的角度去考虑造成一堆多解
期望解
通过exchange交换calc1和calc2的位置,同时控制buff[3]为-1,则可满足条件
即构造data为
b"1"*(20+4*3) + b"\xff\xff\xff\xff" + b"\x07\x00\x00\x00" + b"\x08\x00\x00\x00"
生成flag的关键参数就是-1和8
非预期解
交换顺序非预期
这是最容易看到的多解,7和8两个数作为交换参数,当然8和7也是可行的,而生成flag提取的却只有最后一个参数
这个只有1种情况,试一下也可以接受
calc1的参数非预期
利用之前所说的calc1的溢出计算,使x和y都为正数的情况下和为0x80000001,则可同样使返回值为0
例如构造data为
b"1"*(20+4*3) + b"\xff\xff\xff\x7f" + b"\x07\x00\x00\x00" + b"\x08\x00\x00\x00"
通过交换过后的calc2来获得其中一个参数,由于exchange返回值只为1,从而可知calc2可获得的正数结果只有2
那么对应只要控制buff[3]为0x7fffffff即可
这样产生了2个多解
exchange的参数非预期
既然可以交换,凭什么非要交换calc1和calc2?
在buff前面有那么多可控字段,完全可以在其中填充上一个函数地址,然后通过exchange将其与calc2的指针变量交换,这个函数地址可以是calc1,还可以是随便什么返回0的函数
例如构造data为
b"\x00"*(20+4*3) + b"\xe8\x1f\x40\x00" + b"\x08\x00\x00\x00" + b"\x03\x00\x00\x00"
这里是利用strlen配合头部为'\0'从而返回0的示例
由于该程序没有ASLR,因此地址全部固定
这种情况下产生的多解就多了去了,一方面交换目标可以随意放置,即使在限制长度的情况下每个交换函数也有前面4个位置可以选择
而更关键的是此种情况下校验通过与否根本与第三个参数无关,因此生成的flag完全没有意义,硬要说的话每个buff[5]都有0x100000000个buff[3]可以组合
函数指针覆盖非预期
这个是最容易想到的,也是我最早提交的多解
反正都能溢出,凭什么还要通过exchange函数
例如构造data
b"1"*(20+4*3) + b"\xffffffff" + b"\x00"*(2*4) + b"\x97\x13\x40\x00" + b"\x97\x13\x40\x00"
直接将calc2处的指针修改为calc1
具体情况与上面exchange的情况相同,只不过不用通过exchange多此一举,缺点就是长度会长一些
总结
总体来说基本上是一个pwn题改的逆向,单纯题目可以说挺有意思,作为赛题而言非预期过多还不算就有点坑人了..
如果把exchange中改为单向赋值就能限定住交换参数的顺序了,不过这个有符号数的溢出还是有点尴尬的23333
另一方面如果能提前说明输入要求最短也还算勉强可以接受,我觉得把函数指针放到全局变量去避免被覆盖会好点儿~
期待有dalao能找到更多的非预期发出来交流~