Solving 7+ years old crackme (CC-Team, Arigo CrackMe8)

[Polish version / polska wersja]

Intro

Yesterday night I’ve got an idea to search some unsolved Polish crackme. And I bumped into this one: http://cc-team.org/index.php?name=projekty&show=43. There is no solution on the site, and I found none searching in Google. Perhaps, it was waiting for the solution above 7 years😀.

It is not difficult crackme ( maybe level 2-3 according to Crackmes.de). But yet entertaining and giving a good opportunity to illustrate some concepts to beginners.

This challenge don’t have one defined solution -but I hope mine is good enough for what the author expected🙂 Here it is: loader.c

CrackMe

Let’s start by downloading the file: cm8 (mirror cm8).

Its 32- bit ELF.

The goal is: “deploying program so that it will print the congratulation message and return 0”. Binary edition is acceptable, but not desired.

After deploying it just prints the welcome message “crackme6 by arigo” end exits. So, the first guess is, it should be deployed with some args. After playing around I noticed, that supplying 3 random args will crash the app. OK, let’s look inside…

Analysis

$ objdump -d ./cm8

./cm8:     file format elf32-i386

Disassembly of section .text:

080480a0 <_start>:
 80480a0:    89 e6                    mov    %esp,%esi
 80480a2:    31 c0                    xor    %eax,%eax
 80480a4:    b0 04                    mov    $0x4,%al
 80480a6:    31 db                    xor    %ebx,%ebx
 80480a8:    43                       inc    %ebx
 80480a9:    68 6f 0a 41 41           push   $0x41410a6f
 80480ae:    68 61 72 69 67           push   $0x67697261
 80480b3:    68 20 62 79 20           push   $0x20796220
 80480b8:    68 6b 6d 65 36           push   $0x36656d6b
 80480bd:    68 63 72 61 63           push   $0x63617263
 80480c2:    89 e1                    mov    %esp,%ecx
 80480c4:    31 d2                    xor    %edx,%edx
 80480c6:    b2 12                    mov    $0x12,%dl
 80480c8:    cd 80                    int    $0x80
 80480ca:    89 f4                    mov    %esi,%esp
 80480cc:    58                       pop    %eax
 80480cd:    5b                       pop    %ebx
 80480ce:    59                       pop    %ecx
 80480cf:    5a                       pop    %edx
 80480d0:    5e                       pop    %esi
 80480d1:    68 cb cd 80 90           push   $0x9080cdcb
 80480d6:    68 31 c0 40 fe           push   $0xfe40c031
 80480db:    68 ff e6 31 db           push   $0xdb31e6ff
 80480e0:    68 4b 75 e9 5e           push   $0x5ee9754b
 80480e5:    68 88 06 41 46           push   $0x46410688
 80480ea:    68 8a 11 28 d0           push   $0xd028118a
 80480ef:    68 75 04 89 f9           push   $0xf9890475
 80480f4:    68 fe ca fe c2           push   $0xc2fecafe
 80480f9:    68 8a 06 8a 11           push   $0x118a068a
 80480fe:    68 31 c0 31 d2           push   $0xd231c031
 8048103:    68 31 db b3 5c           push   $0x5cb3db31
 8048108:    68 52 56 8d 0f           push   $0xf8d5652
 804810d:    68 ba 12 89 e6           push   $0xe68912ba
 8048112:    68 24 68 83 ba           push   $0xba836824
 8048117:    68 68 48 fb b1           push   $0xb1fb4868
 804811c:    68 a3 16 84 90           push   $0x908416a3
 8048121:    68 48 de 3e 68           push   $0x683ede48
 8048126:    68 ac 1f 68 51           push   $0x51681fac
 804812b:    68 bc 68 90 0f           push   $0xf9068bc
 8048130:    68 68 02 a4 82           push   $0x82a40268
 8048135:    68 61 c6 da 70           push   $0x70dac661
 804813a:    68 94 73 a4 68           push   $0x68a47394
 804813f:    68 8e 6b 68 7a           push   $0x7a686b8e
 8048144:    68 cd 68 8b ae           push   $0xae8b68cd
 8048149:    68 68 e4 28 96           push   $0x9628e468
 804814e:    68 28 88 34 f1           push   $0xf1348828
 8048153:    68 3e c7 22 68           push   $0x6822c73e
 8048158:    68 13 94 68 96           push   $0x96689413
 804815d:    68 9f 68 7e db           push   $0xdb7e689f
 8048162:    68 68 4e ee 6b           push   $0x6bee4e68
 8048167:    68 02 51 32 b2           push   $0xb2325102
 804816c:    68 d9 4d 3a 68           push   $0x683a4dd9
 8048171:    68 fd d8 68 2f           push   $0x2f68d8fd
 8048176:    68 f8 68 e9 50           push   $0x50e968f8
 804817b:    68 68 80 c5 28           push   $0x28c58068
 8048180:    68 48 50 3a b0           push   $0xb03a5048
 8048185:    68 95 85 86 68           push   $0x68868595
 804818a:    68 65 23 68 2f           push   $0x2f682365
 804818f:    68 6d 68 9d 3b           push   $0x3b9d686d
 8048194:    68 68 ad 17 a7           push   $0xa717ad68
 8048199:    68 1f b0 02 b8           push   $0xb802b01f
 804819e:    68 00 00 5a 68           push   $0x685a0000
 80481a3:    68 0f 85 9c 00           push   $0x9c850f
 80481a8:    68 5f 5f 3c 04           push   $0x43c5f5f
 80481ad:    68 a6 00 00 00           push   $0xa6
 80481b2:    68 f8 4f 0f 84           push   $0x840f4ff8
 80481b7:    68 31 c0 5f 89           push   $0x895fc031
 80481bc:    89 e7                    mov    %esp,%edi
 80481be:    6a 00                    push   $0x0
 80481c0:    56                       push   %esi
 80481c1:    52                       push   %edx
 80481c2:    51                       push   %ecx
 80481c3:    53                       push   %ebx
 80481c4:    50                       push   %eax
 80481c5:    ff e7                    jmp    *%edi

The code is pretty obfuscated🙂
First part (till 80480c8) calls int80 with eax = 4 (sys_write) and bl=0x12 – it prints 0x12 characters of string pushed on the stack in form of DWORDs (it is the welcome message).
The second part [80480ca to 80481c5] is more mysterious – and- easy to notice – here the key lies. Some content is pushed into the stack, and then it is executed…
It comes handy to open the crackme in gdb with TUI mode:
gdb -tui ./cm8
We will use 2 layouts: layout asm, layout regs.
First let’s open layout asm. Set breakpoint at the line with jump *edi, and then run the app with some 3 arguments…
arigo_cm8_0
Press enter, and when it breaks type
ni
(for the next instruction – in this case it is within the stack!)
arigo_cm8_1
That’s cool, because now all the secrets revealed: we can see the obfuscated part of code.

For the convenience I copied it aside and added comments. (Mind that stack addresses are random each execution).

>│0xbffff4c8  xor  eax,eax  ; EAX = 0  
 │0xbffff4ca  pop  edi    ; EDI <- argc  
 │0xbffff4cb  mov  eax,edi   
 │0xbffff4cd  dec  edi   ; if (argc - 1) == 0  
 │0xbffff4ce  je  0xbffff57a ; goto exit_with_error  
 │0xbffff4d4  pop  edi    
 │0xbffff4d5  pop  edi    
 │0xbffff4d6  cmp  al,0x4  ; if (argc != 4) -> supplied NOT 3 args 
 │0xbffff4d8  jne  0xbffff57a ; goto exit_with_error  
 │0xbffff4de  pop  edx    

 │0xbffff4df  push  0xb802b01f   ; pushing on the stack 23 DWORDS -> 23 * 4 = 92 BYTEs 
 │0xbffff4e4  push  0x6da717ad  
 │0xbffff4e9  push  0x23653b9d  
 │0xbffff4ee  push  0x8685952f  
 │0xbffff4f3  push  0xb03a5048  
 │0xbffff4f8  push  0xf828c580  
 │0xbffff4fd  push  0xd8fd50e9  
 │0xbffff502  push  0x3a4dd92f  
 │0xbffff507  push  0xb2325102  
 │0xbffff50c  push  0x9f6bee4e  
 │0xbffff511  push  0x9413db7e  
 │0xbffff516  push  0x22c73e96  
 │0xbffff51b  push  0xf1348828  
 │0xbffff520  push  0xcd9628e4  
 │0xbffff525  push  0x6b8eae8b    
 │0xbffff52a  push  0xa473947a  
 │0xbffff52f  push  0x70dac661  
 │0xbffff534  push  0xbc82a402  
 │0xbffff539  push  0x1fac0f90  
 │0xbffff53e  push  0x3ede4851  
 │0xbffff543  push  0x908416a3  
 │0xbffff548  push  0x24b1fb48  
 │0xbffff54d  push  0x12baba83  
 │0xbffff552  mov  esi,esp  <- stack top  
 │0xbffff554  push  edx    
 │0xbffff555  push  esi    
 │0xbffff556  lea  ecx,[edi]  ; ECX =  1-st arg  
 │0xbffff558  xor  ebx,ebx  <- EBX = 0  
 │0xbffff55a  mov  bl,0x5c  <- EBX = 0x5c = 92  
 │0xbffff55c  xor  eax,eax  <- eax = 0  
 │0xbffff55e  xor  edx,edx  

loop_top : do EBX = 92 times:
 │0xbffff560  mov  al,BYTE PTR [esi]  <- take from the stack to AL    
 │0xbffff562  mov  dl,BYTE PTR [ecx]  <- take from 1st arg to DL 
 │0xbffff564  dec  dl  ;   
 │0xbffff566  inc  dl  ; test DL, DL   

  /0xbffff568  jne  0xbffff56e  ; if (DL != 0) goto decode
 │ 0xbffff56a  mov  ecx,edi  ; reset ECX (start buffer from the 1st arg beginning)  
 │ 0xbffff56c  mov  dl,BYTE PTR [ecx]  ; take character from the buffer
decode:
 │0xbffff56e  sub  al,dl  ; AL -= DL  
 │0xbffff570  mov  BYTE PTR [esi],al   ECX ++
 │0xbffff573  inc  esi  ; take next byte  -> ESI ++  
 │0xbffff574  dec  ebx  ; decrement the counter -> EBX--
^---0xbffff575  jne  0xbffff560  ; if (ebx != 0)  goto loop_top

 │0xbffff577  pop  esi  ; recover pointer to stack top   
 │0xbffff578  jmp  esi  ; redirect to the CODE ON THE STACK!
 
exit_with_error:
 │0xbffff57a  xor  ebx,ebx  <- ebx = 0  
 │0xbffff57c  xor  eax,eax  <- eax = 0  
 │0xbffff57e  inc  eax  ; eax = 1  
 │0xbffff57f  dec  bl   ; bl = (-1) = 0xff 
 │0xbffff581  int  0x80  ; sys_exit (-1)


Summing up:

What this code do:
1. check if there are 3 arguments -> if not: sys_exit (-1)
2. otherwise: push 92 bytes on the stack
3. take the 1-st argument as the KEY
4. decode the pushed content with the KEY (by simple subtraction), pseudocode:

for (i = 0, j = 0; i < 92; i++, j++) { 	
	if (j >= key_len) j = 0;
	pushed[i] = pushed[i] - key[j];
}

Means, the KEY can be anything that, subtracted from the pushed array, will give a valid output…
But what the valid output should be? This is not precisely defined. We only know that we must fit in 92 bytes, do printing and clean exit…
In the previous crackmes of the same author the congratulation message was:
got it😉
so, let’s stick to it.
If we take a closer look, all the composing parts of the solution are already in the crackme, we must just assemble it. Let’s take string printing part and adjust it:

B0 04                 mov     al, 4
31 DB                 xor     ebx, ebx
43                    inc     ebx             ; fd
68 2D 29 0A 41        push    410A292Dh       ; '-)/0aA'
68 69 74 20 3B        push    3B207469h       ; 'it ;'
68 67 6F 74 20        push    20746F67h       ; 'got '
89 E1                 mov     ecx, esp        ; addr
31 D2                 xor     edx, edx
B2 0B                 mov     dl, 0Bh         ; len
CD 80                 int     80h             ; LINUX - sys_write

And the clean exit:

33 DB      xor  ebx,ebx  <- ebx = 0  
33 C0      xor  eax,eax  <- eax = 0  
40         inc  eax  <- eax = 1  
CD 80      int  0x80  ; sys_exit (0)

After writing down the opcodes it will look like:

char solution[] = { 0xB0, 0x04,
		0x31, 0xDB,
		0x43,
		0x68, 0x2d,  0x29, 0x0A, 0x41,
		0x68, 0x69, 0x74, 0x20, 0x3b,
		0x68, 0x67,  0x6f, 0x74, 0x20,
		0x89, 0xE1,
		0x31, 0xD2,
		0xB2, 0x0B,
		0xCD, 0x80,
		0x33 , 0xdb,
		0x33, 0xc0,
		0x40,
		0xcd, 0x80
	};

And what more we need is the pushed content in the same form:

	char pushed[] = {0x83, 0xba, 0xba, 0x12, 0x48, 0xfb, 0xb1, 0x24,
		0xa3, 0x16, 0x84, 0x90, 0x51, 0x48, 0xde, 0x3e, 0x90, 0x0f,
		0xac, 0x1f, 0x02, 0xa4, 0x82, 0xbc, 0x61, 0xc6, 0xda, 0x70,
		0x7a, 0x94, 0x73, 0xa4, 0x8b, 0xae, 0x8e, 0x6b, 0xe4, 0x28,
		0x96, 0xcd, 0x28, 0x88, 0x34, 0xf1, 0x96, 0x3e, 0xc7, 0x22,
		0x7e, 0xdb, 0x13, 0x94, 0x4e, 0xee, 0x6b, 0x9f, 0x02, 0x51,
		0x32, 0xb2, 0x2f, 0xd9, 0x4d, 0x3a, 0xe9, 0x50, 0xfd, 0xd8,
		0x80, 0xc5, 0x28, 0xf8, 0x48, 0x50, 0x3a, 0xb0, 0x2f, 0x95,
		0x85, 0x86, 0x9d, 0x3b, 0x65, 0x23, 0xad, 0x17, 0xa7, 0x6d,
 		0x1f, 0xb0, 0x02, 0xb8};

The only remaining thing is decoding:

	size_t size = sizeof(solution);
	for (int i = 0, j = 0; i < size ; i++, j++) {
		unsigned char res = pushed[i]- solution[j];
		printf("\\x%02x", res);
	}

And the result:
\xd3\xb6\x89\x37\x05\x93\x84\xfb\x99\xd5\x1c\x27\xdd\x28\xa3\xd6\x29\xa0\x38\xff\x79\xc3\x51\xea\xaf\xbb\x0d\xf0\x47\xb9\x40\xe4\x4b\xe1\x0e
must be supplied as a 1-st argument to ./cm8. Can be done i.e by this small loader:

#include <stdio.h>
#include <unistd.h>

char *args[4] = {"0", 
	"\xd3\xb6\x89\x37\x05\x93\x84\xfb\x99\xd5\x1c\x27\xdd\x28\xa3\xd6\x29\xa0\x38\xff\x79\xc3\x51\xea\xaf\xbb\x0d\xf0\x47\xb9\x40\xe4\x4b\xe1\x0e", 
	"2",
	"3" 
};

int main()
{
	execve("./cm8", args, NULL);
	return 0;
}

arigo_cm8_solved
That’s all! Hope you enjoyed🙂

About hasherezade

Programmer and researcher, interested in InfoSec.
This entry was posted in CrackMe and tagged , , , . Bookmark the permalink.

One Response to Solving 7+ years old crackme (CC-Team, Arigo CrackMe8)

  1. Pingback: Rozwiązanie do CC-Team Arigo Crackme8 | hasherezade's 1001 nights [PL]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s