Crackme-4
- Author
-
0x42697262
- Play Date
-
2025/06/13 - 2025/06/13
Steps
file
I always start with file
out of habit:
$ file Crackme-4 Crackme-4: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4aef34e93a282bc5d5be68cd1319c78b63a55b0e, for GNU/Linux 3.2.0, not stripped
Confirmed it’s a 64-bit ELF unstripped binary.
strings
Ran strings
to look for hardcoded password:
$ strings Crackme-4 ... GLIBCXX_3.4 u/UH []A\A]A^A_ Sorry that is incorrect. Please enter the password: That's CORRECT, Well Done. ;*3$" GCC: (Debian 9.2.1-4) 9.2.1 20190821 ...
No obvious password found.
Ghidra
Loaded in Ghidra.
Noticed many functions (e.g. c0()
to c9()
) being used to build the password inside buildPassword()
.
Realized the password was assembled byte-by-byte.
I considered recreating the logic of all these functions (e.g. writing a python script to simulate them) but decided it would be too tedious. Not to mention, I don’t want to do this method when it scales up to thousands of functions.
GDB + GEF
Opened GDB with GEF and ran the binary.
Started checking each cX()
return value.
Then, I noted down the return values to manually piece together the password.
In addition, I tried altering the comparison checks:
-
Set breakpoints on
cmp
instructions after eachcX()
call. -
Used:
set $bl = $al
to force my input byte to match expected values.
I was able to get these values from the cX() functions:
c3() = 0x3a c0() = 0x55 c1() = 0x36 c2() = 0x2d c4() = 0x59 c8() = 0x2b c5() = 0x4c c6() = 0x2e c7() = 0x22 c9() = ??
Which is just U6-:YL."+\
but I missed the function c9() and the character for it was /
.
What I should have done
After seeing the disassembly of Vault::checkPassword()
:
gef➤ disas /r _ZN5Vault13checkPasswordEv Dump of assembler code for function _ZN5Vault13checkPasswordEv: 0x0000000000001874 <+0>: 55 push rbp 0x0000000000001875 <+1>: 48 89 e5 mov rbp,rsp 0x0000000000001878 <+4>: 48 83 ec 10 sub rsp,0x10 0x000000000000187c <+8>: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi 0x0000000000001880 <+12>: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 0x0000000000001884 <+16>: 48 89 c7 mov rdi,rax 0x0000000000001887 <+19>: e8 90 fc ff ff call 0x151c <_ZN11PasswordGen13buildPasswordEv> 0x000000000000188c <+24>: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] (1) 0x0000000000001890 <+28>: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8] 0x0000000000001894 <+32>: 48 83 c2 08 add rdx,0x8 0x0000000000001898 <+36>: 48 89 d6 mov rsi,rdx 0x000000000000189b <+39>: 48 89 c7 mov rdi,rax 0x000000000000189e <+42>: e8 f3 f8 ff ff call 0x1196 <_ZN11PasswordGen13checkPasswordEPc> 0x00000000000018a3 <+47>: 84 c0 test al,al 0x00000000000018a5 <+49>: 74 13 je 0x18ba <_ZN5Vault13checkPasswordEv+70> 0x00000000000018a7 <+51>: 48 8d 35 90 07 00 00 lea rsi,[rip+0x790] # 0x203e 0x00000000000018ae <+58>: 48 8d 3d ab 27 00 00 lea rdi,[rip+0x27ab] # 0x4060 <_ZSt4cout@@GLIBCXX_3.4> 0x00000000000018b5 <+65>: e8 b6 f7 ff ff call 0x1070 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> 0x00000000000018ba <+70>: 90 nop 0x00000000000018bb <+71>: c9 leave 0x00000000000018bc <+72>: c3 ret End of assembler dump.
1 | Breakpoint here |
I could’ve just set a breakpoint at _ZN5Vault13checkPasswordEv+24 then inspected the value of $rdi
.
I ran gdb again.
gef➤ r Starting program: /home/chicken/crackmes/Crackme-4 Function(s) ^std::(move|forward|as_const|(__)?addressof) will be skipped when stepping. Function(s) ^std::(shared|unique)_ptr<.*>::(get|operator) will be skipped when stepping. Function(s) ^std::(basic_string|vector|array|deque|(forward_)?list|(unordered_|flat_)?(multi)?(map|set)|span)<.*>::(c?r?(begin|end)|front|back|data|size|empty) will be skipped when stepping. Function(s) ^std::(basic_string|vector|array|deque|span)<.*>::operator.] will be skipped when stepping. [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Please enter the password: hello
Entered a random password and gdb will pause at the breakpoint I have set earlier.

Then finally print out the password with x/s 0x000055555556bad0
:

Although, there is no need to print it out since the password can already be seen in $rdi register.
Okay, maybe I was wrong earlier about needing /
since after the +
symbol is a newline character.
Although for some reason, it still accepted with or without /
as the final character.