GreHack-2012/100-Check That Sum Baby

From aldeid
Jump to navigation Jump to search
You are here
100-Check That Sum Baby


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:


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.



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:

 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 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                 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 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 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 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 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 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 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:

c ord(c) index ord(c)*index cumul
i 105 1 105 105
a 97 2 194 299
a 97 3 291 590
a 97 4 388 978
a 97 5 485 1463
o 111 6 666 2129
i 105 7 735 2864
e 101 8 808 3672
n 110 9 990 4662
c ord(c) index ord(c)*index cumul remainder
z 122 1 122 122 4662 - 122 = 4540
z 122 2 244 366 4296
z 122 3 366 732 3930
z 122 4 488 1220 3442
z 122 5 610 1830 2832
z 122 6 732 2562 2100
m 109 7 763 3325 1337
#!/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:


Keywords: grehack-2012 assembly x86 reverse-engineering crackme