Write-up-josamont-j666

From aldeid
Jump to navigation Jump to search

Description

This crackme is available here: http://crackmes.de/users/josamont/j666/. It is an 32-bit ELF written in assembly, with stripped symbols.

File ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, stripped
MD5 60b7c56f174faf4b617af4d724fda88d
SHA1 2d2d85f649822cc42c57f091e1f9449f0c0451a5
SHA256 60b7971688eaf90b287f981b01fd59378d60930d33235cb9d7dbeead5620de0e

When run, it prompts for a password and returns "No" if it is incorrect.

$ ./j666
Crackme 666 Josep
Password: aldeid
No

Analysis

Graph overview

The start function is as follows:

Analysis of the start function

At offset 0x048091, there is a call to sub_048074 (renamed f_compute_checksum) that updates a checksum at memory location 0x8049292 but this variable will never be accessed. So we won't spend time on this function.

LOAD:08048091                               start           proc near
LOAD:08048091 E8 DE FF FF FF                                call    f_compute_checksum

Then, starting from offset 0x8048096, the program displays a banner and prompts for a password.

LOAD:08048096
LOAD:08048096                               loc_8048096:
LOAD:08048096 B9 9A 91 04 08                                mov     ecx, offset aCrackme666Jose ; "Crackme 666 Josep\n"
LOAD:0804809B BA 13 00 00 00                                mov     edx, 13h
LOAD:080480A0 E8 70 00 00 00                                call    f_printf
LOAD:080480A5 B9 AD 91 04 08                                mov     ecx, offset aPassword ; "Password: "
LOAD:080480AA BA 0B 00 00 00                                mov     edx, 0Bh
LOAD:080480AF E8 61 00 00 00                                call    f_printf
LOAD:080480B4 B8 03 00 00 00                                mov     eax, 3          ; sys_read
LOAD:080480B9 BB 01 00 00 00                                mov     ebx, 1          ; fd
LOAD:080480BE B9 D8 91 04 08                                mov     ecx, offset my_password ; addr
LOAD:080480C3 BA 08 00 00 00                                mov     edx, 8          ; len
LOAD:080480C8 CD 80                                         int     80h             ; LINUX - sys_read

At offset 0x80480CA, the program calls a function that:

  • ensures that no breakpoint has been set in the interesting section (repe cmpsb at offset 0x80480E8)
  • ensures that the provided password is exclusively composed of hexadecimal characters in uppercase
  • builds an array (checksum_) in little-endian from the password
LOAD:080480CA E8 53 00 00 00                                call    f_check_my_password_hex
LOAD:080480CF B9 09 00 00 00                                mov     ecx, 9

Then, it calls another function (sub_8048122 renamed f_check_my_password_hex) at offset 0x80480D4 but it's useless for the rest of the analysis, so we won't analyze it. Then, it saves 4 bytes of memory location 0x8048096 in ESI at offset 0x80480DE and saves the checksum_ array in EDI at offset 0x80480E3:

LOAD:080480D4 E8 AC 00 00 00                                call    f_do_nothing_interesting
LOAD:080480D9 B9 04 00 00 00                                mov     ecx, 4
LOAD:080480DE BE 96 80 04 08                                mov     esi, offset loc_8048096 ; esi = [B9 9A 91 04]
LOAD:080480E3 BF 89 92 04 08                                mov     edi, offset checksum_ ; password = 04919AB9?

sub_8048122 (f_check_my_password_hex)

The function starts by checking whether the code at offset 0x8048E8 has been modified. What this section of code does is to ensure that no software breakpoint has been set at this location. If the bytes at location 0x8048E8 are not equal to 0x1174A6F3, the program will exit.

LOAD:08048122                               f_check_my_password_hex proc near
LOAD:08048122 8B 2D 96 92 04 08                             mov     ebp, dword_8049296 ; ebp = 0x1174A6F3
LOAD:08048128 BB E8 80 04 08                                mov     ebx, offset loc_80480E8 ; ebx = mem_80480E8
LOAD:0804812D BF 01 00 00 00                                mov     edi, 1          ; edi = 1
LOAD:08048132 8B 03                                         mov     eax, [ebx]      ; eax = mem_80480E8
LOAD:08048134 31 E8                                         xor     eax, ebp        ; integrity check (loc_80480E8)
LOAD:08048136 75 D4                                         jnz     short exit      ; exit if code has been modified (e.g. software breakpoint set)
;... [SNIP] ...
LOAD:080480E8                               loc_80480E8:
LOAD:080480E8 F3 A6                                         repe cmpsb                 ; opcode F3 A6
LOAD:080480EA 74 11                                         jz      short loc_80480FD  ; opcode 74 11

Then starting from offset 0x8048160, the program loops through all characters of the password and ensures that they are hexadecimal characters (in the range 0x30 - 0x46) and save the password in a little-endian array that will be saved into memory location 0x8049289 (checksum) at offset 0x804817E:

Solution

Static approach

Back to the start function, there is a check at offset 0x80480E8 (where the integrity check was previously performed) that compares bytes of ESI (set to [0xB9, 0x9A, 0x91, 0x04]) and EDI (set to our little-endian password). If both value match, the program jumps to the good boy.

LOAD:080480DE BE 96 80 04 08                                mov     esi, offset loc_8048096 ; esi = [B9 9A 91 04]
LOAD:080480E3 BF 89 92 04 08                                mov     edi, offset checksum_   ; password little endian
LOAD:080480E8
LOAD:080480E8                               loc_80480E8:
LOAD:080480E8 F3 A6                                         repe cmpsb
LOAD:080480EA 74 11                                         jz      short good_boy
LOAD:080480EC B9 57 81 04 08                                mov     ecx, offset loc_8048157
LOAD:080480F1 BA 04 00 00 00                                mov     edx, 4
LOAD:080480F6 E8 1A 00 00 00                                call    f_printf
LOAD:080480FB EB 0F                                         jmp     short exit
LOAD:080480FD                               ; ---------------------------------------------------------------------------
LOAD:080480FD
LOAD:080480FD                               good_boy:
LOAD:080480FD B9 42 81 04 08                                mov     ecx, offset loc_8048142
LOAD:08048102 BA 04 00 00 00                                mov     edx, 4
LOAD:08048107 E8 09 00 00 00                                call    f_printf

With all that in mind, we can easily guess that our password should be [0xB9, 0x9A, 0x91, 0x04] in reverse order: 04919AB9.

Dynamic approach

Note that we might also have solved this crackme with a hardware breakpoint, as follows:

$ gdb -q j666
Reading symbols from j666...(no debugging symbols found)...done.
(gdb) b *0x8048091
Breakpoint 1 at 0x8048091
(gdb) r
Starting program: j666 

Breakpoint 1, 0x08048091 in ?? ()
(gdb) hbreak *0x80480E8
Hardware assisted breakpoint 2 at 0x80480e8
(gdb) c
Continuing.
Crackme 666 Josep
Password: aldeid

Breakpoint 2, 0x080480e8 in ?? ()
(gdb) x/x $esi
0x8048096:	0x04919ab9

At offset 0x8048096, ESI contains the value of the expected password (04919ab9) that you will just need to put in upper case.

Verification

Now, let's verify our password:

$ ./j666 
Crackme 666 Josep
Password: 04919AB9
OK

Comments

Keywords: crackme reverse-engineering josamont j666 elf keygen