GreHack-2012/100-Check That Sum Baby
You are here | 100-Check That Sum Baby
|
Description
Running the program
GreHack CTF 2012 reverse engineering challenge (100 points).
This crackme is a GUI based program prompting for a login and a password:
Unpacking
PEiD tells us that the program may be packed with UPX:
UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo
Let's unpack the program:
C:\CTF>upx -d Rev100_Check_That_Sum_Baby.exe -o unpacked.exe Ultimate Packer for eXecutables Copyright (C) 1996 - 2013 UPX 3.91w Markus Oberhumer, Laszlo Molnar & John Reiser Sep 30th 2013 File size Ratio Format Name -------------------- ------ ----------- ----------- 567808 <- 311808 54.91% win32/pe unpacked.exe Unpacked 1 file.
Analysis
Decompilation
PEiD informs us that the resulting executable is a Delphi program:
Borland Delphi 6.0
Let's use IDR to decompile it.
Button click
It leads to the below code where we see an interesting call to sub_459C28 at offset 0x459D66:
Unit1::TForm2.Button1Click
00459D18 push ebp
00459D19 mov ebp,esp
00459D1B push 0
00459D1D push 0
00459D1F push ebx
00459D20 mov ebx,eax
00459D22 xor eax,eax
00459D24 push ebp
00459D25 push 459D86
00459D2A push dword ptr fs:[eax]
00459D2D mov dword ptr fs:[eax],esp
00459D30 lea edx,[ebp-4]
00459D33 mov eax,dword ptr [ebx+334]; TForm2.Edit1:TEdit
00459D39 call TLabel.GetCaption
00459D3E mov eax,dword ptr [ebp-4]
00459D41 call @LStrLen
00459D46 test eax,eax
>00459D48 je 00459D6B
00459D4A lea edx,[ebp-8]
00459D4D mov eax,dword ptr [ebx+338]; TForm2.Edit2:TEdit
00459D53 call TLabel.GetCaption
00459D58 mov eax,dword ptr [ebp-8]
00459D5B call @LStrLen
00459D60 test eax,eax
>00459D62 je 00459D6B
00459D64 mov eax,ebx
00459D66 call 00459C28
00459D6B xor eax,eax
00459D6D pop edx
00459D6E pop ecx
00459D6F pop ecx
00459D70 mov dword ptr fs:[eax],edx
00459D73 push 459D8D
00459D78 lea eax,[ebp-8]
00459D7B mov edx,2
00459D80 call @LStrArrayClr
00459D85 ret
<00459D86 jmp @HandleFinally
<00459D8B jmp 00459D78
00459D8D pop ebx
00459D8E pop ecx
00459D8F pop ecx
00459D90 pop ebp
00459D91 ret
Verification function
Below is the code at 0x459C28:
CODE:00459C28 sub_459C28 proc near
CODE:00459C28
CODE:00459C28 my_password_ = dword ptr -14h
CODE:00459C28 my_password = dword ptr -10h
CODE:00459C28 my_username_ = dword ptr -0Ch
CODE:00459C28 my_username = dword ptr -8
CODE:00459C28 flag = dword ptr -4
CODE:00459C28
CODE:00459C28 push ebp
CODE:00459C29 mov ebp, esp
CODE:00459C2B xor ecx, ecx
CODE:00459C2D push ecx
CODE:00459C2E push ecx
CODE:00459C2F push ecx
CODE:00459C30 push ecx
CODE:00459C31 push ecx
CODE:00459C32 push ebx
CODE:00459C33 push esi
CODE:00459C34 push edi
CODE:00459C35 mov edi, eax
CODE:00459C37 xor eax, eax
CODE:00459C39 push ebp
CODE:00459C3A push offset loc_459CFA
CODE:00459C3F push dword ptr fs:[eax]
CODE:00459C42 mov fs:[eax], esp
CODE:00459C45 xor eax, eax ; \
CODE:00459C47 mov [ebp+flag], eax ; / flag = 0
CODE:00459C4A lea edx, [ebp+my_username]
CODE:00459C4D mov eax, [edi+334h] ; this
CODE:00459C53 call @Controls@TControl@GetText$qqrv ; Controls::TControl::GetText(void)
CODE:00459C58 mov eax, [ebp+my_username]
CODE:00459C5B call unknown_libname_71 ; strlen
CODE:00459C60 mov esi, eax ; ESI = len(my_username)
CODE:00459C62 test esi, esi
CODE:00459C64 jl short loc_459C88
CODE:00459C66 inc esi ; ESI += 1
CODE:00459C67 xor ebx, ebx ; EBX = 0
CODE:00459C69
CODE:00459C69 loc_459C69:
CODE:00459C69 lea edx, [ebp+my_username_]
CODE:00459C6C mov eax, [edi+334h] ; this
CODE:00459C72 call @Controls@TControl@GetText$qqrv ; Controls::TControl::GetText(void)
CODE:00459C77 mov eax, [ebp+my_username_]
CODE:00459C7A movzx eax, byte ptr [eax+ebx-1] ; EAX = my_username[i], i starting from 0
CODE:00459C7F imul ebx ; EAX *= EBX
CODE:00459C81 add [ebp+flag], eax ; flag += my_username[i] * i
CODE:00459C84 inc ebx ; i+=1
CODE:00459C85 dec esi ; \ loop until all letters of username
CODE:00459C86 jnz short loc_459C69 ; / have been processed
CODE:00459C88
CODE:00459C88 loc_459C88:
CODE:00459C88 lea edx, [ebp+my_password]
CODE:00459C8B mov eax, [edi+338h] ; this
CODE:00459C91 call @Controls@TControl@GetText$qqrv ; Controls::TControl::GetText(void)
CODE:00459C96 mov eax, [ebp+my_password]
CODE:00459C99 call unknown_libname_71 ; strlen
CODE:00459C9E mov esi, eax ; ESI = len(my_password)
CODE:00459CA0 test esi, esi
CODE:00459CA2 jl short loc_459CC6
CODE:00459CA4 inc esi
CODE:00459CA5 xor ebx, ebx
CODE:00459CA7
CODE:00459CA7 loc_459CA7:
CODE:00459CA7 lea edx, [ebp+my_password_]
CODE:00459CAA mov eax, [edi+338h] ; this
CODE:00459CB0 call @Controls@TControl@GetText$qqrv ; Controls::TControl::GetText(void)
CODE:00459CB5 mov eax, [ebp+my_password_]
CODE:00459CB8 movzx eax, byte ptr [eax+ebx-1]
CODE:00459CBD imul ebx
CODE:00459CBF sub [ebp+flag], eax ; flag -= my_password[i] * i
CODE:00459CC2 inc ebx
CODE:00459CC3 dec esi
CODE:00459CC4 jnz short loc_459CA7
CODE:00459CC6
CODE:00459CC6 loc_459CC6:
CODE:00459CC6 mov eax, [ebp+flag]
CODE:00459CC9 cdq
CODE:00459CCA xor eax, edx
CODE:00459CCC sub eax, edx
CODE:00459CCE cmp eax, 539h ; \ if EAX == 0x539
CODE:00459CD3 jnz short loc_459CDF ; / goto VALID!
CODE:00459CD5 mov eax, offset _str_Valid__.Text
CODE:00459CDA call @Dialogs@ShowMessage$qqrx17System@AnsiString ; Dialogs::ShowMessage(System::AnsiString)
CODE:00459CDF
CODE:00459CDF loc_459CDF:
CODE:00459CDF xor eax, eax
CODE:00459CE1 pop edx
CODE:00459CE2 pop ecx
CODE:00459CE3 pop ecx
CODE:00459CE4 mov fs:[eax], edx
CODE:00459CE7 push offset loc_459D01
CODE:00459CEC
CODE:00459CEC loc_459CEC:
CODE:00459CEC lea eax, [ebp+my_password_]
CODE:00459CEF mov edx, 4
CODE:00459CF4 call @System@@LStrArrayClr$qqrpvi ; System::__linkproc__ LStrArrayClr(void *,int)
CODE:00459CF9 retn
Starting at offset 0x459C69, there is a first loop that is computing a number by multiplying each letter of the username with the index. Then, starting from offset 0x459CA7, a second loop is decrementing the counter with the multiplication of each letter of the password with the index. The resulting number is compared with 0x539 (1337 in decimal) at offset 0x459CCE. If it is equal, the code jumps to the good boy.
Proof of concept
Below is a proof of concept with a username and a password:
USERNAME | PASSWORD | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
#!/usr/bin/env python
my_username = "iaaaaoien"
my_password = "zzzzzzm"
flag = 0
for (c, i) in enumerate(my_username):
flag += ord(i)*(c+1)
for (c, i) in enumerate(my_password):
flag -= ord(i)*(c+1)
print "Gap with objective: %s" % (0x539 - flag)
Trying this combination in the program confirms that this is a valid one:
Comments
Keywords: grehack-2012 assembly x86 reverse-engineering crackme