EZ crackme
name | EZ crackme |
category | reversing |
platform | crackmes.one |
link | https://crackmes.one/crackme/5fcfb87933c5d424269a1afc |
ctf | n/a |
description | n/a |
file inspection
I started off by running file
to determine the file type. The output was as follows:
> file run.exe
run.exe: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, with debug_info, not stripped
It’s a 32-bit ELF binary. Running readelf
on it gives us interesting information about the sections especially .symtab
(the Symbol Table):
> readelf -a run.exe
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08049000 001000 000045 00 AX 0 0 16
[ 2] .data PROGBITS 0804a000 002000 00001d 00 WA 0 0 4
[ 3] .debug_aranges PROGBITS 00000000 00201d 000020 00 0 0 1
[ 4] .debug_info PROGBITS 00000000 00203d 00003a 00 0 0 1
[ 5] .debug_abbrev PROGBITS 00000000 002077 00001b 00 0 0 1
[ 6] .debug_line PROGBITS 00000000 002092 00004a 00 0 0 1
[ 7] .symtab SYMTAB 00000000 0020dc 000140 10 8 16 4
[ 8] .strtab STRTAB 00000000 00221c 000076 00 0 0 1
[ 9] .shstrtab STRTAB 00000000 002292 00005c 00 0 0 1
Symbol table '.symtab' contains 20 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08049000 0 SECTION LOCAL DEFAULT 1
2: 0804a000 0 SECTION LOCAL DEFAULT 2
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 5
6: 00000000 0 SECTION LOCAL DEFAULT 6
7: 00000000 0 FILE LOCAL DEFAULT ABS code.asm
8: 0804900e 0 NOTYPE LOCAL DEFAULT 1 _start.goodjob
9: 08049026 0 NOTYPE LOCAL DEFAULT 1 _start.wrong
10: 0804903c 0 NOTYPE LOCAL DEFAULT 1 _start.end
11: 0804a000 1 OBJECT LOCAL DEFAULT 2 password
12: 0804a008 1 OBJECT LOCAL DEFAULT 2 goodjobText
13: 0000000e 0 NOTYPE LOCAL DEFAULT ABS goodjobLen
14: 0804a016 1 OBJECT LOCAL DEFAULT 2 nope
15: 00000007 0 NOTYPE LOCAL DEFAULT ABS nopeLen
16: 08049000 0 NOTYPE GLOBAL DEFAULT 1 _start
17: 0804a01d 0 NOTYPE GLOBAL DEFAULT 2 __bss_start
18: 0804a01d 0 NOTYPE GLOBAL DEFAULT 2 _edata
19: 0804a020 0 NOTYPE GLOBAL DEFAULT 2 _end
disassembly
Moving on to disassembling the program, I used objdump
.
> objdump -M intel -d run.exe
run.exe: file format elf32-i386
Disassembly of section .text:
08049000 <_start>:
8049000: 5b pop ebx
8049001: 5b pop ebx
8049002: 5b pop ebx
8049003: a1 00 a0 04 08 mov eax,ds:0x804a000
8049008: 3b 03 cmp eax,DWORD PTR [ebx]
804900a: 74 02 je 804900e <_start.goodjob>
804900c: eb 18 jmp 8049026 <_start.wrong>
0804900e <_start.goodjob>:
804900e: b8 04 00 00 00 mov eax,0x4
8049013: bb 01 00 00 00 mov ebx,0x1
8049018: b9 08 a0 04 08 mov ecx,0x804a008
804901d: ba 0e 00 00 00 mov edx,0xe
8049022: cd 80 int 0x80
8049024: eb 16 jmp 804903c <_start.end>
08049026 <_start.wrong>:
8049026: b8 04 00 00 00 mov eax,0x4
804902b: bb 01 00 00 00 mov ebx,0x1
8049030: b9 16 a0 04 08 mov ecx,0x804a016
8049035: ba 07 00 00 00 mov edx,0x7
804903a: cd 80 int 0x80
0804903c <_start.end>:
804903c: b8 01 00 00 00 mov eax,0x1
8049041: 31 db xor ebx,ebx
8049043: cd 80 int 0x80
It’s a simple program which starts off by comparing an initialized variable in the data segment (ds) with an argument. Based on the result of the cmp
(compare) operation, it executes the write
syscall to write a message to stdout
(file descriptor 1). So now, the C program would look something like this:
if (arg == *0x804a000){
write(1, *0x804a008, 0xe);
}
else {
write(1, *0x804a016, 0x7);
}
The Linux Man page for the write syscall explains each argument. But it’s still pretty self explanatory—
write(int fd, const void *buf, size_t count);
exploitation
We now know where to look for our secret string. Firing up radare2
:
> r2 run.exe
[0x08049000]> s 0x804a000
[0x0804a000]> px
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x0804a000 5034 3535 7730 7264 596f 7520 476f 7420 P455w0rdYou Got
0x0804a010 5468 6973 210a 5772 6f6e 6721 0aff ffff This!.Wrong!....
0x0804a020 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a030 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a040 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a050 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a060 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a070 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a080 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a090 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a0a0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a0b0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a0c0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a0d0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a0e0 ffff ffff ffff ffff ffff ffff ffff ffff ................
0x0804a0f0 ffff ffff ffff ffff ffff ffff ffff ffff ................
Looks like we found our key! P455w0rd
. Running the binary with P455w0rd
as an argument:
> ./run.exe P455w0rd
You Got This!
However, the compare instruction is done with a DWORD, i.e 4 bytes. So the key is P455
.
> ./run.exe P455
You Got This!