# Super Secret Login - zer0pts CTF 2021
###### tags: `zer0pts CTF 2021` `reversing`
## 調査
stringsで見るとこんな文字列が出てくる。
```
This is a third-party compiled AutoIt script.
```
AutoIT製と思われるので例のdecompilerに投げる。
ここがメイン処理:
```
$hgui = GUICreate("Super Secret Login", 0x160, 0x6d, + -1, + -1)
GUISetFont(0x9, 0x190, 0x0, "Courier New")
$hlabel = GUICtrlCreateLabel("Enter Password:", 0x19, 0x19, 0x6d, 0x12)
$hinput = GUICtrlCreateInput('', 0x8c, 0x17, 0xbc, 0x14)
GUICtrlSetFont(+ -1, 0x9, 0x190, 0x0, "Courier New", 0x5)
$hbutton = GUICtrlCreateButton("Login!", 0x7a, 0x47, 0x6d, 0x19)
GUICtrlSetStyle(+ -1, $BS_FLAT)
$hcaret = _WinAPI_CreateCaret($hgui, 0xa, 0x2)
$mserverkey = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
_WinAPI_ShowCaret($hgui)
GUISetState()
While 0x1
$hmsg = GUIGetMsg()
Switch $hmsg
Case $GUI_EVENT_CLOSE
Exit
Case $hbutton
onclick()
EndSwitch
WEnd
Func onclick()
Local $text = GUICtrlRead($hinput)
Local $fuckpython = StringLen($text)
If $fuckpython == 0x0 Then Return
Local $oldfuck = $fuckpython
GUICtrlSetState($hbutton, $GUI_DISABLE)
Local $fuck = fuckit($text, $mserverkey)
GUICtrlSetState($hbutton, $GUI_ENABLE)
$fuckpython = "7188bb1563e5702342e22a856ad3df1cfa9729b4115d8cfb1f07a0c6fc916477f02f77d656834379b32e"
If String(BinaryToString($fuck)) == ('' & $fuckpython) Then
MsgBox(0x40, "Good Job!", "Read the instructions and see if you can submit It!", 0x0, $hgui)
Else
MsgBox(0x40, ":(", "You have a long way to go buddy!" & @CRLF & ":(", 0x0, $hgui)
EndIf
EndFunc ; -> onclick
Func fuckit($data, $key)
Local $opcode = "0x5589e56031c081ec00010000880404403d0001000075f531f631db0fb6043401c389f0b92b00000031d2f7f18b45140fbe0c1001cb0fb6db8a0c348a041c880434880c1c83c60181fe0001000075cc" & _
"31f631d231c942460fb6d28a041401c10fb6c98a1c0c88040c881c1400d80fb6c08b7d0c8a5c37ff8a040431c38b7d08e8100000003b751075cc81c40001000061c9c210005053eb1858c1eb048a1c18" & _
"885c77fe5b83e30f8a1c18885c77ff58c3e8e3ffffff30313233343536373839616263646566"
Local $codebuffer = DllStructCreate("byte[" & BinaryLen($opcode) & "]")
DllStructSetData($codebuffer, 0x1, Binary($opcode))
Local $buffer = DllStructCreate("byte[" & StringLen($data) & "]")
DllStructSetData($buffer, 0x1, $data)
Local $destbuffer = DllStructCreate("char[" & (StringLen($data) * 0x2 + 0x1) & "]")
DllCall("user32.dll", "none", "CallWindowProc", "ptr", DllStructGetPtr($codebuffer), "ptr", DllStructGetPtr($destbuffer), "ptr", DllStructGetPtr($buffer), "int", StringLen($data), "str", $key)
Local $ret = DllStructGetData($destbuffer, 0x1)
$buffer = 0x0
$codebuffer = 0x0
Return $ret
EndFunc ; -> fuckit
```
onclikの処理を読むと、`fuckit($text, $mserverkey)`で暗号化か何かをして、その結果と`7188bb1563e5702342e22a856ad3df1cfa9729b4115d8cfb1f07a0c6fc916477f02f77d656834379b32e`が等しいかを確認している。
## fuckitの解析
```
Func fuckit($data, $key)
Local $opcode = "0x5589e56031c081ec00010000880404403d0001000075f531f631db0fb6043401c389f0b92b00000031d2f7f18b45140fbe0c1001cb0fb6db8a0c348a041c880434880c1c83c60181fe0001000075cc" & _
"31f631d231c942460fb6d28a041401c10fb6c98a1c0c88040c881c1400d80fb6c08b7d0c8a5c37ff8a040431c38b7d08e8100000003b751075cc81c40001000061c9c210005053eb1858c1eb048a1c18" & _
"885c77fe5b83e30f8a1c18885c77ff58c3e8e3ffffff30313233343536373839616263646566"
Local $codebuffer = DllStructCreate("byte[" & BinaryLen($opcode) & "]")
DllStructSetData($codebuffer, 0x1, Binary($opcode))
Local $buffer = DllStructCreate("byte[" & StringLen($data) & "]")
DllStructSetData($buffer, 0x1, $data)
Local $destbuffer = DllStructCreate("char[" & (StringLen($data) * 0x2 + 0x1) & "]")
DllCall("user32.dll", "none", "CallWindowProc", "ptr", DllStructGetPtr($codebuffer), "ptr", DllStructGetPtr($destbuffer), "ptr", DllStructGetPtr($buffer), "int", StringLen($data), "str", $key)
Local $ret = DllStructGetData($destbuffer, 0x1)
$buffer = 0x0
$codebuffer = 0x0
Return $ret
EndFunc ; -> fuckit
```
なるほど機械語を実行してるっぽい。
呼び出しは
```
f(res, text, textlen, key)
```
みたいな感じ。
## 機械語の解析
objdumpで読む。
```
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 60 pusha
4: 31 c0 xor eax,eax
6: 81 ec 00 01 00 00 sub esp,0x100
c: 88 04 04 mov BYTE PTR [esp+eax*1],al
f: 40 inc eax
10: 3d 00 01 00 00 cmp eax,0x100
15: 75 f5 jne 0xc
17: 31 f6 xor esi,esi
19: 31 db xor ebx,ebx
1b: 0f b6 04 34 movzx eax,BYTE PTR [esp+esi*1]
1f: 01 c3 add ebx,eax
21: 89 f0 mov eax,esi
23: b9 2b 00 00 00 mov ecx,0x2b
28: 31 d2 xor edx,edx
2a: f7 f1 div ecx
2c: 8b 45 14 mov eax,DWORD PTR [ebp+0x14]
2f: 0f be 0c 10 movsx ecx,BYTE PTR [eax+edx*1]
33: 01 cb add ebx,ecx
35: 0f b6 db movzx ebx,bl
38: 8a 0c 34 mov cl,BYTE PTR [esp+esi*1]
3b: 8a 04 1c mov al,BYTE PTR [esp+ebx*1]
3e: 88 04 34 mov BYTE PTR [esp+esi*1],al
41: 88 0c 1c mov BYTE PTR [esp+ebx*1],cl
44: 83 c6 01 add esi,0x1
47: 81 fe 00 01 00 00 cmp esi,0x100
4d: 75 cc jne 0x1b
4f: 31 f6 xor esi,esi
51: 31 d2 xor edx,edx
53: 31 c9 xor ecx,ecx
55: 42 inc edx
56: 46 inc esi
57: 0f b6 d2 movzx edx,dl
5a: 8a 04 14 mov al,BYTE PTR [esp+edx*1]
5d: 01 c1 add ecx,eax
5f: 0f b6 c9 movzx ecx,cl
62: 8a 1c 0c mov bl,BYTE PTR [esp+ecx*1]
65: 88 04 0c mov BYTE PTR [esp+ecx*1],al
68: 88 1c 14 mov BYTE PTR [esp+edx*1],bl
6b: 00 d8 add al,bl
6d: 0f b6 c0 movzx eax,al
70: 8b 7d 0c mov edi,DWORD PTR [ebp+0xc]
73: 8a 5c 37 ff mov bl,BYTE PTR [edi+esi*1-0x1]
77: 8a 04 04 mov al,BYTE PTR [esp+eax*1]
7a: 31 c3 xor ebx,eax
7c: 8b 7d 08 mov edi,DWORD PTR [ebp+0x8]
7f: e8 10 00 00 00 call 0x94
84: 3b 75 10 cmp esi,DWORD PTR [ebp+0x10]
87: 75 cc jne 0x55
89: 81 c4 00 01 00 00 add esp,0x100
8f: 61 popa
90: c9 leave
91: c2 10 00 ret 0x10
94: 50 push eax
95: 53 push ebx
96: eb 18 jmp 0xb0
98: 58 pop eax
99: c1 eb 04 shr ebx,0x4
9c: 8a 1c 18 mov bl,BYTE PTR [eax+ebx*1]
9f: 88 5c 77 fe mov BYTE PTR [edi+esi*2-0x2],bl
a3: 5b pop ebx
a4: 83 e3 0f and ebx,0xf
a7: 8a 1c 18 mov bl,BYTE PTR [eax+ebx*1]
aa: 88 5c 77 ff mov BYTE PTR [edi+esi*2-0x1],bl
ae: 58 pop eax
af: c3 ret
b0: e8 e3 ff ff ff call 0x98
```
ここまで解析すると、初期化部分だけでRC4の臭いが強い。
```c
// ebp+0x14: key
// ebp+0x10: len
// ebp+0x0c: data
// ebp+0x08: dst
int f(char *dst, char *data, int len, char *key)
{
int i;
char buf[0x100];
for (i = 0; i < 0x100; i++)
buf[i] = i;
unsigned int j = 0;
for (i = 0; i < 0x100; i++) {
j += buf[i];
j += key[i % 0x2b];
unsigned char tmp = buf[i];
buf[i] = buf[j % 0x100];
buf[j % 0x100] = tmp;
}
// not analyzed yet
}
```
ということでRC4にかける。
```python=
def KSA(key):
keylength = len(key)
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % keylength]) % 256
S[i], S[j] = S[j], S[i] # swap
return S
def PRGA(S):
i = 0
j = 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i] # swap
K = S[(S[i] + S[j]) % 256]
yield K
def RC4(key):
S = KSA(key)
return PRGA(S)
if __name__ == '__main__':
key = b'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
plaintext = bytes.fromhex('7188bb1563e5702342e22a856ad3df1cfa9729b4115d8cfb1f07a0c6fc916477f02f77d656834379b32e')
def convert_key(s):
return [c for c in s]
key = convert_key(key)
keystream = RC4(key)
import sys
for c in plaintext:
sys.stdout.write(chr(c ^ next(keystream)))
print("")
```
終わり。
```
$ python solve.py
zeropts{aut0it!_n0_!l0l!_y0u_g0t_tr0ll3d!}
```
と思ってtask.yml確認したらフラグが違うっぽい。
まぁ確かによく見ると`zer0pts`じゃなくて`zeropts`になってる。
## 動的解析
コード読んでも分からんので動的解析する。
`DllCall`で止めるためにsupersecretlogin.exeモジュール中の`LoadLibrary`および`GetProcAddress`の参照箇所すべてにブレークポイントを付けて、適当な文字列でLoginする。
すると、まずuser32.dllがLoadLibraryに渡され、さらにGetProcAddressが呼ばれる様子が分かる。
GetProcAddressの戻り値でCallWindowProcのアドレスが貰えるので、そこにブレークポイントを付ける。
この時のスタックはこのような状態で、確かにkey, len, data, dst, codeが渡されている。
![](https://i.imgur.com/7RDaEC4.png)
で、機械語の結果を見てもやはり
```
7188bb1563e5702342e22a856ad3df1cfa9729b4115d8cfb1f07a0c6fc916477f02f77d656834379b32e
```
にはなっている :thinking_face:
## リソース
リソースをダンプするとAutoItのスクリプト(の中間言語)があるが、そのEOSマーカーが2つある。1つ目のマーカーの後ろがx86の関数の出だしっぽい見た目をしているので機械語としてobjdumpする。
```
00000000 <.data>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 60 pusha
4: 64 a1 30 00 00 00 mov eax,fs:0x30
a: 8b 58 08 mov ebx,DWORD PTR [eax+0x8]
d: 68 6a ae 9c db push 0xdb9cae6a
12: 68 08 b7 01 00 push 0x1b708
17: e8 9a 00 00 00 call 0xb6
1c: 8d bb 74 7f 0c 00 lea edi,[ebx+0xc7f74]
...
```
ここらへんにbreakpointを付けてみる。
0xb6はたぶんdll-lessでwin32api取るやつ。動的に調べると最初のはNtQueryInformationprocessであると分かる。これはたぶんanti-debugなのでスキップする。
次の
```
33: 68 8e ac 34 00 push 0x34ac8e
38: 68 88 8f 03 00 push 0x38f88
3d: e8 74 00 00 00 call 0xb6
```
はGetWindowTextWと分かる。
あとはなんかxorっぽいのしてるので、対象の箇所のデータをダンプする。
![](https://i.imgur.com/VI0DwJk.png)
この辺。
```
from ptrlib import xor
fake = b'zeropts{aut0it!_n0_!l0l!_y0u_g0t_tr0ll3d!}'
with open("hoge", "rb") as f:
f.seek(0x5a)
key = f.read()
print(xor(fake, key))
```
できた。
```
zer0pts{1n_th3_w1ld_m4lw4r3s_us3_4ut0-i7!}
```