首页
社区
课程
招聘
看雪CTF.TSRC 2018 团队赛 第十二题 移动迷宫
发表于: 2018-12-25 04:33 6541

看雪CTF.TSRC 2018 团队赛 第十二题 移动迷宫

2018-12-25 04:33
6541

题目给了一个带一个很滑稽的图标的程序。运行一下会发现它长得像某种安装器。strings一下可以看到一些像是

testsoft Install System v2.48
XXXXsofttestS.exehead
http:/tests.sf.net/test_Error

强行打了打码。Google 搜一下 "Install System v2.48" 即可得知打码之前应该是 Nullsoft Install System v2.48。
所以这看起来外面包了一层 NSIS,考虑到运行起来之后的那个输入的界面长的也很像 NSIS 本体,估计是写了一些 NSIS 脚本或者插件来执行验证。

NSIS 安装包 7-Zip 是可以解压的,不过直接打开会报错,先使用任意你最喜欢的十六进制编辑器打开 CrackMe.exe,观察一下,发现文件尾部附带的数据头部的 NullsoftInst 被改成了 XXXXsoftInst,改回来之后用 7-Zip 就可以打开啦,可以把包含 NSIS 脚本的反汇编结果和插件dll的所有内容提取出来。记得使用一个早一点版本的 7-Zip,例如 9.38,新版本移除了反汇编 NSIS 脚本的功能。

7-Zip 并没有把所有的函数偏移识别出来,不过这不大影响我们分析。可以看到显示 Page 0 的函数是 func_747,其调用 SetTimer 跑了一个反调试的 timer,每秒扫一次进程,kill 名字长得像调试器的,对于我们静态分析选手没有影响。

验证逻辑在按钮的 OnClick 函数里,如下,稍微手动整理一下之后也很清晰,其中 Bamer::P 之类的函数是在 Bamer.dll 里实现的,可以找一个 NSIS 插件的头文件对着看。

Bamer 中除 G 以外的函数大致功能如下,分析过程略去,都是看看代码就能看出来的:

可以看到在 NSIS 脚本中和插件中均有验证逻辑,梳理一下:

NSIS 脚本中的验证逻辑有两处,分别是:

这里就是简单的稍微写的别扭了一点的和固定串的比较,可以直接还原,答案是 2018TSCRCTF(腾讯的人要哭了,TSCR 是什么?)。

另一处是:

乍一看吓人,仔细一看会发现是个线性方程组,可以解出答案是 WelcomeHave

考虑到要把输入喂回去,得能进行 Bamer::A 的反向操作(即加密),看一下其相比标准的 AES-128 改动的地方:

Key Schedule 的过程翻译成 Python 代码如下:

可以看到基本是改的面目全非,而且写在内存里的 layout 也是特意扭了一下,后面 AddRoundKey 的时候有专门处理,估计是为了防止别人直接把轮密钥 dump 出来用吧。另外这里面有个可能是 bug 的东西,Rcon and 上的值没有移到高位去,因此这里总是 0。将正常 layout 的轮密钥算出来备用,之后只要随便找一个实现改一下 ShiftRows 即可用来加密/解密:

接下来看 G 这个验证函数是在干啥,主要逻辑在 10002309 中。大致看一下会发现其先用传入的 16 字节作为 key 解密了 144 字节的数据:

然后根据解密出来的数据生成了三个 8 字节的 buffer,接下来把输入的另一个四进制数当作“指令”开始进行以下操作:

总结一下,就是棋盘上的八对字母之间要连起来,连线不能交叉,最后必须占满整个棋盘。这听上去就像是一种非常经典的逻辑谜题,几乎可以肯定不是这个 CrackMe 的作者自己想出来的,也几乎可以肯定只要我们知道它叫什么,就能搜到现成的求解器。于是我们去看看 Nikoli 出版过的著名 logic puzzle 列表,翻着翻着可以翻到一种叫 Numberlink 的东西,看起来跟上面描述的规则一模一样。搜一下 "Numberlink solver",可以找到一个 跑的比香港记者还快的求解器 和一个 能告诉我们解的个数的求解器

这里我们用前者试试:

果然跑的比香港记者还快,接下来只要简单写几行代码将这个解转换为上文提到的“指令”即可:

运行即可得到答案 1113333333333000000333330033331113033111333030000222222111220000003333333333311111333333333111113112122222222221133333333000333333331111。利用 Bamer.dll 自身将其转换为 36 进制,得到 32WSFUPIFV9TYJWWPH14NZZ85YDHXOLO37ATG4IYC4ZCDIKCA7EJ9
至此,我们已经得到构造序列号的所有要素了。

将之前得到的答案前面拼上 WelcomeHave,加密,前面再拼上 2018TSCRCTF,编码即可得到最后的序列号 MhAyOFQSR2JDUEb0OUopUQkh5Ax23nEnCLxoBT06JRd7EdLrwooWsXQG68wLcneAqDy3UU78AgdrYnabVL0M9vd852girNqF9a3F

由于 Bamer 中的 C 函数对于输入的大小写不敏感,因此翻转作为上面的小游戏答案中的字母之后前面的验证依然都能过,但问题在于魔改 Base64 编码后必须没有 +/ 以及 FNV-1a hash 验证必须过。注意到小游戏答案中有 40 个可以翻转大小写的字母,因此这部分可能的解有 2 ** 40 个,不是很多,我们可以枚举一遍。由于 FNV-1a hash 有 32 位,假设它性质比较良好的话,不考虑 +/ 的问题最后应该能得到接近 256 个答案,写个程序试一试:

开上 256 个核,跑个 20 分钟即可得到所有解,共 237 个,去掉包含 +/ 的之后还剩下 24 个,如下,其中第一行的是“预期解",但其他的输入原来的程序也会得到 You win! 提示。

 
 
 
 
 
 
 
Function OnClick
  System::Call user32::GetWindowText(p$_1_,t.s,i1024)
    ; Call Initialize_____Plugins
    ; File $PLUGINSDIR\System.dll
    ; SetDetailsPrint lastused
    ; Push user32::GetWindowText(p$_1_,t.s,i1024)
    ; CallInstDLL $PLUGINSDIR\System.dll Call
  Pop $0
  StrCpy $_3_ $0
  Bamer::P $0                  ;; Is Alphanumeric?
    ; Call Initialize_____Plugins
    ; AllowSkipFiles off
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $0
    ; CallInstDLL $PLUGINSDIR\Bamer.dll P
  Pop $R0
  StrCmp $R0 0 0 label_658
  Goto boom
label_658:
  StrLen $1 $0
  IntCmp $1 100 0 boom boom     ;; Input size should be 100
  Bamer::B $0 $1                ;; Modified Base64 decode
    ; Call Initialize_____Plugins
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $1
    ; Push $0
    ; CallInstDLL $PLUGINSDIR\Bamer.dll B
  Pop $2                        ;; $2 = decoded data
  StrCpy $3 $2 11 0             ;; $3 = first 11 char of decoded data
  Push $3
  Call CheckPart1               ;; Must return true
  Pop $R0
  StrCmp $R0 False 0 label_673
  Goto boom
label_673:
  Push $3
  Call func_125                 ;; $3 = $3 + "00010", pad 11 bytes key to 16 bytes
  Pop $3
  StrCpy $4 $2 64 11            ;; data[11:11+64]
  StrLen $R0 $4
  StrCmp $R0 64 label_680
  Goto boom
label_680:
  Bamer::A $4 64 $3             ;; ModifiedAESDecrypt(data[11:11+64], 64, $3)
    ; Call Initialize_____Plugins
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $3
    ; Push 64
    ; Push $4
    ; CallInstDLL $PLUGINSDIR\Bamer.dll A
  Pop $R0
  StrCmp $R0 0 0 label_690      ;; Must return 1
  Goto boom
label_690:
  Pop $R1                       ;; Decrypted data[11:11+64]
  StrCpy $5 $R1 11 0            ;; first 11 bytes
  StrCpy $_6_ $5
  Push $5
  Call func_133                 ;; CheckPart2First11Bytes, Answer: WelcomeHave
  Pop $R0
  StrCmp $R0 False 0 label_698
  Goto boom
label_698:
  StrCpy $6 $R1 53 11           ;; next 53 bytes
  Bamer::C 36 4 $6
    ; Call Initialize_____Plugins
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $6
    ; Push 4
    ; Push 36
    ; CallInstDLL $PLUGINSDIR\Bamer.dll C
  Pop $R2
  Push $_6_                    ;; $_6_ = "WelcomeHave"
  Call func_615
  Pop $5                       ;; $5 = "WelcomeToHaveFun" (len = 16)
  Bamer::G $5 $R2
    ; Call Initialize_____Plugins
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $R2
    ; Push $5
    ; CallInstDLL $PLUGINSDIR\Bamer.dll G
  Pop $R0
  StrCmp $R0 0 0 label_719
  Goto boom
label_719:
  Bamer::F $_3_                  ;; FinalCheck: FNVHash(entire_input) == 0x400D49F3
    ; Call Initialize_____Plugins
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $_3_
    ; CallInstDLL $PLUGINSDIR\Bamer.dll F
  Pop $R0
  StrCmp $R0 0 0 win
  Goto boom
win:
  StrLen $R1 XX+2IHcragE=          ;; You win!
  Bamer::B XX+2IHcragE= $R1
    ; Call Initialize_____Plugins
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $R1
    ; Push XX+2IHcragE=
    ; CallInstDLL $PLUGINSDIR\Bamer.dll B
  Pop $R2
  MessageBox MB_OK $R2
  Return

boom:
  StrLen $R1 U0JtakdiZX6wc1UxIR==
  Bamer::B U0JtakdiZX6wc1UxIR== $R1 ;; Wrong answer!
    ; Call Initialize_____Plugins
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $R1
    ; Push U0JtakdiZX6wc1UxIR==
    ; CallInstDLL $PLUGINSDIR\Bamer.dll B
  Pop $R2
  MessageBox MB_OK|MB_ICONINFORMATION $R2
FunctionEnd
Function CheckPart1
  Exch $R8
    ; Push $R8
    ; Exch
    ; Pop $R8
  StrLen $8 JTZmLD/8Sh6MOmd=
  Bamer::B JTZmLD/8Sh6MOmd= $8
    ; Call Initialize_____Plugins
    ; AllowSkipFiles on
    ; File $PLUGINSDIR\Bamer.dll
    ; SetDetailsPrint lastused
    ; Push $8
    ; Push JTZmLD/8Sh6MOmd=
    ; CallInstDLL $PLUGINSDIR\Bamer.dll B
  Pop $R9
  StrLen $R7 $R8
  IntCmp $R7 11 label_99 label_120 label_120 ;; input size must be 11
label_99:
  IntOp $R6 0 + 0 ;; $R6 is loop index
label_100:
  StrCpy $R1 $R8 1 $R6 ;; $R1 = input[$R6]
  Push $R1
  Call Ord
  Pop $R1
  IntOp $R2 $R1 ^ 0x17
  IntOp $R3 $R2 - $R6  ;; (input[i] ^ 0x17) - i should equal to predefined answer
  StrCpy $R4 $R9 1 $R6
  Push $R4
  Call Ord
  Pop $R4
  IntCmp $R3 $R4 0 label_120 label_120
  StrCmp $R6 10 0 label_113
  Goto label_115
label_113:
  IntOp $R6 $R6 + 1
  Goto label_100
label_115:
  StrCpy $0 True
  Exch $0
    ; Push $0
    ; Exch
    ; Pop $0
  Return

label_120:
  StrCpy $0 False
  Exch $0
    ; Push $0
    ; Exch
    ; Pop $0
FunctionEnd
Function func_133
  Exch $0
    ; Push $0
    ; Exch
    ; Pop $0
  StrLen $1 $0
  IntCmp $1 11 0 label_fail label_fail
  StrCpy $1 $0 1 0
  StrCpy $2 $0 1 1
  StrCpy $3 $0 1 2
  StrCpy $4 $0 1 3
  StrCpy $5 $0 1 4
  StrCpy $6 $0 1 5
  StrCpy $7 $0 1 6
  StrCpy $8 $0 1 7
  StrCpy $9 $0 1 8
  StrCpy $_4_ $0 1 9
  StrCpy $_5_ $0 1 10
  Push $1
  Call Ord
  Pop $1
  Push $2
  Call Ord
  Pop $2
  Push $3
  Call Ord
  Pop $3
  Push $4
  Call Ord
  Pop $4
  Push $5
  Call Ord
  Pop $5
  Push $6
  Call Ord
  Pop $6
  Push $7
  Call Ord
  Pop $7
  Push $8
  Call Ord
  Pop $8
  Push $9
  Call Ord
  Pop $9
  Push $_4_
  Call Ord
  Pop $_4_
  Push $_5_
  Call Ord
  Pop $_5_
  IntOp $R2 $1 * 18334
  IntOp $R3 $2 * 19371
  IntOp $R2 $R2 + $R3
  IntOp $R4 $3 * 15568
  IntOp $R3 $4 * 19321
  IntOp $R4 $3 * 17784
  IntOp $R2 $R2 - $R4
  IntOp $R5 $R2 * 21534
  IntOp $R5 $4 * 21534
  IntOp $R3 $4 * 18321
  IntOp $R2 $R2 + $R5
  IntOp $R4 $R4 * 11321
  IntOp $R3 $9 * 16158
  IntOp $R6 $5 * 23633
  IntOp $R5 $_5_ * 18278
  IntOp $R7 $6 * 16027
  IntOp $R8 $7 * 18430
  IntOp $R2 $R2 + $R3
  IntOp $R4 $_4_ * 15917
  IntOp $R9 $8 * 24544
  IntOp $R2 $R2 - $R6
  IntOp $R2 $R2 + $R7
  IntOp $R3 $R3 * 25621
  IntOp $R2 $R2 + $R5
  IntOp $R2 $R2 - $R8
  IntOp $R5 $R2 * 33321
  IntOp $R2 $R2 + $R4
  IntOp $R4 $R3 * 25321
  IntOp $R2 $R2 - $R9
  IntOp $R3 $R2 * 12345
  IntOp $R2 $1 * 19292
  IntOp $R4 $3 * 17677
  IntOp $R5 $4 * 18327
  IntOp $R9 $8 * 20472
  IntOp $R6 $5 * 19344
  IntOp $R3 $2 * 21770
  IntOp $R7 $6 * 16593
  IntOp $R8 $7 * 20094
  IntOp $R2 $R2 - $R8
  IntOp $R2 $R2 + $R9
  IntOp $R2 $R2 + $R3
  IntOp $R2 $R2 + $R4
  IntOp $R2 $R2 - $R5
  IntOp $R2 $R2 + $R6
  IntOp $R2 $R2 + $R7
  IntOp $R3 $9 * 19029
  IntOp $R4 $_4_ * 16001
  IntOp $R5 $_5_ * 20980
  IntOp $R2 $R2 - $R3
  IntOp $R2 $R2 + $R4
  IntOp $R2 $R2 - $R5
  IntCmp $R2 5295553 0 label_fail label_fail
  ... 略 ...

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 5
支持
分享
最新回复 (3)
雪    币: 3003
活跃值: (479)
能力值: ( LV15,RANK:1395 )
在线值:
发帖
回帖
粉丝
2
牛逼了!!!!
1.2018TSCRCTF  是因为换成 TSRC异或之后不是可见字符。
2.中间的进制转换并不是必须的,加这个东西只是为了压缩长度,没有想到造成了多解。
3.改版的base64和AES其实还是出自腾讯之手,如果你能猜到这个,估计用时更短。
4.最后的小游戏是微信小程序里边的连线世界。
5.大佬懂的真多。。。 。。。
2018-12-25 14:26
0
雪    币: 6435
活跃值: (441)
能力值: ( LV12,RANK:831 )
在线值:
发帖
回帖
粉丝
3
_
最后于 2020-4-17 02:32 被Riatre编辑 ,原因:
2018-12-25 23:36
0
雪    币: 10845
活跃值: (1054)
能力值: (RANK:190 )
在线值:
发帖
回帖
粉丝
4
在题目里设计个游戏 确实很好玩
学了 下次我也设计一个 一个没见过的
2018-12-27 22:27
0
游客
登录 | 注册 方可回帖
返回
//