Reversing ELF
Hey remember that I’ll have to change the flag so that you can try it and find the actual flag. You can find the challenges here.
Crackme1
This was one of the easiest crackme challenges. All you have to do is change the file to be an executable: chmod +x crackme1
Now if you run it you will find the flag
1
2
└─$ ./crackme1
flag{I have removed flag}
Crackme2
Once you have downloaded the binary file and changed the permissions to executable we can now start reversing it.
We are going to try and run the elf file.
1
2
└─$ ./crackme2
Usage: ./crackme2 password
Well it seems we have to put password just after the elf.
1
2
└─$ ./crackme2 test
Access denied.
We can see we don’t have the right password. We would use ltrace so as we can check if the password is being compared to a certain string.
1
2
3
4
5
6
└─$ ltrace ./crackme2 test
__libc_start_main(0x804849b, 2, 0xfff88504, 0x80485c0 <unfinished ...>
strcmp("test", "super_secret_password") = -1
puts("Access denied."Access denied.
) = 15
+++ exited (status 1) +++
“Test” is being compared to “super_secret_password” meaning that’s the actual password being compared to. Now let’s run it with the actual password
1
2
3
└─$ ./crackme2 super_secret_password
Access granted.
flag{I have removed flag}
Nice it gives us the flag.
Crackme3
On this challenge we shall do some recon on the file which we were given. We shall check if there are any strings on the file by using string command
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
41
42
43
44
45
46
47
48
49
50
51
52
53
└─$ strings crackme3
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
_IO_stdin_used
puts
strlen
malloc
stderr
fwrite
fprintf
strcmp
__libc_start_main
GLIBC_2.0
PTRh
iD$$
D$,;D$
UWVS
[^_]
Usage: %s PASSWORD
malloc failed
ZjByX3kwdXJfNWVjMG5kX2xlNTVvbl91bmJhc2U2NF80bGxfN2gzXzdoMW5nNQ==
Correct password!
Come on, even my aunt Mildred got this one!
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
;*2$"8
GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.ctors
.dtors
.jcr
.dynamic
.got
.got.plt
.data
.bss
.comment
We are able to see a base64 string which we shall decode it later. Let’s use Radare2
to check the strings. The first thing we shall do is open the file on radare2
and analyze it by using “aaaa
”
1
2
3
4
5
6
7
8
9
10
11
12
13
└─$ r2 crackme3
-- Add more blockchains to your life.
[0x08048440]> aaaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze all functions arguments/locals
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Finding and parsing C++ vtables (avrr)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information (aanr)
[x] Finding function preludes
[x] Enable constraint types analysis for variables
[0x08048440]>
Once we have analyzed it we shall use iz
which prints strings in data sections
1
2
3
4
5
6
7
8
9
10
11
[0x08048440]> iz
[Strings]
nth paddr vaddr len size section type string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00000e68 0x08048e68 19 20 .rodata ascii Usage: %s PASSWORD\n
1 0x00000e7c 0x08048e7c 14 15 .rodata ascii malloc failed\n
2 0x00000e8b 0x08048e8b 64 65 .rodata ascii ZjByX3kwdXJfNWVjMG5kX2xlNTVvbl91bmJhc2U2NF80bGxfN2gzXzdoMW5nNQ==
3 0x00000ed0 0x08048ed0 17 18 .rodata ascii Correct password!
4 0x00000ef0 0x08048ef0 43 44 .rodata ascii Come on, even my aunt Mildred got this one!
5 0x00000f1c 0x08048f1c 64 65 .rodata ascii ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
[0x08048440]>
We are able to get the same base64
string on the executable file.The next thing we shall do is to decode the base64
string.
1
2
└─$ echo "ZjByX3kwdXJfNWVjMG5kX2xlNTVvbl91bmJhc2U2NF80bGxfN2gzXzdoMW5nNQ==" | base64 -d
{I have removed flag}
Crackme4
On this challenge we are provides an elf file which it’s interesting because if we run the file we are somehow given hint.
1
2
3
└─$ ./crackme4
Usage : ./crackme4 password
This time the string is hidden and we used strcmp
So let’s try and supply it with a password
1
2
└─$ ./crackme4 test
password "test" not OK
Because we have some hint that it uses strcmp
to compare the actual password and the one you supplied it, let us use ltrace
.
1
2
3
4
5
6
└─$ ltrace ./crackme4 test
__libc_start_main(0x400716, 2, 0x7ffe8ee36218, 0x400760 <unfinished ...>
strcmp("my_m0r3_secur3_pwd", "test") = -7
printf("password "%s" not OK\n", "test"password "test" not OK
) = 23
+++ exited (status 0) +++
Well we can see it is comparing my_m0r3_secur3_pwd to test meaning my_m0r3_secur3_pwd is the correct password. Now running the binary with the password which we have found it gives us the password OK message meaning we are right.
1
2
└─$ ./crackme4 my_m0r3_secur3_pwd
password OK
Crackme5
By now I think it’s becoming easier and easier. So let’s have a look at crackme5 binary file. Let’s start by seeing if there is any strings on the binary by using strings. Well we ain’t lucky this time because their is no unusual string. By running the binary file normal with unknown input it gives us “Always dig deeper
” Now we shall run the elf using ltrace
to see if the input is being compared to a string.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
└─$ ltrace ./crackme5
__libc_start_main(0x400773, 1, 0x7ffdb0898aa8, 0x4008d0 <unfinished ...>
puts("Enter your input:"Enter your input:
) = 18
__isoc99_scanf(0x400966, 0x7ffdb0898960, 0, 0x7ffbcdfbe603test
) = 1
strlen("test") = 4
strlen("test") = 4
strlen("test") = 4
strlen("test") = 4
strlen("test") = 4
strncmp("test", "OfdlDSA|3tXb32~X3tX@sX`4tXtz", 28) = 37
puts("Always dig deeper"Always dig deeper
) = 18
+++ exited (status 0) +++
As you can see it is comparing test with OfdlDSA|3tXb32~X3tX@sX`4tXtz
so let’s try and use that as our input.
1
2
3
4
└─$ ./crackme5
Enter your input:
OfdlDSA|3tXb32~X3tX@sX`4tXtz
Good game
Well game over we are in. OfdlDSA|3tXb32~X3tX@sX`4tXtz
is the actual input being required.
Crackme6
We would start off by basic recon which is searching through the strings to check if we have something juicy. I think we are unlucky once again. So we shall use ltrace
to see if their is string comparison happening.
1
2
3
4
5
└─$ ltrace ./crackme6 test
__libc_start_main(0x400711, 2, 0x7ffc9bdb22b8, 0x400760 <unfinished ...>
printf("password "%s" not OK\n", "test"password "test" not OK
) = 23
+++ exited (status 0) +++
Well once again we are unlucky so we need to decompile the file using radare2
to see what’s happening. Once you have opened and analyzed using aaa
we would list all the function by afl
. There is main function so we would seek to main function by s main
and print assembly of main function by pdf
. We can see sym.compare_pwd
is being called.
1
2
0x0040074c 4889c7 mov rdi, rax ; int64_t arg1
0x0040074f e87dffffff call sym.compare_pwd
We would seek to sym.compare_pwd and print assembly of compare_pwd function and see what’s going on there.
1
2
3
0x004006dd 488b45f8 mov rax, qword [var_8h]
0x004006e1 4889c7 mov rdi, rax ; int64_t arg1
0x004006e4 e894feffff call sym.my_secure_test
Another function is being called and that’s my_secure_test. We shall seek to my_secure_test and print the assembly of that function. You will notice it is comparing on the al
register
1
2
0x00400594 0fb600 movzx eax, byte [rax]
0x00400597 3c31 cmp al, 0x31
We shall manually take the hex value which compares to the one stored on al
register which are: (0x31, 0x33, 0x33, 0x37, 0x5f, 0x70, 0x77, 0x64)
Now we have to convert the hex values to their corresponding strings. On this case let’s keep everything as beginner friendly as we can. So we shall still use radare2
to convert but also you can use a simple python script Append a question mark before the hex and it would convert it to different format. We shall grab the string because that’s what we want.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[0x0040057d]> ? 0x31
int32 49
uint32 49
hex 0x31
octal 061
unit 49
segment 0000:0031
string "1" <-----take this value
fvalue 49.0
float 0.000000f
double 0.000000
binary 0b00110001
ternary 0t1211
[0x0040057d]>
Take the values one after the other till 0x64
1
2
3
4
5
6
7
8
9
10
11
12
13
[0x0040057d]> ? 0x64
int32 100
uint32 100
hex 0x64
octal 0144
unit 100
segment 0000:0064
string "d"
fvalue 100.0
float 0.000000f
double 0.000000
binary 0b01100100
ternary 0t10201
Well… Again you can automate this process by using python. If you noticed we would be having 1337_pwd
as our password. Try running with it and you’ll have solved the challenge.
1
2
└─$ ./crackme6 1337_pwd
password OK
Crackme7
Once you have the elf we can run it to as intended so as we can know what the program is supposed to do.
1
2
3
4
5
6
└─$ ./crackme7
Menu:
[1] Say hello
[2] Add numbers
[3] Quit
As we can see you are required to choose one of the three numbers. Well let’s run the program on Radare2
so as we can have deep understanding of the program.
1
2
3
4
5
6
7
8
9
10
11
12
r2 crackme7
-- A git pull a day keeps the segfault away
[0x080483c0]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze all functions arguments/locals
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Finding and parsing C++ vtables (avrr)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information (aanr)
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x080483c0]>
Seek on the main function and you will see there is a comparison and if it is true it would call a function which prints the flag.
1
2
3
4
5
6
7
0x08048665 3d697a0000 cmp eax, 0x7a69
│ ││││ │╭─< 0x0804866a 7517 jne 0x8048683
│ ││││ ││ 0x0804866c 83ec0c sub esp, 0xc
│ ││││ ││ 0x0804866f 68bc880408 push str.Wow_such_h4x0r_ ; 0x80488bc ; "Wow such h4x0r!" ; const char *s
│ ││││ ││ 0x08048674 e8f7fcffff call sym.imp.puts ; int puts(const char *s)
│ ││││ ││ 0x08048679 83c410 add esp, 0x10
│ ││││ ││ 0x0804867c e825000000 call sym.giveFlag
eax
is being compared to 0x7a69
which is a hex value. If you convert it to decimal you will find it is 31337
. Going back to the program and using 31337 instead of 1,2 or 3 it would display the flag.
1
2
3
4
5
6
7
8
9
10
└─$ ./crackme7 105 ⨯
Menu:
[1] Say hello
[2] Add numbers
[3] Quit
[>] 31337
Wow such h4x0r!
flag{I have removed flag}
Crackme8
This one of the best challenges in this series of challenges.We are given an elf file which we have to reverse it. We would start by seeing if we can get something juicy by using strings. Well we don’t have anything which can help us there. Let’s use ltrace
to see if the program compares the input we give with any hard coded string.
1
2
3
4
5
6
└─$ ltrace ./crackme8 test
__libc_start_main(0x804849b, 2, 0xff9efb94, 0x80485c0 <unfinished ...>
atoi(0xff9f01be, 0xff9efb94, 0xff9efba0, 0x80485e1) = 0
puts("Access denied."Access denied.
) = 15
+++ exited (status 1) +++
Once again it doesn’t. We shall open it with Radare2
and analyze then seek to main function.
1
2
3
4
5
6
7
8
9
10
11
12
13
─$ r2 crackme8
-- Beer in mind.
[0x080483a0]> aaaaaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze all functions arguments/locals
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Finding and parsing C++ vtables (avrr)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information (aanr)
[x] Finding function preludes
[x] Enable constraint types analysis for variables
[0x080483a0]> s main
From here, their are two ways to solve the challenge i) Method 1 set a break point on this address0x080484e4
i.e db 0x080484e4
.You can start by using ood test
then dc
to continue. On that address eax
is being compared to 0xcafef00d
then jumps to 0x8048502
1
2
0x080484e4 3d0df0feca cmp eax, 0xcafef00d
0x080484e9 7417 je 0x8048502
So we can set eip
to 0x8048502
meaning we have jumped the comparison happening on 0x080484e9
1
2
[0xf7f12070]> dc
hit breakpoint at: 0x80484e4
To change the eip
register we shall use dr eip=0x8048502
1
2
[0x080484e4]> dr eip=0x08048502
0x080484e4 ->0x08048502
So continue to run the program and you will notice we have bypassed the comparison and now it prints the flag.
1
2
3
4
5
[0x080484e4]> dc
Access granted.
flag{I have removed flag}
(25658) Process exited with status=0x0
[0xf7f0f559]>
ii) Method 2 We shall set a breakpoint on the same address which is 0x080484e4
Now rather than us jumping to 0x8048502
we shall edit the value of eax
register to 0xcafef00d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[0xf7f00070]> dc
hit breakpoint at: 0x80484e4
[0x080484e4]> dr
eax = 0x00000000
ebx = 0x00000000
ecx = 0xff93418f
edx = 0x0000001d
esi = 0x00000002
edi = 0x080483a0
esp = 0xff933b50
ebp = 0xff933b58
eip = 0x080484e4
eflags = 0x00000286
oeax = 0xffffffff
[0x080484e4]>
To change the eax
register we shall use dr eax=0xcafef00d
and now when we print all the registers, eax
has been changed.
1
2
3
4
5
6
7
8
9
10
11
12
13
[0x080484e4]> dr
eax = 0xcafef00d
ebx = 0x00000000
ecx = 0xff93418f
edx = 0x0000001d
esi = 0x00000002
edi = 0x080483a0
esp = 0xff933b50
ebp = 0xff933b58
eip = 0x080484e4
eflags = 0x00000286
oeax = 0xffffffff
[0x080484e4]>
Register eax
is being compared to 0xcafef00d
which it’s true and it prints the flag.
1
2
3
4
5
[0x080484e4]> dc
Access granted.
flag{I have removed flag}
(25951) Process exited with status=0x0
[0xf7efd559]>
Well this was fun…