Post

IOLI - crackme0x09

This level adds nothing new to the previous nine crackmes of the IOLI - suite Let’s have a look to the function main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[0x08048420]> pdf @ main
/ (fcn) main 120
|           ; var int local_78h @ ebp-0x78
|           ; var int local_4h @ ebp-0x4
|           ; arg int arg_10h @ ebp+0x10
|           ; var int local_4h @ esp+0x4
|           ; DATA XREF from 0x08048437 (entry0)
|           0x080486ee      55             push ebp
|           0x080486ef      89e5           mov ebp, esp
|           0x080486f1      53             push ebx
|           0x080486f2      81ec84000000   sub esp, 0x84
|           0x080486f8      e869000000     call fcn.08048766 ; ebx = eip + 5
|           0x080486fd      81c3f7180000   add ebx, 0x18f7
|           0x08048703      83e4f0         and esp, 0xfffffff0
|           0x08048706      b800000000     mov eax, 0
|           0x0804870b      83c00f         add eax, 0xf
|           0x0804870e      83c00f         add eax, 0xf
|           0x08048711      c1e804         shr eax, 4
|           0x08048714      c1e004         shl eax, 4
|           0x08048717      29c4           sub esp, eax
|           0x08048719      8d8375e8ffff   lea eax, [ebx - 0x178b]
|           0x0804871f      890424         mov dword [esp], eax
|           0x08048722      e8b9fcffff     call sym.imp.printf ; "IOLI Crackme Level 0x09\n"
|           0x08048727      8d838ee8ffff   lea eax, [ebx - 0x1772]
|           0x0804872d      890424         mov dword [esp], eax
|           0x08048730      e8abfcffff     call sym.imp.printf ; "Password:"
|           0x08048735      8d4588         lea eax, [ebp - local_78h]
|           0x08048738      89442404       mov dword [esp + local_4h], eax
|           0x0804873c      8d8399e8ffff   lea eax, [ebx - 0x1767]
|           0x08048742      890424         mov dword [esp], eax ; "%s"
|           0x08048745      e876fcffff     call sym.imp.scanf
|           0x0804874a      8b4510         mov eax, dword [ebp + arg_10h]
|           0x0804874d      89442404       mov dword [esp + local_4h], eax
|           0x08048751      8d4588         lea eax, [ebp - local_78h]
|           0x08048754      890424         mov dword [esp], eax
|           0x08048757      e8bafeffff     call sub.strlen_616
|           0x0804875c      b800000000     mov eax, 0
|           0x08048761      8b5dfc         mov ebx, dword [ebp - local_4h]
|           0x08048764      c9             leave
\           0x08048765      c3             ret

It looks like the program is compiled using the flag -fPIC and the compiler chose ebx as PIC register. The function at 0x08048766 is used to assign to ebx the instruction immediately following the call instruction. Since the size of a call instruction is 5 bytes this translates into ebx = eip + 5.

1
2
3
4
5
6
[0x08048420]> pdf @ fcn.08048766 
/ (fcn) fcn.08048766 4
|           ; XREFS: CALL 0x080486f8  CALL 0x0804861d  CALL 0x080484db  CALL 0x08048564  CALL 0x08048590       
|           ; XREFS: CALL 0x08048778 
|           0x08048766      8b1c24         mov ebx, dword [esp] ; eip was pushed into the stack
\           0x08048769      c3             ret

Looking at the XREFS we can see that this function is called quite often. For convenience let’s rename it: afn init_ebx 0x08048766
After this call and the instruction add ebx, 0x18f7 the register ebx points to the end of the GOT: ebx = 0x080486fd + 0x18f7.

Then, to see what contains the string passed to the first printf as argument we can just do like this:

1
2
[0x08048420]> ps @ section_end..got - 0x178b
IOLI Crackme Level 0x09

The function main would look more or less like this:

#define SIZE_PASS 100 // This is just a random value
int main(int argc, char **argv, char **envp){

    char password[SIZE_PASS];

    printf("IOLI Crackme Level 0x09\n");
    printf("Password: \n");

    scanf("%s",password);

    strlen_616(password, envp);

    return 0;

}

Lets have a look to the first block of the function strlen_616:

1
2
3
4
5
6
7
8
0x08048616      55             push ebp
0x08048617      89e5           mov ebp, esp
0x08048619      53             push ebx
0x0804861a      83ec24         sub esp, 0x24
0x0804861d      e844010000     call init_ebx
0x08048622      81c3d2190000   add ebx, 0x19d2
0x08048628      c745f4000000.  mov dword [ebp - local_ch], 0
0x0804862f      c745f0000000.  mov dword [ebp - local_10h], 0

After the usual prelude, there are two values initialize with 0. We can suppose they are counters used in some sort of loop, let’s rename them count_1 and count_2.
From the second block it would seem that count_2 is used in a loop along all the length of our password:

1
2
3
4
5
0x08048636      8b4508         mov eax, dword [ebp + password]
0x08048639      890424         mov dword [esp], eax  
0x0804863c      e88ffdffff     call sym.imp.strlen 
0x08048641      3945f0         cmp dword [ebp - count_2], eax
0x08048644      734f           jae 0x8048695

Now let’s have a look at what happens inside this loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0x08048646      8b45f0         mov eax, dword [ebp - count_2]                        
0x08048649      034508         add eax, dword [ebp + password]                       
0x0804864c      0fb600         movzx eax, byte [eax]                                 
0x0804864f      8845ef         mov byte [ebp - local_11h], al                        
0x08048652      8d45f8         lea eax, [ebp - local_8h] 
0x08048655      89442408       mov dword [esp + local_8h], eax                       
0x08048659      8d835ee8ffff   lea eax, [ebx - 0x17a2]   
0x0804865f      89442404       mov dword [esp + local_4h], eax                       
0x08048663      8d45ef         lea eax, [ebp - local_11h] 
0x08048666      890424         mov dword [esp], eax                                  
0x08048669      e882fdffff     call sym.imp.sscanf ; sscanf(local_11h, "%d", &local_8h)
0x0804866e      8b55f8         mov edx, dword [ebp - local_8h]                       
0x08048671      8d45f4         lea eax, [ebp - count_1]
0x08048674      0110           add dword [eax], edx  ; count_1 += local_8h
0x08048676      837df410       cmp dword [ebp - count_1], 0x10
0x0804867a      7512           jne 0x804868e   

So, the program reads one by one all the characters of our password as they were integers. For the sake of clarity, let’s rename some variable:

  • count_1 -> sum
  • local_11h -> pass_char
  • local_8h -> char_value

A function is called when the value of sum is equal to 0x10:

1
2
3
4
5
0x0804867c      8b450c         mov eax, dword [ebp + envp]
0x0804867f      89442404       mov dword [esp + local_4h], eax 
0x08048683      8b4508         mov eax, dword [ebp + password]
0x08048686      890424         mov dword [esp], eax
0x08048689      e8fbfeffff     call sub.sscanf_589; sscanf_589(password,envp)

Otherwise the value of count_2 is incremented and the loop continues:

1
2
3
0x0804868e      8d45f0         lea eax, [ebp - count_2]
0x08048691      ff00           inc dword [eax]                                       
0x08048693      eba1           jmp 0x8048636      

If we go out of the loop, the program calls a function which will print an error message and exit:

1
2
3
4
5
6
7
8
9
10
11
0x0804855d      55             push ebp
0x0804855e      89e5           mov ebp, esp                                         
0x08048560      53             push ebx
0x08048561      83ec04         sub esp, 4                                                             
0x08048564      e8fd010000     call init_ebx               
0x08048569      81c38b1a0000   add ebx, 0x1a8b                                                        
0x0804856f      8d8349e8ffff   lea eax, [ebx - 0x17b7]
0x08048575      890424         mov dword [esp], eax                                                   
0x08048578      e863feffff     call sym.imp.printf ; "Invalid password!\n"
0x0804857d      c70424000000.  mov dword [esp], 0                                                     
0x08048584      e887feffff     call sym.imp.exit

So apparently we must enter into the function named by radare sub.sscanf_589. This gives as a first constraint: at some point the sum of the characters of our password must be equal to 0x10 (16). So, “466” would work, but “1234abc” or “999” would not. So let’s see what happens when the password meets this constraint:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0x08048589      55             push ebp
0x0804858a      89e5           mov ebp, esp
0x0804858c      53             push ebx
0x0804858d      83ec14         sub esp, 0x14
0x08048590      e8d1010000     call init_ebx
0x08048595      81c35f1a0000   add ebx, 0x1a5f
0x0804859b      8d45f8         lea eax, [ebp - value]
0x0804859e      89442408       mov dword [esp + 8], eax
0x080485a2      8d835ee8ffff   lea eax, [ebx - 0x17a2]
0x080485a8      89442404       mov dword [esp + 4], eax
0x080485ac      8b4508         mov eax, dword [ebp + password]
0x080485af      890424         mov dword [esp], eax
0x080485b2      e839feffff     call sym.imp.sscanf ; sscanf(password, "%d", &value)
0x080485b7      8b450c         mov eax, dword [ebp + envp]
0x080485ba      89442404       mov dword [esp + 4], eax
0x080485be      8b45f8         mov eax, dword [ebp - value]
0x080485c1      890424         mov dword [esp], eax 
0x080485c4      e80bffffff     call sub.strncmp_4d4 ; sub.strncmp_4d4(value, envp)
0x080485c9      85c0           test eax, eax
0x080485cb      7443           je 0x8048610

This function reads the value of the whole password as if it was an integer and put it in a variable that I already renamed value. Then ,if the value returned by the function sub.strncmp_4d4(value, envp) is 0, it jumps to the address 0x8048610 end just returns:

1
2
3
4
0x08048610      83c414         add esp, 0x14                                                           
0x08048613      5b             pop ebx                                                                 
0x08048614      5d             pop ebp                                                                 
0x08048615      c3             ret      

Otherwise we enter in a loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0x080485cd      c745f4000000.  mov dword [ebp - count], 0 
0x080485d4      837df409       cmp dword [ebp - count], 9
0x080485d8      7f36           jg 0x8048610 ; quit the loop if (count > 9)
0x080485da      8b45f8         mov eax, dword [ebp - value]                                         
0x080485dd      83e001         and eax, 1                                                              
0x080485e0      85c0           test eax, eax                                                           
0x080485e2      7525           jne 0x8048609 ; continue if (value & 1 != 0) 
0x080485e4      8b83fcffffff   mov eax, dword [ebx - 4] ; probably a global var
0x080485ea      833801         cmp dword [eax], 1 
0x080485ed      750e           jne 0x80485fd ; so [eax] must be == 1 
0x080485ef      8d8361e8ffff   lea eax, [ebx - 0x179f] ; "Password OK!\n"
0x080485f5      890424         mov dword [esp], eax                                                    
0x080485f8      e8e3fdffff     call sym.imp.printf  
0x080485fd      c70424000000.  mov dword [esp], 0                                                      
0x08048604      e807feffff     call sym.imp.exit  
0x08048609      8d45f4         lea eax, [ebp - count]  
0x0804860c      ff00           inc dword [eax] ; count++
0x0804860e      ebc4           jmp 0x80485d4 

For count = 0 until count <= 9, if the value & 1 == 0 and the content at [ebx - 4] is an address to a value of 1 we pass the level! In order to have value & 1 == 0, value must be an even number. Now let’s see the function sub.strncmp_4d4 and let’s figure out how to obtain a return value != 0. This is the content after the usual prelude:

1
2
3
4
5
6
0x080484e6      c745f8000000.  mov dword [ebp - count], 0
0x080484ed      8b45f8         mov eax, dword [ebp - count]
0x080484f0      8d1485000000.  lea edx, [eax*4]
0x080484f7      8b450c         mov eax, dword [ebp + envp]
0x080484fa      833c0200       cmp dword [edx + eax], 0
0x080484fe      7448           je 0x8048548

It looks like a loop over all the values contained into envp.

1
2
3
4
5
6
7
8
9
10
11
12
13
0x08048500      8b45f8         mov eax, dword [ebp - count]
0x08048503      8d1485000000.  lea edx, [eax*4]
0x0804850a      8b4d0c         mov ecx, dword [ebp + envp]
0x0804850d      8d45f8         lea eax, [ebp - count]
0x08048510      ff00           inc dword [eax]
0x08048512      8d8344e8ffff   lea eax, [ebx - 0x17bc]; "LOLO"
0x08048518      c74424080300.  mov dword [esp + 8], 3
0x08048520      89442404       mov dword [esp + 4], eax
0x08048524      8b040a         mov eax, dword [edx + ecx]
0x08048527      890424         mov dword [esp], eax
0x0804852a      e8d1feffff     call sym.imp.strncmp; strncmp(envp[count],"LOLO",3)
0x0804852f      85c0           test eax, eax
0x08048531      75ba           jne 0x80484ed

Apparently the program search for an env. variable having a name that starts with “LOL”. If this variable exists, the function changes the value pointed by [ebx - 4] to 1, and returns 1:

1
2
3
4
5
6
7
8
9
10
11
12
0x08048533      8b83fcffffff   mov eax, dword [ebx - 4]                                                
0x08048539      c70001000000   mov dword [eax], 1                                                      
0x0804853f      c745f4010000.  mov dword [ebp - local_ch], 1                                           
0x08048546      eb0c           jmp 0x8048554 

... 

0x08048554      8b45f4         mov eax, dword [ebp - local_ch]                                         
0x08048557      83c414         add esp, 0x14                                                           
0x0804855a      5b             pop ebx                                                                 
0x0804855b      5d             pop ebp                                                                 
0x0804855c      c3             ret  

This is exactly what we need in order to pass the level. To recap: - The password must start with a series of numbers which sum is equal to 16 - The password must be an even number - There must be an env. variable having a name that starts with “LOL”

So let’s try it out:

1
2
3
4
$ export LOL=123; ./crackme0x09
IOLI Crackme Level 0x09
Password: 556
Password OK!

Cool, it worked :)

This post is licensed under CC BY 4.0 by the author.