-
-
[原创] CTF2019 Q1 第一题write up
-
2019-3-10 13:04 2491
-
环境配置
系统 : Windows xp
程序 : 流浪者
要求 : 输入口令
使用工具 :IDA pro / Ollydbg / peid
开始分析
首先将程序拖入peid中查看下,程序提示:Microsoft Visual C++ 6.0
看来这就是一个传统的CrackMe程序,输入flag即可夺旗成功。我们直接用od附加程序,然后用中文搜索引擎插件查看字符串信息:
中文搜索引擎 地址 反汇编 文本字符串 00401059 mov dword ptr ds:[eax],cm.00403240 X@ 004010F9 mov ecx,cm.00404028 @2@ 004010F9 mov ecx,cm.00404028 X@ 00401139 mov ecx,cm.00404028 @2@ 00401139 mov ecx,cm.00404028 X@ 00401155 push cm.004021F9 给5@ 004012CB mov dword ptr ds:[eax],cm.004033A0 B@ 004013E7 mov dword ptr ds:[ecx],cm.0040347C B@ 00401572 push cm.00403554 关于(&A)... 004015D5 push cm.00402289 赴6@ 00401655 push cm.004022A9 肛6@ 0040177B push cm.00403568 恭喜! 00401780 push cm.00403560 pass! 004017BB push cm.00403578 错了! 004017C0 push cm.00403570 加油! 0040180A mov [local.17],cm.004035C0 KanXueCTF2019JustForhappy 00401811 mov [local.44],cm.00403580 abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ 004018E8 push cm.004035DC 请输入pass! 004018FA mov [local.3],0x0 (Initial CPU selection)
这里,程序中的关键信息便一目了然。我们直接双击错了!
定位到关键代码段:
004017B0 /$ 55 push ebp 004017B1 |. 8BEC mov ebp,esp 004017B3 |. 83EC 44 sub esp,0x44 004017B6 |. 53 push ebx 004017B7 |. 56 push esi 004017B8 |. 57 push edi 004017B9 |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL 004017BB |. 68 78354000 push cm.00403578 ; |错了! 004017C0 |. 68 70354000 push cm.00403570 ; |加油! 004017C5 |. 6A 00 push 0x0 ; |hOwner = NULL 004017C7 |. FF15 00324000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA 004017CD |. FF15 0C304000 call dword ptr ds:[<&KERNEL32.GetCurrent>; [GetCurrentProcess 004017D3 |. 8945 FC mov [local.1],eax 004017D6 |. 6A 00 push 0x0 ; /ExitCode = 0 004017D8 |. 8B45 FC mov eax,[local.1] ; | 004017DB |. 50 push eax ; |hProcess 004017DC |. FF15 00304000 call dword ptr ds:[<&KERNEL32.TerminateP>; \TerminateProcess 004017E2 |. 5F pop edi 004017E3 |. 5E pop esi 004017E4 |. 5B pop ebx 004017E5 |. 8BE5 mov esp,ebp 004017E7 |. 5D pop ebp 004017E8 \. C3 retn
然后在call MessageBoxA
函数处下断,运行程序,我们直接输入一个口令如123456
引发断点。断下后,按下Alt + k
查看函数调用栈:
调用堆栈: 主线程 地址 堆栈 函数过程 / 参数 调用来自 结构 0012F690 0040187F cm.004017B0 cm.0040187A 0012F68C 0012F794 004019C8 cm.004017F0 cm.004019C3 0012F790 0012F860 73D324C0 cm.00401890 MFC42.73D324BD 0012F85C 0012F870 73D323BF MFC42.73D3243E MFC42.73D323BA 0012F86C 0012F8A0 73D9DEAD MFC42.#4424 MFC42.73D9DEA8 0012F89C 0012F8C4 73D33244 包含MFC42.73D9DEAD MFC42.73D33241 0012F8C0 0012F914 73D31BF1 包含MFC42.73D33244 MFC42.73D31BEB 0012F910 0012F994 73D31B9B 包含MFC42.73D31BF1 MFC42.73D31B95 0012F990 0012F9B4 73D31B05 包含MFC42.73D31B9B MFC42.73D31AFF 0012F9B0 0012FA14 73D31A58 MFC42.#1109 MFC42.73D31A53 0012FA10 0012FA34 73DC847D MFC42.#1578 MFC42.73DC8478 0012FA30 0012FA60 77D18734 包含MFC42.73DC847D USER32.77D18731 0012FA5C 0012FA8C 77D18816 ? USER32.77D1870C USER32.77D18811 0012FA88 0012FAF4 77D2927B USER32.77D1875F USER32.77D29276 0012FAF0 0012FB30 77D292E3 USER32.77D291B3 USER32.77D292DE 0012FB2C 0012FB50 77D4FF7D USER32.SendMessageW USER32.77D4FF78 0012FB4C 0012FB54 00A8014E hWnd = A8014E 0012FB58 00000111 Message = WM_COMMAND 0012FB5C 00000001 age = Notify = MENU/BN_CLICKED... 0012FB60 008C0134 hControage = 008C0134 ('验证',clas 0012FB68 77D465D2 USER32.77D4FF3C USER32.77D465CD 0012FB64 0012FB84 77D25E94 USER32.77D25491 USER32.77D25E8F 0012FB80 0012FC08 77D3B082 USER32.77D25238 USER32.77D3B07D 0012FC04 0012FC28 77D18734 包含USER32.77D3B082 USER32.77D18731 0012FC24 0012FC54 77D18816 ? USER32.77D1870C USER32.77D18811 0012FC50 0012FC58 77D3B036 包含USER32.77D18816 USER32.77D3B030 0012FCB8
单击可查看函数体,这里用IDA将关键汇编代码段转换成伪C代码:
int __thiscall sub_401890(CWnd *this) { struct CString *v1; // ST08_4@1 CWnd *v2; // eax@1 int v3; // eax@1 int result; // eax@2 int input_data[26]; // [sp+4Ch] [bp-74h]@7 int index; // [sp+B4h] [bp-Ch]@3 char *my_str; // [sp+B8h] [bp-8h]@1 CWnd *v8; // [sp+BCh] [bp-4h]@1 v8 = this; v1 = (CWnd *)((char *)this + 100); v2 = CWnd::GetDlgItem(this, 1002); CWnd::GetWindowTextA(v2, v1); v3 = sub_401A30((char *)v8 + 100); my_str = CString::GetBuffer((CWnd *)((char *)v8 + 100), v3); if ( strlen(my_str) ) { for ( index = 0; my_str[index]; ++index ) { if ( my_str[index] > 57 || my_str[index] < 48 ) { if ( my_str[index] > 122 || my_str[index] < 97 ) { if ( my_str[index] > 'Z' || my_str[index] < 'A' )// a - z,A -z,0 - 9 wrong_4017B0(); else input_data[index] = my_str[index] - 29; } else { input_data[index] = my_str[index] - 87; } } else { input_data[index] = my_str[index] - '0'; } } result = check_4017F0((int)input_data); } else { result = CWnd::MessageBoxA(v8, "请输入pass!", 0, 0); } return result; } int __cdecl check_4017F0(int input_data) { int result; // eax@6 char Str1[28]; // [sp+D8h] [bp-24h]@4 int v3; // [sp+F4h] [bp-8h]@1 int index; // [sp+F8h] [bp-4h]@1 index = 0; v3 = 0; while ( *(_DWORD *)(input_data + 4 * index) < '>' && *(_DWORD *)(input_data + 4 * index) >= 0 ) { Str1[index] = aAbcdefghiabcde[*(_DWORD *)(input_data + 4 * index)]; ++index; } Str1[index] = 0; if ( !strcmp(Str1, "KanXueCTF2019JustForhappy") ) result = success_401770(); else result = wrong_4017B0(); return result; }
逻辑推理
可以看出这里用input_data数组的元素作为索引,取aAbcdefghiabcde数组内的值,最终将它们组成一个子串和KanXueCTF2019JustForhappy
对比,相等则成功。
所以这里input_data数组的元素是由offest(偏移值)组成,而偏移值按以下公式取得y = F(my_str - x)。
那么首先算出偏移值,然后将偏移值加上减去的x即可得到flag。
编写代码
按照以上逻辑,编写如下python代码:
abc_str = 'abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ' target_str = 'KanXueCTF2019JustForhappy' offest_list = [] for ch in target_str: offest_list.append(abc_str.find(ch)) flag = '' for offest in offest_list: if offest >= 0 and offest <= 9: flag += str(offest) if offest >= 10 and offest <= 35: flag += chr(offest+87) if offest >= 36 and offest <= 61: flag += chr(offest+29) print flag
夺旗成功
运行程序,得到flag为:
➜ playground python test.py j0rXI4bTeustBiIGHeCF70DDM
输入flag,程序提示恭喜
,夺旗成功。
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
最后于 2019-3-10 13:06
被胡八一编辑
,原因:
赞赏
他的文章
看原图