Tonight, Paul (rootbsd) Rascagneres gave a talk at the Hackgyver hackspace in Belfort, and proposed afterwards two littles reversing challenges (one on Linux, and one on Windows) with malware.lu (which is a nice resources website by the way) T-shirts as prizes to finish the evening: I won both \o/
Nothing complicated, since they had to be solvable in less than 2 hours, but still fun to reverse under pressure ("I must get a free T-shirt !").
Linux
You can get the bin here
/ function: sym.main (242)
| 0x080489a5 55 push ebp
| 0x080489a6 89e5 mov ebp, esp
| 0x080489a8 57 push edi
| 0x080489a9 83e4f0 and esp, 0xfffffff0
| 0x080489ac 83ec70 sub esp, 0x70
| 0x080489af 8b450c mov eax, [ebp+0xc]
| 0x080489b2 8944241c mov [esp+0x1c], eax
| 0x080489b6 65a114000000 mov eax, [gs:0x14]
| 0x080489bc 8944246c mov [esp+0x6c], eax
| 0x080489c0 31c0 xor eax, eax
| 0x080489c2 837d0802 cmp dword [ebp+0x8], 0x2
| ,=< 0x080489c6 740c jz loc.080489d4 ; first test on arguments
| | 0x080489c8 c7042401000000 mov dword [esp], 0x1
| | 0x080489cf e8fcfcffff call dword imp.exit
| `-> 0x080489d4 8b44241c mov eax, [esp+0x1c]
| 0x080489d8 83c004 add eax, 0x4
| 0x080489db 8b00 mov eax, [eax]
| 0x080489dd c7442418ffffffff mov dword [esp+0x18], 0xffffffff
| 0x080489e5 89c2 mov edx, eax
| 0x080489e7 b800000000 mov eax, 0x0
| 0x080489ec 8b4c2418 mov ecx, [esp+0x18]
| 0x080489f0 89d7 mov edi, edx
| 0x080489f2 f2ae repne scasb
| 0x080489f4 89c8 mov eax, ecx
| 0x080489f6 f7d0 not eax
| 0x080489f8 83e801 sub eax, 0x1
| 0x080489fb 83f808 cmp eax, 0x8
| ,==< 0x080489fe 770c ja loc.08048a0c ;second test on arguments
| | 0x08048a00 c7042401000000 mov dword [esp], 0x1
| | 0x08048a07 e8c4fcffff call dword imp.exit
| `--> 0x08048a0c 8b44241c mov eax, [esp+0x1c]
| 0x08048a10 83c004 add eax, 0x4
| 0x08048a13 8b00 mov eax, [eax]
| 0x08048a15 8d542421 lea edx, [esp+0x21]
| 0x08048a19 89542404 mov [esp+0x4], edx
| 0x08048a1d 890424 mov [esp], eax
| 0x08048a20 e88ffeffff call dword sym.md6
| 0x08048a25 8b44241c mov eax, [esp+0x1c]
| 0x08048a29 83c004 add eax, 0x4
| 0x08048a2c 8b00 mov eax, [eax]
| 0x08048a2e c744240409000000 mov dword [esp+0x4], 0x9
| 0x08048a36 890424 mov [esp], eax
| 0x08048a39 e856fdffff call dword sym.RC4_encode
| 0x08048a3e 8d542442 lea edx, [esp+0x42]
| 0x08048a42 89542404 mov [esp+0x4], edx
| 0x08048a46 890424 mov [esp], eax
| 0x08048a49 e866feffff call dword sym.md6
| 0x08048a4e 8d442442 lea eax, [esp+0x42]
| 0x08048a52 89442404 mov [esp+0x4], eax
| 0x08048a56 8d442421 lea eax, [esp+0x21]
| 0x08048a5a 890424 mov [esp], eax
| 0x08048a5d e85efcffff call dword imp.strcmp
| 0x08048a62 85c0 test eax, eax
| ,===< 0x08048a64 750e jnz loc.08048a74 ;jumps tbadboy
| | 0x08048a66 c70424788b0408 mov dword [esp], str.Well,nowcreateyourownkeygen
| | 0x08048a6d e82efcffff call dword imp.puts
| ,====< 0x08048a72 eb0c jmp loc.08048a80
| |`---> 0x08048a74 c704249c8b0408 mov dword [esp], str.Badkey
| | 0x08048a7b e820fcffff call dword imp.puts
| `----> 0x08048a80 8b54246c mov edx, [esp+0x6c]
| 0x08048a84 65331514000000 xor edx, [gs:0x14]
|,=====< 0x08048a8b 7405 jz loc.08048a92
|| 0x08048a8d e8fefbffff call dword imp.__stack_chk_fail
|`-----> 0x08048a92 8b7dfc mov edi, [ebp-0x4]
| 0x08048a95 c9 leave
\ 0x08048a96 c3 ret
Looks pretty straightforward : md6(string) == md6(string[0-9] + ARC4(string, 9), but the tricks is that sys.md6 is in fact MD5,
and sym.RC4_encode is base64.
At first, I was candid enough to not check what they were really doing, and therefore lost some time.
import sys, base64
print(sys.argv[1][:9] + base64.b64encode(sys.argv[1][:9]))
Funny note, r00tBSD didn't known that MD6 is a real thing!
Windows
You can get the bin here
Since I'm not as familiar with Windows as I am in Linux, I searched for string "Good PIN code" in the binary, because as the challenge was supposed to be easy enough to be solved in a short amount of time,
I though that I was not supposed to dig all around the bloated visual-studio-produced code to find the verification routine. But Windows use Unicode, so I didn't find the string at first check. R00tBSD was kind enough to remind me this fact, so I ran strings with the appropriate arguments, and voilà:
$ strings -el -t x hackgyver.exe
[...]
c918 CONOUT$
c928 HACK
c934 hackgyver.png
c950 Error
c960 Unable to create a file in the current directory
c9c4 Hackgyver
c9d8 Hackgyver: Windows Reverse
ca10 RegisterClassEx Failed!
ca40 CreateWindow Failed!
ca6c validate
ca80 Button
ca94 Edit
caa0 Enter your PIN code
cac8 Static
cad8 Good PIN code
cb0c Bad PIN code
e6a2 HACK
With those informations, I supposed that that crackme will show an (embedded) PNG if I provide him the right PIN.
.text:00401425 loc_401425: ; CODE XREF: sub_4013A0+8Ej
.text:00401425 mov ax, [ecx]
.text:00401428 add ecx, 2
.text:0040142B test ax, ax
.text:0040142E jnz short loc_401425
.text:00401430 push 0 ; lpUsedDefaultChar
.text:00401432 push 0 ; lpDefaultChar
.text:00401434 push 0Ch ; cbMultiByte
.text:00401436 sub ecx, edx
.text:00401438 sar ecx, 1
.text:0040143A lea eax, [ebp+MultiByteStr]
.text:0040143D push eax ; lpMultiByteStr
.text:0040143E lea eax, [ecx+1]
.text:00401441 push eax ; cchWideChar
.text:00401442 push offset String ; lpWideCharStr
.text:00401447 push 0 ; dwFlags
.text:00401449 push 0 ; CodePage
.text:0040144B call ds:WideCharToMultiByte
.text:00401451 lea ecx, [ebp+MultiByteStr]
.text:00401454 call sub_401000
.text:00401459 push 0 ; lpParam
.text:0040145B push 0 ; hInstance
.text:0040145D push 0 ; hMenu
.text:0040145F push edi ; hWndParent
.text:00401460 push 19h ; nHeight
.text:00401462 push 8Ch ; nWidth
.text:00401467 push 32h ; Y
.text:00401469 push 32h ; X
.text:0040146B push 50000000h ; dwStyle
.text:00401470 cmp al, 1
.text:00401472 jnz short loc_4014A6 ; BADBOY
.text:00401474 push offset aGoodPinCode ; "Good PIN code"
.text:00401479 push offset aStatic ; "Static"
.text:0040147E push 0 ; dwExStyle
.text:00401480 call ds:CreateWindowExW
.text:00401486 push offset aExplorerHackgy ; "explorer hackgyver.png"
.text:0040148B call sub_401572
.text:00401490 add esp, 4
.text:00401493 xor eax, eax
.text:00401495 pop edi
.text:00401496 mov ecx, [ebp+var_4]
.text:00401499 xor ecx, ebp
.text:0040149B call @__security_check_cookie@4 ; __security_check_cookie(x)
.text:004014A0 mov esp, ebp
.text:004014A2 pop ebp
.text:004014A3 retn 10h
.text:004014A6 ; ---------------------------------------------------------------------------
.text:004014A6
.text:004014A6 loc_4014A6: ; CODE XREF: sub_4013A0+D2j
.text:004014A6 push offset aBadPinCode ; "Bad PIN code"
.text:004014AB push offset aStatic ; "Static"
.text:004014B0 push 0 ; dwExStyle
.text:004014B2 call ds:CreateWindowExW
.text:004014B8 xor eax, eax
.text:004014BA pop edi
.text:004014BB mov ecx, [ebp+var_4]
.text:004014BE xor ecx, ebp
.text:004014C0 call @__security_check_cookie@4 ; __security_check_cookie(x)
.text:004014C5 mov esp, ebp
.text:004014C7 pop ebp
.text:004014C8 retn 10h
Everything seems to happen in sub_401000.
;Prolog, and strlen
[...]
.text:00401029 cmp ecx, 5 ; Our PIN must be 5 digits long
.text:0040102C jnz BADBOY
.text:00401032 push ebx
.text:00401033 push esi
.text:00401034 push offset Type ; "HACK"
.text:00401039 push 67h ; lpName
.text:0040103B push 0 ; hModule
.text:0040103D call ds:FindResourceW
.text:00401043 mov esi, eax
.text:00401045 push esi ; hResInfo
.text:00401046 push 0 ; hModule
.text:00401048 call ds:SizeofResource
.text:0040104E push esi ; hResInfo
.text:0040104F push 0 ; hModule
.text:00401051 mov ebx, eax
.text:00401053 call ds:LoadResource
.text:00401059 push ecx
.text:0040105A mov edx, edi
.text:0040105C lea ecx, [ebp+var_908]
.text:00401062 mov esi, eax
.text:00401064 call sub_401150 ; First key computation routine
.text:00401069 lea eax, [ebp+var_804]
.text:0040106F push ebx
.text:00401070 push eax
.text:00401071 mov edx, esi
.text:00401073 lea ecx, [ebp+var_908]
.text:00401079 call sub_4011C0 ; Second key computation routine
.text:0040107E add esp, 0Ch
.text:00401081 push 0 ; hTemplateFile
.text:00401083 push 80h ; dwFlagsAndAttributes
.text:00401088 push 2 ; dwCreationDisposition
.text:0040108A push 0 ; lpSecurityAttributes
.text:0040108C push 0 ; dwShareMode
.text:0040108E push 40000000h ; dwDesiredAccess
.text:00401093 push offset FileName ; "hackgyver.png"
.text:00401098 call ds:CreateFileW
.text:0040109E mov esi, eax
.text:004010A0 cmp esi, 0FFFFFFFFh
.text:004010A3 jnz short loc_4010CC
;Unable to create the file, oh noes.
[...]
.text:004010CC ; ---------------------------------------------------------------------------
.text:004010CC
.text:004010CC loc_4010CC: ; Looks like to be the check of the key
.text:004010CC cmp [ebp+var_804], 0AAh
.text:004010D3 jnz short BADBOY2
.text:004010D5 cmp [ebp+var_803], 0BBh
.text:004010DC jnz short BADBOY2
.text:004010DE cmp [ebp+var_802], 0CCh
.text:004010E5 jnz short BADBOY2
.text:004010E7 cmp [ebp+var_801], 0DDh
.text:004010EE jnz short BADBOY2
.text:004010F0 push 0 ; lpOverlapped ;GOODBOY ;)
.text:004010F2 lea eax, [ebp+NumberOfBytesWritten]
.text:004010F8 push eax ; lpNumberOfBytesWritten
.text:004010F9 push ebx ; nNumberOfBytesToWrite
.text:004010FA lea eax, [ebp+Buffer]
.text:00401100 push eax ; lpBuffer
.text:00401101 push esi ; hFile
.text:00401102 call ds:WriteFile
.text:00401108 push esi ; hObject
.text:00401109 call ds:CloseHandle
[...]
.text:00401121 retn
Once again, r00tBSD gave a hint by saying that the key computation was using a well-known encryption scheme, and since it was split in two functions with characteristic loops, it can/should be ARC4, that "We shall all had recognized". Unfortunately, I'm not able (yet) to identify encryption schemes simply by taking a quick look at disassembly.
To sum up, the ARC4-deciphered chunk passed as second argument to the second
"key computation routine " (\xF3\xDB\x99\x90\x95\x4E\xC2\x1E\x32...) with our
PIN as the key must be equals to \xAA\xBB\xCC\xDD. Time to write a
bruteforcer:
import Crypto.Cipher.ARC4 as arc4
b = '\xAA\xBB\xCC\xDD'
string = '\xF3\xDB\x99\x90\x95\x4E\xC2\x1E\x32'
for i in range(10000, 99999):
if arc4.new(str(i)).decrypt(string).startswith(b):
print i
break
Conclusion
A nice talk + two uncomplicated-by-still-entertaining crackme + one T-shirt = a good evening ;)