GreHack-2015/50-EasyRE
You are here: | 50-EasyRE
|
Description
We have to deal with a 64-bit ELF:
$ file re50 re50: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f41797f7bb4823df72478c166b1ee316ef38b6f2, not stripped
The program is expecting an argument and returns Bad boy ! when the one provided is not the expected one:
$ ./re50 Usage : ./re50 password $ ./re50 aldeid Bad boy !
Analysis
Graph overview
The code layout is as follows:
Initiailization
Starting from offset 0x4005AE, the code is building an encryption key and checks that an argument is passed to the program at offset 0x4005DB. If no argument is passed, the code will jump to 0x4005E1 (Usage).
.text:0000000000400596 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000400596 public main
.text:0000000000400596 main proc near
.text:0000000000400596
.text:0000000000400596 var_70 = qword ptr -70h
.text:0000000000400596 num_args = dword ptr -64h
.text:0000000000400596 s = byte ptr -60h
.text:0000000000400596 counter_i = dword ptr -24h
.text:0000000000400596
.text:0000000000400596 push rbp
.text:0000000000400597 mov rbp, rsp
.text:000000000040059A push r13
.text:000000000040059C push r12
.text:000000000040059E push rbx
.text:000000000040059F sub rsp, 58h
.text:00000000004005A3 mov [rbp+num_args], edi
.text:00000000004005A6 mov [rbp+var_70], rsi
.text:00000000004005AA lea rax, [rbp+s] ; encryption key
.text:00000000004005AE mov rcx, 5D5445565B415E4Bh ; 0x4B, 0x5E, 0x41, 0x5B, 0x56, 0x45, 0x54, 0x5D
.text:00000000004005B8 mov [rax], rcx
.text:00000000004005BB mov rsi, 554356415256445Ch ; 0x5C, 0x44, 0x56, 0x52, 0x41, 0x56, 0x43, 0x55
.text:00000000004005C5 mov [rax+8], rsi
.text:00000000004005C9 mov dword ptr [rax+10h], 525E415Eh ; 0x5E, 0x41, 0x5E, 0x52
.text:00000000004005D0 mov byte ptr [rax+14h], 0
.text:00000000004005D4 mov [rbp+counter_i], 0
.text:00000000004005DB cmp [rbp+num_args], 1
.text:00000000004005DF jg short loc_4005FF
Encryption
At offset 0x400651, the code is XORing each character of the user input (password) with the rolling encryption key.
.text:00000000004005FF loc_4005FF:
.text:00000000004005FF mov [rbp+counter_i], 0
.text:0000000000400606 jmp short loc_400660
.text:0000000000400608 ; ---------------------------------------------------------------------------
.text:0000000000400608
.text:0000000000400608 loc_400608:
.text:0000000000400608 mov eax, [rbp+counter_i]
.text:000000000040060B cdqe
.text:000000000040060D movzx r12d, [rbp+rax+s]
.text:0000000000400613 mov rax, [rbp+var_70]
.text:0000000000400617 add rax, 8
.text:000000000040061B mov r13, [rax]
.text:000000000040061E mov eax, [rbp+counter_i]
.text:0000000000400621 movsxd rbx, eax
.text:0000000000400624 mov rax, [rbp+var_70]
.text:0000000000400628 add rax, 8
.text:000000000040062C mov rax, [rax]
.text:000000000040062F mov rdi, rax ; s
.text:0000000000400632 call _strlen
.text:0000000000400637 mov rcx, rax
.text:000000000040063A mov rax, rbx
.text:000000000040063D mov edx, 0
.text:0000000000400642 div rcx
.text:0000000000400645 mov rax, rdx
.text:0000000000400648 add rax, r13
.text:000000000040064B movzx eax, byte ptr [rax]
.text:000000000040064E mov edx, r12d
.text:0000000000400651 xor edx, eax ; password[i] ^ encryption_key[i]
.text:0000000000400653 mov eax, [rbp+counter_i]
.text:0000000000400656 cdqe
.text:0000000000400658 mov [rbp+rax+s], dl
.text:000000000040065C add [rbp+counter_i], 1
.text:0000000000400660
.text:0000000000400660 loc_400660:
.text:0000000000400660 mov eax, [rbp+counter_i]
.text:0000000000400663 movsxd rbx, eax
.text:0000000000400666 lea rax, [rbp+s]
.text:000000000040066A mov rdi, rax ; s
.text:000000000040066D call _strlen
.text:0000000000400672 cmp rbx, rax
.text:0000000000400675 jb short loc_400608
Verification
The code then checks whether the encrypted password results in the following string: xorhavenosecretforme:
.text:0000000000400677 lea rax, [rbp+s]
.text:000000000040067B mov esi, offset s2 ; "xorhavenosecretforme"
.text:0000000000400680 mov rdi, rax ; s1
.text:0000000000400683 call _strcmp
.text:0000000000400688 test eax, eax
.text:000000000040068A jnz short loc_40069D
.text:000000000040068C mov edi, offset aGoodBoy ; "Good boy !"
.text:0000000000400691 mov eax, 0
.text:0000000000400696 call _printf
.text:000000000040069B jmp short loc_4006AC
.text:000000000040069D ; ---------------------------------------------------------------------------
.text:000000000040069D
.text:000000000040069D loc_40069D:
.text:000000000040069D mov edi, offset aBadBoy ; "Bad boy !"
.text:00000000004006A2 mov eax, 0
.text:00000000004006A7 call _printf
Solution
The solution can be scripted in python:
#!/usr/bin/env python
p = []
x = [0x4B, 0x5E, 0x41, 0x5B, 0x56,
0x45, 0x54, 0x5D, 0x5C, 0x44,
0x56, 0x52, 0x41, 0x56, 0x43,
0x55, 0x5E, 0x41, 0x5E, 0x52]
s = "xorhavenosecretforme"
for (c, i) in enumerate(s):
p.append(ord(i) ^ x[c])
print(''.join([chr(i) for i in p]))
Let's run the script:
$ ./solution_re50.py 31337313373133731337
We can confirm that this is the correct password:
$ ./re50 31337313373133731337 Good boy !
Comments
Keywords: grehack-2015 assembly reverse-engineering crackme