-
-
[看雪CTF.TSRC 2018 团队赛][分析] 第二题 半加器
-
2018-12-3 23:30 2320
-
第二题 半加器
观察
题目给出了一个 32 位控制台 Windows 应用程序 Exam.exe
:
$ file Exam.exe Exam.exe: PE32 executable (console) Intel 80386, for MS Windows $ strings Exam.exe <...略...> c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\xmemory0 <...略...>
看起来没有什么保护,它是一个 MSVC 2017 编译的 C/C++ 程序。
运行一下:
N:\pediy\2>Exam.exe Please Input:hasjkhdasjkdsda 输入错误;
看起来就是一个经典的输入 flag,输出对错的挑战。
分析
简单用 IDA Pro 分析一下,顺着 MSVCRT 的初始化逻辑脑补一下,可以找到 main 在 0x4A19B0 处。
逻辑大致如下:
int main(int argc, char* argv[]) { signed int len; fprintf(stdout, "Please Input:"); scanf_s("%s", g_input, 30); len = strlen(g_input); if ( len > 30 || len < 10 ) { puts("输入错误;"); exit(0); } tcscpy_s(g_p_buf, 30, g_input); if ( g_p_buf[7] != 'A' ) { puts("输入错误;"); exit(0); } j_MangleInput(g_p_buf); return 0; } void MangleInput(char *buf) { unsigned int i; buf[7] = 35; for ( i = 0; i < strlen(buf); ++i ) buf[i] ^= 0x1Fu; }
仅仅检查了长度是否大于等于10,小于30,以及 0-based index 下 [7]
处是否是 'A'。
若不是的话则报错,这很奇怪,因为按照规则,若答案正确的话应该输出明确的正确提示。
尝试输入满足以上条件的串 0123456A89
,程序确实没有任何输出。
显然,程序中存在一些隐藏的检查 flag 的逻辑。观察可以发现程序没有写文件和外部环境的行为,因此可以排除规则中所允许的“重启验证”,flag 应该就在本次执行中被验证了。考虑到输入的来源就这么两处,只要死咬着输入(和被变换过的输入)不放,肯定能找到处理 flag 的地方。
在下一个教科书般的内存读取断点之前,让我们先试试查找到 g_input
和 g_p_buf
的交叉引用,结果发现在 main
和 MangleInput
以外另有两个函数 0x495810 (命名为 InitBuffer
) 和 0x49DC80 (命名为ValidateFlag
)引用到了 g_input
。
这两个函数的逻辑也很简单:
void InitBuffer() { g_p_buf = (char *)malloc(30); } void ValidateFlag(char *expected) { unsigned int i; if ( expected ) { for ( i = 0; i < strlen(expected); ++i ) expected[i] ^= 0x1Cu; if ( !strcmp(expected, g_p_buf) ) { fputc(stdout, 'o'); fputc(stdout, 'k'); } } }
看起来,这里才是真正的检查 flag 的地方,注意到与之比较的标准答案来自于函数参数,寻找交叉引用得到该参数:
ValidateFlag("invalid argument");
解决
至此,已经可以计算出输入惹:
from pwn import * ans = list(xor('invalid argument', 0x1c, 0x1f)) ans[7] = 'A' print ''.join(ans) # jmubojgAbqdvnfmw
提交即可。可以看出来多出来的那个判断 [7]
的地方是为了对付规则中的 “flag只能包含字母和数字”。
分析 2
然而,题目里的谜团还没有彻底解开,这个检查 flag 的函数到底是在哪里调用的呢?
继续追踪交叉引用,可以找到是在 0x4957B0 这个函数里调用 atexit()
注册成退出时执行的函数的。
Trivia
其实这里的 [7]
的 7 都是用 1 * 7
算出来的,不过直接被 Hex-Rays 的常量折叠折成最终结果辣。
吐槽
简单的题目的 writeup 写起来好累呀。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课