首页
社区
课程
招聘
[原创]2017秋季赛第二题Writeup
2017-10-27 12:04 2731

[原创]2017秋季赛第二题Writeup

2017-10-27 12:04
2731
 

感慨一下,作者声东击西的思路真**清奇。简单说一下解题思路,程序真正的校验逻辑被隐藏了,解题的关键是找到真正的校验过程,然后反推key。

0x00 主函数

.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main           proc near               ; CODE XREF: start+AF↓p
.text:00401000
.text:00401000 argc            = dword ptr  4
.text:00401000 argv            = dword ptr  8
.text:00401000 envp            = dword ptr  0Ch
.text:00401000
.text:00401000                 push    offset aCrackmeForCtf2 ; "\n Crackme for CTF2017 @Pediy.\n"
.text:00401005                 call    sub_413D42
.text:0040100A                 add     esp, 4
.text:0040100D                 mov     key_41B034, 2
.text:00401017                 call    sub_401050       ; 读取输入
.text:0040101C                 call    checkA_401090    ; 假的校验分支1
.text:00401021                 call    checkB_4010E0    ; 假的校验分支2
.text:00401026                 mov     eax, key_41B034
.text:0040102B                 test    eax, eax
.text:0040102D                 jnz     short loc_40103F
.text:0040102F                 push    offset aYouGetIt ; "You get it!\n"
.text:00401034                 call    sub_413D42
.text:00401039                 add     esp, 4
.text:0040103C                 xor     eax, eax
.text:0040103E                 retn
.text:0040103F ; ---------------------------------------------------------------------------
.text:0040103F
.text:0040103F loc_40103F:                             ; CODE XREF: _main+2D↑j
.text:0040103F                 push    offset aBadRegisterCod ; "Bad register-code, keep trying.\n"
.text:00401044                 call    sub_413D42
.text:00401049                 add     esp, 4
.text:0040104C                 xor     eax, eax
.text:0040104E                 retn
.text:0040104E _main           endp

主函数的逻辑很清晰,但这是作者给少年们下的套,因为checkA_401090处的限制条件和checkB_4010E0处的限制条件是自相矛盾的,所以找不到解能通过这段校验。

0x01 栈溢出

这是第一个线索,读取用户输入的代码存在栈溢出,并且可以劫持返回地址,所以程序的执行流是可以重定向的,这也暗示了另一个校验过程的存在:

.text:00401050 sub_401050      proc near               ; CODE XREF: _main+17↑p
.text:00401050
.text:00401050 var_C           = dword ptr -0Ch
.text:00401050
.text:00401050                 sub     esp, 0Ch
.text:00401053                 push    offset aCodedByFpc ; " Coded by Fpc.\n\n"
.text:00401058                 call    sub_413D42
.text:0040105D                 add     esp, 4
.text:00401060                 push    offset aPleaseInputYou ; " Please input your code: "
.text:00401065                 call    sub_413D42
.text:0040106A                 add     esp, 4
.text:0040106D                 lea     eax, [esp+0Ch+var_C]
.text:00401071                 push    eax
.text:00401072                 push    offset aS       ; "%s"
.text:00401077                 call    _scanf          ; 使用scanf直接向栈上拷贝数据,造成了溢出  
.text:0040107C                 lea     eax, [esp+14h+var_C]
.text:00401080                 add     esp, 14h
.text:00401083                 retn
.text:00401083 sub_401050      endp

0x02 可显示字符和隐藏的代码

相信很多少年都使用不可显示字符本地过了校验,但按照比赛规则,最终的flag只能包含字母和数字,所以无法提交。

 

其实可显示字符这一个条件指出了隐藏代码所在的区域。猜测作者把真正的校验逻辑藏在某个地址,然后通过栈溢出漏洞劫持控制流,跳转过去执行,由于覆盖的地址来自用户输入,所以跳转的地址一定可以看成数字/字母ASCII编码的组合。这是第二个线索。

 

然后很蛋疼的事情就来了,在公司加班,搜索各种可疑指令的机器码,找了一晚上,没找到...

 

回家之后又玩了一会儿,笔记本上只有windbg,反正没有反调试就走起了。突然想起来IDA里有一块很可疑的区域:

.text:00413131                 db 83h, 0C4h, 0F0h
.text:00413134                 dd 20712A70h, 0F1C75F2h, 28741C71h, 2E0671DDh, 870F574h
.text:00413134                 dd 74F17169h, 0DC167002h, 0EA74C033h, 0DC261275h, 0F471E771h
.text:00413134                 dd 6903740Fh, 0EB75EB70h, 0FDF7069h, 22712C70h, 0B8261F7Dh
.text:00413134                 dd 2B741E71h, 3E067169h, 870F57Ch, 7CF17169h, 0DC197002h
.text:00413134                 dd 41B034A3h, 75E77400h, 0E571DC12h, 7CDCF271h, 0E9706903h
.text:00413134                 dd 6965E97Dh, 70B8DC70h, 3E1D7127h, 710F1971h, 0DD257019h
.text:00413134                 dd 0F6700571h, 71DD0870h, 700270F2h, 70580F14h, 0F1171ECh
......

0x31是数字“1”的ASCII编码,0x41是字母“A”的ASCII编码,这地址来的太诡异了,像是精心设计的,正好匹配第二条线索。并且递归下降的IDA没有识别出.text段的这段内容,说明正常执行流程很可能不会光顾这里,这又很符合第一条线索。

 

其实,我看这段东西不爽很久了...

 

很自然的,把溢出地址换成“11A”(0x413131)试了一下,发现跳转后的指令先调整了esp寄存器,并且正好保护了我们的输入:

00413131 83c4f0          add     esp,0FFFFFFF0h
00413134 702a            jo      image00400000+0x13160 (00413160)
00413136 7120            jno     image00400000+0x13158 (00413158)

后面两个条件跳转,有很明显的花指令特征。

 

然后......就没有然后了。

 

写到这儿感觉差不多了,基本玩躲猫猫的整个心路历程都在这里了。感谢作者的精心设计,厘清思路之后,感觉真的蛮好玩的。

0x03反推和解方程

这段就简略的过了,动态调试,真正的校验过程和之前那个伪造的很像,只是需要解三个方程。写个Python脚本计算一下,输入key:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

A = 0xeaf917e2
B = 0xe8f508c8
C = 0xc0a3c68

c = (B - C)/2
b_a = A - B
b = C + c - 3*b_a
a = b - b_a

key = ""

for n in (b, a, c):
    key += chr(n & 0x000000ff)
    key += chr((n & 0x0000ff00) >> 8)
    key += chr((n & 0x00ff0000) >> 16)
    key += chr((n & 0xff000000) >> 24)

print key

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回