Simple crack me.
Information
- category: reverse
- points: 50
Description
Note: Enclose the flag with flag{}.
1 file : generic_crackme.bin
Writeup
Execute the program:
$ ./generic_crackme.bin
plz enter password plz:
hii
lolno
Mh… Using ltrace
:
$ ltrace ./generic_crackme.bin
puts("plz enter password plz:"plz enter password plz:
) = 24
fgets(hii
"hii\n", 100, 0x7f0f2442b860) = 0x7ffffaa83bd0
puts("lolno"lolno
) = 6
+++ exited (status 0) +++
Ok it doesn’t give the informations we need.
Let’s analyze the binary using radare2 :
# r2 -d generic_crackme.bin
[0x7fc214358100]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[Warning: Invalid range. Use different search.in=? or anal.in=dbg.maps.x
Warning: Invalid range. Use different search.in=? or anal.in=dbg.maps.x
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[TOFIX: aaft can't run in debugger mode.ions (aaft)
[x] Type matching analysis for all functions (aaft)
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x7fc214358100]> afl
0x561d89546060 1 46 entry0
0x561d89548fe0 1 4124 reloc.__libc_start_main
0x561d89546030 1 6 sym.imp.puts
0x561d89546040 1 6 sym.imp.__stack_chk_fail
0x561d89545000 3 404 -> 397 loc.imp._ITM_deregisterTMCloneTable
0x561d895451aa 5 32 -> 55 fcn.561d895451aa
0x561d89546050 1 6 sym.imp.fgets
0x561d8954621f 6 128 main
0x561d89546150 5 153 -> 60 entry.init0
0x561d89546100 5 65 -> 55 entry.fini0
0x561d89548ff8 1 4100 reloc.__cxa_finalize
0x561d89546090 4 41 -> 34 fcn.561d89546090
[0x7fc214358100]> s main
[0x561d8954621f]> pdf
/ (fcn) main 128
| int main (int argc, char **argv, char **envp);
| ; var int32_t var_70h @ rbp-0x70
| ; var int32_t var_8h @ rbp-0x8
| ; DATA XREF from entry0 @ 0x561d89546081
| 0x561d8954621f 55 push rbp
| 0x561d89546220 4889e5 mov rbp, rsp
| 0x561d89546223 4883ec70 sub rsp, 0x70 ; 'p'
| 0x561d89546227 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
| 0x561d89546230 488945f8 mov qword [var_8h], rax
| 0x561d89546234 31c0 xor eax, eax
| 0x561d89546236 488d3dc70d00. lea rdi, str.plz_enter_password_plz: ; 0x561d89547004 ; "plz enter password plz:"
| 0x561d8954623d e8eefdffff call sym.imp.puts ; int puts(const char *s)
| 0x561d89546242 488b15f72d00. mov rdx, qword [reloc.stdin_64] ; [0x561d89549040:8]=0
| 0x561d89546249 488d4590 lea rax, [var_70h]
| 0x561d8954624d be64000000 mov esi, 0x64 ; 'd' ; 100
| 0x561d89546252 4889c7 mov rdi, rax
| 0x561d89546255 e8f6fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
| 0x561d8954625a 488d4590 lea rax, [var_70h]
| 0x561d8954625e 4889c7 mov rdi, rax
| 0x561d89546261 e802ffffff call 0x561d89546168
| 0x561d89546266 84c0 test al, al
| ,=< 0x561d89546268 740e je 0x561d89546278
| | 0x561d8954626a 488d3dab0d00. lea rdi, str.good_job_kthxbye ; 0x561d8954701c ; "good job kthxbye"
| | 0x561d89546271 e8bafdffff call sym.imp.puts ; int puts(const char *s)
| ,==< 0x561d89546276 eb0c jmp 0x561d89546284
| |`-> 0x561d89546278 488d3dae0d00. lea rdi, str.lolno ; 0x561d8954702d ; "lolno"
| | 0x561d8954627f e8acfdffff call sym.imp.puts ; int puts(const char *s)
| | ; CODE XREF from main @ 0x561d89546276
| `--> 0x561d89546284 b800000000 mov eax, 0
| 0x561d89546289 488b4df8 mov rcx, qword [var_8h]
| 0x561d8954628d 6448330c2528. xor rcx, qword fs:[0x28]
| ,=< 0x561d89546296 7405 je 0x561d8954629d
| | 0x561d89546298 e8a3fdffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
| `-> 0x561d8954629d c9 leave
\ 0x561d8954629e c3 ret
Or visually :
We can see that before the puts("good job kthxbye")
, the program calls a function 0x561d89546168
(I’m going to call this function flag) and if it returns 1 the program will print the good string. Let’s see what this function does :
[0x561d89546168]> pdf
p: Cannot find function at 0x561d89546168
[0x561d89546168]> pd
; CALL XREF from main @ 0x561d89546261
0x561d89546168 55 push rbp
0x561d89546169 4889e5 mov rbp, rsp
0x561d8954616c 4883ec08 sub rsp, 8
0x561d89546170 48897df8 mov qword [rbp - 8], rdi
0x561d89546174 488b45f8 mov rax, qword [rbp - 8]
0x561d89546178 0fb600 movzx eax, byte [rax]
0x561d8954617b 0fbec0 movsx eax, al
0x561d8954617e 89c7 mov edi, eax
0x561d89546180 e8d4ffffff call 0x561d89546159
0x561d89546185 83f865 cmp eax, 0x65 ; 'e' ; 101
,=< 0x561d89546188 740a je 0x561d89546194
| 0x561d8954618a b800000000 mov eax, 0
,==< 0x561d8954618f e989000000 jmp 0x561d8954621d
|`-> 0x561d89546194 488b45f8 mov rax, qword [rbp - 8]
| 0x561d89546198 4883c001 add rax, 1
| 0x561d8954619c 0fb600 movzx eax, byte [rax]
| 0x561d8954619f 0fbec0 movsx eax, al
| 0x561d895461a2 89c7 mov edi, eax
| 0x561d895461a4 e8b0ffffff call 0x561d89546159
| 0x561d895461a9 83f870 cmp eax, 0x70 ; 'p' ; 112
|,=< 0x561d895461ac 7407 je 0x561d895461b5
|| 0x561d895461ae b800000000 mov eax, 0
,===< 0x561d895461b3 eb68 jmp 0x561d8954621d
||`-> 0x561d895461b5 488b45f8 mov rax, qword [rbp - 8]
|| 0x561d895461b9 4883c002 add rax, 2
|| 0x561d895461bd 0fb600 movzx eax, byte [rax]
|| 0x561d895461c0 0fbec0 movsx eax, al
|| 0x561d895461c3 89c7 mov edi, eax
|| 0x561d895461c5 e88fffffff call 0x561d89546159
|| 0x561d895461ca 83f868 cmp eax, 0x68 ; 'h' ; 104
||,=< 0x561d895461cd 7407 je 0x561d895461d6
||| 0x561d895461cf b800000000 mov eax, 0
,====< 0x561d895461d4 eb47 jmp 0x561d8954621d
|||`-> 0x561d895461d6 488b45f8 mov rax, qword [rbp - 8]
||| 0x561d895461da 4883c003 add rax, 3
||| 0x561d895461de 0fb600 movzx eax, byte [rax]
||| 0x561d895461e1 0fbec0 movsx eax, al
||| 0x561d895461e4 89c7 mov edi, eax
||| 0x561d895461e6 e86effffff call 0x561d89546159
||| 0x561d895461eb 83f868 cmp eax, 0x68 ; 'h' ; 104
|||,=< 0x561d895461ee 7407 je 0x561d895461f7
|||| 0x561d895461f0 b800000000 mov eax, 0
,=====< 0x561d895461f5 eb26 jmp 0x561d8954621d
||||`-> 0x561d895461f7 488b45f8 mov rax, qword [rbp - 8]
|||| 0x561d895461fb 4883c004 add rax, 4
|||| 0x561d895461ff 0fb600 movzx eax, byte [rax]
|||| 0x561d89546202 0fbec0 movsx eax, al
|||| 0x561d89546205 89c7 mov edi, eax
|||| 0x561d89546207 e84dffffff call 0x561d89546159
|||| 0x561d8954620c 83f87a cmp eax, 0x7a ; 'z' ; 122
||||,=< 0x561d8954620f 7407 je 0x561d89546218
||||| 0x561d89546211 b800000000 mov eax, 0
,======< 0x561d89546216 eb05 jmp 0x561d8954621d
|||||`-> 0x561d89546218 b801000000 mov eax, 1
`````--> 0x561d8954621d c9 leave
0x561d8954621e c3 ret
We have multiple if on our input. If we try to write a pseudocode of this function we get:
int flag(char *string) == 0x561d89546168
{
// somefunc() = 0x561d89546159
if (somefunc(string[0]) == 'e')
if(somefunc(string[1]) == 'p')
if(somefunc(string[2]) == 'h')
if(somefunc(string[3]) == 'h')
if(somefunc(string[4]) == 'z')
return 1;
return 0;
}
Now what the function located at 0x561d89546159
(in our pseudocode somefunc) does :
[0x561d895461c5]> s 0x561d89546159
[0x561d89546159]> pd
0x561d89546159 55 push rbp
0x561d8954615a 4889e5 mov rbp, rsp
0x561d8954615d 897dfc mov dword [rbp - 4], edi
0x561d89546160 8b45fc mov eax, dword [rbp - 4]
0x561d89546163 83c001 add eax, 1
0x561d89546166 5d pop rbp
0x561d89546167 c3 ret
Psuedocode :
char somefunc(char c)
{
return c + 1;
}
So what we have to do to print good job kthxbye
?
We need to take the string ephhz
and substract to every character 1, in this way when the flag
function will compare the result of somefunc
char per char we will make all the if
statements true:
“exploit.py” :
s = 'ephhz'
payload = ''
for i in range(0, len(s)):
payload += chr(ord(s[i])-1)
print(payload)
Output : doggy
$ ./generic_crackme.bin
plz enter password plz:
doggy
good job kthxbye
Flag
flag{doggy}