【转帖】Pragyan CTF 19 逆向详解
转自:https://xz.aliyun.com/t/4429CTFtime平台上发现的一场比赛,记录一下其中的2道逆向题
# Feed_me
题目打开,发现一个`scanf("%s",s)`,明显有栈溢出倾向,而IDA将变量识别如下
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160044-1a9aa73a-4566-1.png)
注意后面的三个`atoi`,后两个的参数比较迷,相对`s`的偏移分别为10和20,这里我把`s`的类型重新定义为`char s`
因为这题保护全开,不是考察pwn,应该是考察栈上变量偏移的识别
那么我们输入的字符串每10个字符被解析成3个`int`型数据
主要的判断可以看做三元一次方程
```
input1 + input2 = num1
input2 + input3 = num2
input3 + input1 = num3
=>
input1 = num1 + num3 - num2 / 2
input2 = num1 + num2 - num3 / 2
input3 = num2 + num3 - num1 / 2
```
多说一句,解方程时把三个式子加起来除以二后,分别减去每一个式子,即可得到方程的解
根据`srand(time(0))`和`rand()`的特性:随机种子确定,生成的序列也确定,写出python脚本扔程序跑就可以了
但是我不太熟悉python对rand的处理,直接C程序跑出来,手动喂给程序了
## 补充
解题过程中有一些想法
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160119-2f49f582-4566-1.png)
这里num1,num2,num3都是`unsigned int`,而它们都被以`%d`的形式输出
因为`rand()`返回值为`int`型,但是必定返回一个正数,模10000后再乘以`-2`,相当于把一个绝对值很小的负数赋值给了`unsigned int`
以`%d`有符号数输出结果,是三个负数,是应该的
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160139-3b523902-4566-1.png)
可是num系列数据的实际类型是无符号的,是一个很大的正数
而我们输入的字符串被解析成了3个`int`数据,而且都是负数,在判断时,如何比较一个`正unsigned int`和`负int`?
看一下汇编代码
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160159-47787340-4566-1.png)
2个`int`数据`add`后与`unsigned int`比较,实际上应该是比较的二进制数据
只要二进制的32个bit相等,那么就判断相等
做一个小实验
```
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char**argv){
int num1 = -1;
unsigned int num2 = -1;
if(num1==num2){
printf("num1==num2");
}
return 0;
}
```
`num1`就是单纯的-1,而num2会是`0xffffffff`,是一个大正数
实际上`if`判断为真,会输出`num1==num2`
> 发现这个问题的原因主要是,我本地写C代码测试时,发现以`%d`输出预期的值input1、input2、input3时,都是长度为10的int数据,而连起来就是30长度的纯数字字符串,这不能被`atoi`识别,超过范围会返回`-1`,于是有了上文的一些想法,发现原来是二进制比较的问题
# Super Secure Vault
IDA打开,主逻辑如下
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160218-5266bd84-4566-1.png)
发现函数名都在,而且`getNum`和`mod`函数都接收了一个字符串作为参数,实际上是指向字符串的指针
先看一下`getNum`函数
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160241-60642b92-4566-1.png)
它的作用实际上是从字符串中取出一段数据并返回int64型
`mod`函数的行为也和名字一样
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160307-6f7f6812-4566-1.png)
注意到程序虽然用了`scanf`,但是保护全开,也就没有`REpwn`这种要改数据的可能性了,输入的长度姑且认为是30
因为`getNum`返回的值不受我们输入字符串的影响,只要动态调出来就可以了
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160335-805f5f5c-4566-1.png)
比如第一个值是27644437,要求`input % 27644437 == 213`
同样的,可以得出以下5个线性同余方程组
```
input % 27644437 == 213
input % 10459 == 229
input % 1489 == 25
input % 1046527== 83
input % 16127 == 135
```
和中国剩余定理有关,网上找个脚本解一下
```
from functools import reduce
def egcd(a, b):
if 0 == b:
return 1, 0, a
x, y, q = egcd(b, a % b)
x, y = y, (x - a // b * y)
return x, y, q
def Chinese_remainder(pairs):
mod_list, remainder_list = for p in pairs], for p in pairs]
mod_product = reduce(lambda x, y: x * y, mod_list)
mi_list =
mi_inverse = , mod_list) for i in range(len(mi_list))]
x = 0
for i in range(len(remainder_list)):
x += mi_list * mi_inverse * remainder_list
x %= mod_product
return x
if __name__=='__main__':
print(Chinese_remainder([(27644437, 213), (10459, 229), (1489, 25),(1046527,83),(16127,135)]))
```
出结果:
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160402-90bfbaa4-4566-1.png)
长度也符合要求,是一个符合条件的解
然后我们就被要求输入`password`
主要逻辑在`func2`中
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160422-9c3d9a04-4566-1.png)
我们第一轮输入的字符串会被追加"27644437104591489104652716127"
再被追加`0x3038`和`\x00`
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160437-a55eedae-4566-1.png)
然后基本就是查表了,由于程序开了PIE,我不知道怎么用angr秒解它,于是只能patch程序,用gdb下断点看了
!(https://xzfile.aliyuncs.com/media/upload/picture/20190313160453-aeb9e0c0-4566-1.png)
这里把两行中的`!=`改成了`==`,并且下断点观察矩阵中给出的值
密码随便输入`!!!!!!!!!!!!!!`
然后RAX中的值,也就是`cmp cl,al`中会给出应该输入的字符
收集一下就是:`pctf{R3v3rS1Ng_#s_h311_L0t_Of_Fun}`
我猜测程序中可能是手动把符合同余方程组的解都算了一遍?然后把数据填入那个巨型矩阵?
最后其他位置乱放了一些字符?
# 总结一下
这两道题的基本是一个递进的关系,其中第二题的函数编写值得学习一下,C语言如何处理这种大数的mod,之前我没有仔细想过,这段代码实现也没有彻底的理解......
确认过眼神,是个大佬! 看的出 是个大佬,收藏了 看不懂,是个大佬! 可以可以 学习了!感谢研究思路。
页:
[1]