2019 RCTF WriteUp
🏛️ 组织架构
RCTF 2019 由国家创新与发展战略研究会主办,360 集团与南京赛宁信息技术有限公司联合承办
命题团队为 r3kapig 战队
⚙️ 赛制与规模
赛制:采用线上 CTF(Capture The Flag)解题模式(Jeopardy-style)
时间:比赛于 2019 年 5 月举行
规模:赛事在 CTFtime 上的权重为 52.89 分,吸引了全球众多顶尖战队参与
Misc
draw(签到)
一门叫 logo 的冷门编程语言,在线编译器运行得到图片

flag 就是 RCTF_HeyLogo
Reverse
babyre1(XXTEA)

IDA 反编译,首先输入必须严格 16 个字符,然后调用了 sub_C00(v6,16,&ptr),进去看看

在这里面又调用了 sub_BE0(),负责把一个字符转换成十六进制数值

关键代码是 v1 = a1 - 48;,例如:'4' = 52 - 48 = 4

接下来重点分析 sub_1180() 函数
首先是长度检查:
if ((a2 & 3)==0),检查变量a2的最低两位(bit 0 和 bit 1)是否均为 0,也就是是否为 4 的倍数再复制数据
memcpy(v8, src, a2);然后调用
sub_CE0(v7,(a2/-4),a3);,因为前面sub_C00(v6,16,&ptr)中,一次取两个字符v11 = sub_BE0(*v4);v12 = sub_BE0(v4[1]);,所以a2 = 16 / 2 = 8最终调用结果是
sub_CE0(v7,-2,key)
这非常像 XXTEA decrypt,因为 XXTEA 的函数通常
1 | /** |
因为 a = -2 < 0,所以可以确定这是解密函数

现在需要去找 Key,但是 IDA 识别参数有问题

在 Linux x64 参数传递规则中
| 参数 | 寄存器 |
|---|---|
| 第1个 | RDI |
| 第2个 | RSI |
| 第3个 | RDX |
| 第4个 | RCX |
| 第5个 | R8 |
而在函数内部,我们的 a3 就是 Key,参数的第三个,也就是去看传给 RDX 寄存器的值即可

这样就找到 Key 了

提取出来是 c7e0c7e0d7d3f1c6d3c6d3c6ced2d0c4,GBK 编码后是 青青子衿悠悠我心
现在去看解密函数内部,代码中大量出现 1640531527,可以推测这就是常量 DELTA,几乎可以确定这就是 XXTEA 加密算法
| 特征 | 伪代码 |
|---|---|
| delta 常量 | ✅ 1640531527 |
| MX公式 | ✅ 位移+异或+加法 |
| key[4] | ✅ a3+4*i |
| n正负控制 | ✅ |
| 52/n轮数 | ✅ |

执行完 XXTEA 函数后还去除了 Padding
1 | sub_CE0(v7, (a2 / -4), a3); |
代码 v9 = v7[a2 - 1];,因为 a2 = 8,所以 v9 = v7[7],也就是取解密结果的第 8 个字节
然后 if (a2 > v9 && v9 <= 4),也就是说程序认为最后一个字节表示 Padding 长度
程序开头给了提示 __printf_chk(1, "Input right flag you can got 'Bingo!' :");
则实际有效数据是 Bingo!,6 个字节,缺少了两个字节,所以最后一个字节需要补齐 0x02
但还是缺少了解密后的数据,查看汇编发现后面调用了 sub_13D0 函数
sub_13D0() 是 CRC16-CCITT 校验函数,作用是对 XXTEA 解密后的数据计算 CRC16,用来进一步验证输入是否正确
代码中多次出现 v4 ^= 0x1021u;,对应着 CRC16-CCITT 的标准多项式 x^16 + x^12 + x^5 + 1

继续往后看发现对每个字节都异或 0x17

对 Bingo! 进行异或 0x17
1 | 42 ^ 17 = 55 |
明确了 6 个字节,但是 XXTEA 输出的是 8 字节,最后一字节在上面 Padding 确认是 0x02
第七个字节我们可以循环爆破,
1 | import xxtea |
