crackme2-be-D4RK_FL0W
- Author
-
0x42697262
- Play Date
-
2024/11/08 - 2024/11/9
Static Analysis
First checked what type of binary the challenge is.
$ file crack2-by-D4RK_FL0Wcrack2-by-D4RK_FL0W: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0871944192e8647d242b6212762d588346cd1b73, not stripped
This crackme is similar to the previous challenge that I did. Lucky that the debug information aren’t stripped.
Next I check if there are hardcoded strings.
$ strings crack2-by-D4RK_FL0W/lib64/ld-linux-x86-64.so.2 d}$+b libstdc++.so.6 __gmon_start__ _ITM_deregisterTMCloneTable _ITM_registerTMCloneTable _ZNSt8ios_base4InitD1Ev _ZNSt8ios_base4InitC1Ev _ZNSi7getlineEPcl _ZSt3cin _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc _ZSt4cout libm.so.6 libgcc_s.so.1 libc.so.6 __cxa_atexit strcat __cxa_finalize __libc_start_main GLIBC_2.2.5 GLIBCXX_3.4 u/UH BBCCf []A\A]A^A_ Please Enter The Password: ***Correct You Cracked It*** Wrong ;*3$" Hello this crackMe from D4RK_FL0W HAVE GCC: (Debian 8.2.0-20) 8.2.0 crtstuff.c deregister_tm_clones __do_global_dtors_aux completed.7325 __do_global_dtors_aux_fini_array_entry frame_dummy __frame_dummy_init_array_entry crack2-by-D4RK_FL0W.cpp _ZStL19piecewise_construct _ZStL8__ioinit _Z41__static_initialization_and_destruction_0ii _GLOBAL__sub_I_pass __FRAME_END__ __GNU_EH_FRAME_HDR _DYNAMIC __init_array_end __init_array_start _GLOBAL_OFFSET_TABLE_ _edata key6 _ZNSi7getlineEPcl@@GLIBCXX_3.4 _IO_stdin_used _Z3xxxv __cxa_finalize@@GLIBC_2.2.5 main key3 __dso_handle key4 __cxa_atexit@@GLIBC_2.2.5 key1 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4 key9 __TMC_END__ key2 _ZSt4cout@@GLIBCXX_3.4 __data_start key7 __bss_start _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4 __libc_csu_init key8 _ITM_deregisterTMCloneTable strcat@@GLIBC_2.2.5 __libc_csu_fini _ZSt3cin@@GLIBCXX_3.4 _Z14check_passwordPc __libc_start_main@@GLIBC_2.2.5 __gmon_start__ _ITM_registerTMCloneTable key5 _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4 .symtab .strtab .shstrtab .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .dynamic .got.plt .data .bss .comment
There’s a few strings that might have been initialized as a variable.
Please Enter The Password: ***Correct You Cracked It*** Wrong Hello this crackMe from D4RK_FL0W HAVE
This might give a hint about figuring the password.
I always use objdump to get an overview of the binary before heading to Ghidra. Albeit
$ objdump -r -M intel -d crack2-by-D4RK_FL0W0000000000001175 <main>:
1175: 55 push rbp
1176: 48 89 e5 mov rbp,rsp
1179: 48 83 ec 20 sub rsp,0x20
117d: 48 8d 35 85 0e 00 00 lea rsi,[rip+0xe85] # 2009 <_ZStL19piecewise_construct+0x1>
1184: 48 8d 3d 15 2f 00 00 lea rdi,[rip+0x2f15] # 40a0 <_ZSt4cout@GLIBCXX_3.4>
118b: e8 c0 fe ff ff call 1050 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
1190: 48 8d 45 e0 lea rax,[rbp-0x20]
1194: ba 11 00 00 00 mov edx,0x11
1199: 48 89 c6 mov rsi,rax
119c: 48 8d 3d 1d 30 00 00 lea rdi,[rip+0x301d] # 41c0 <_ZSt3cin@GLIBCXX_3.4>
11a3: e8 88 fe ff ff call 1030 <_ZNSi7getlineEPcl@plt>
11a8: e8 ea 00 00 00 call 1297 <_Z3xxxv> (1)
11ad: 48 8d 45 e0 lea rax,[rbp-0x20]
11b1: 48 89 c7 mov rdi,rax
11b4: e8 33 00 00 00 call 11ec <_Z14check_passwordPc> (2)
11b9: 84 c0 test al,al
11bb: 74 15 je 11d2 <main+0x5d>
11bd: 48 8d 35 64 0e 00 00 lea rsi,[rip+0xe64] # 2028 <_ZStL19piecewise_construct+0x20>
11c4: 48 8d 3d d5 2e 00 00 lea rdi,[rip+0x2ed5] # 40a0 <_ZSt4cout@GLIBCXX_3.4>
11cb: e8 80 fe ff ff call 1050 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
11d0: eb 13 jmp 11e5 <main+0x70>
11d2: 48 8d 35 6f 0e 00 00 lea rsi,[rip+0xe6f] # 2048 <_ZStL19piecewise_construct+0x40>
11d9: 48 8d 3d c0 2e 00 00 lea rdi,[rip+0x2ec0] # 40a0 <_ZSt4cout@GLIBCXX_3.4>
11e0: e8 6b fe ff ff call 1050 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
11e5: b8 00 00 00 00 mov eax,0x0
11ea: c9 leave
11eb: c3 ret
00000000000011ec <_Z14check_passwordPc>:
11ec: 55 push rbp
11ed: 48 89 e5 mov rbp,rsp
11f0: 48 89 7d e8 mov QWORD PTR [rbp-0x18],rdi
11f4: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
11fb: 83 7d fc 0f cmp DWORD PTR [rbp-0x4],0xf
11ff: 7f 31 jg 1232 <_Z14check_passwordPc+0x46>
1201: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
1204: 48 63 d0 movsxd rdx,eax
1207: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
120b: 48 01 d0 add rax,rdx
120e: 0f b6 10 movzx edx,BYTE PTR [rax]
1211: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
1214: 48 98 cdqe
1216: 48 8d 0d c3 30 00 00 lea rcx,[rip+0x30c3] # 42e0 <pass>
121d: 0f b6 04 08 movzx eax,BYTE PTR [rax+rcx*1]
1221: 38 c2 cmp dl,al
1223: 74 07 je 122c <_Z14check_passwordPc+0x40>
1225: b8 00 00 00 00 mov eax,0x0
122a: eb 0b jmp 1237 <_Z14check_passwordPc+0x4b>
122c: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1
1230: eb c9 jmp 11fb <_Z14check_passwordPc+0xf>
1232: b8 01 00 00 00 mov eax,0x1
1237: 5d pop rbp
1238: c3 ret
0000000000001297 <_Z3xxxv>: (3)
1297: 55 push rbp
1298: 48 89 e5 mov rbp,rsp
129b: 48 8d 35 b9 2d 00 00 lea rsi,[rip+0x2db9] # 405b <key3>
12a2: 48 8d 3d 37 30 00 00 lea rdi,[rip+0x3037] # 42e0 <pass>
12a9: e8 c2 fd ff ff call 1070 <strcat@plt>
12ae: 48 8d 05 2b 30 00 00 lea rax,[rip+0x302b] # 42e0 <pass>
12b5: 48 c7 c1 ff ff ff ff mov rcx,0xffffffffffffffff
12bc: 48 89 c2 mov rdx,rax
12bf: b8 00 00 00 00 mov eax,0x0
12c4: 48 89 d7 mov rdi,rdx
12c7: f2 ae repnz scas al,BYTE PTR es:[rdi]
12c9: 48 89 c8 mov rax,rcx
12cc: 48 f7 d0 not rax
12cf: 48 8d 50 ff lea rdx,[rax-0x1]
12d3: 48 8d 05 06 30 00 00 lea rax,[rip+0x3006] # 42e0 <pass>
12da: 48 01 d0 add rax,rdx
12dd: 66 c7 00 41 41 mov WORD PTR [rax],0x4141
12e2: c6 40 02 00 mov BYTE PTR [rax+0x2],0x0
12e6: 48 8d 35 69 2d 00 00 lea rsi,[rip+0x2d69] # 4056 <key2>
12ed: 48 8d 3d ec 2f 00 00 lea rdi,[rip+0x2fec] # 42e0 <pass>
12f4: e8 77 fd ff ff call 1070 <strcat@plt>
12f9: 48 8d 35 7f 2d 00 00 lea rsi,[rip+0x2d7f] # 407f <key9>
1300: 48 8d 3d d9 2f 00 00 lea rdi,[rip+0x2fd9] # 42e0 <pass>
1307: e8 64 fd ff ff call 1070 <strcat@plt>
130c: 48 8d 05 cd 2f 00 00 lea rax,[rip+0x2fcd] # 42e0 <pass>
1313: 48 c7 c1 ff ff ff ff mov rcx,0xffffffffffffffff
131a: 48 89 c2 mov rdx,rax
131d: b8 00 00 00 00 mov eax,0x0
1322: 48 89 d7 mov rdi,rdx
1325: f2 ae repnz scas al,BYTE PTR es:[rdi]
1327: 48 89 c8 mov rax,rcx
132a: 48 f7 d0 not rax
132d: 48 8d 50 ff lea rdx,[rax-0x1]
1331: 48 8d 05 a8 2f 00 00 lea rax,[rip+0x2fa8] # 42e0 <pass>
1338: 48 01 d0 add rax,rdx
133b: c7 00 42 42 43 43 mov DWORD PTR [rax],0x43434242
1341: 66 c7 40 04 44 00 mov WORD PTR [rax+0x4],0x44
1347: 90 nop
1348: 5d pop rbp
1349: c3 ret
134a: 66 0f 1f 44 00 00 nop WORD PTR [rax+rax*1+0x0]
| 1 | xxx() is called albeit not having any parameters or returned values |
| 2 | check_password() function called to verify the input from stdin |
| 3 | This function generates a password based on the existing variable keys (key1 to key9, not all are used) |
I am merely guessing what this code does so far.
Ghidra
The main function in the decompiled code initiates the password generation process by calling the xxx() function, which I have relabeled as generate_password in Ghidra for clarity.
This function is responsible for creating the required password, after which the program checks if the provided password matches the generated one.
undefined8 main(void)
{
char is_password_valid;
char password_input [32];
std::operator<<((basic_ostream *)std::cout,"\nPlease Enter The Password: ");
std::basic_istream<>::getline(std::cin,(long)password_input);
generate_password();
is_password_valid = check_password(password_input);
if (is_password_valid == '\0') {
std::operator<<((basic_ostream *)std::cout,"\nWrong\n");
}
else {
std::operator<<((basic_ostream *)std::cout,"\n\n***Correct You Cracked It***\n");
}
return 0;
}
The generate_password() function does not appear to take any parameters as input, this might hint that it relies on global variables.
This includes key1 through key9 and password, with password appearing to be built up incrementally through calls to the strcat() function.
Interestingly, not all keys are utilized in the concatenation process—only key2, key3, and key9 contribute to the final password value.
Here is the generate_password() function in the decompiled code:
void generate_password(void)
{
char cVar1;
ulong maxed_out_ulong;
byte whatisthis;
undefined4 *address_of_password;
whatisthis = 0;
strcat((char *)&password,(char *)&key3);
maxed_out_ulong = 0xffffffffffffffff;
address_of_password = &password;
do {
if (maxed_out_ulong == 0) break;
maxed_out_ulong = maxed_out_ulong - 1;
cVar1 = *(char *)address_of_password;
address_of_password = (undefined4 *)((long)address_of_password + (ulong)whatisthis * -2 + 1);
} while (cVar1 != '\0');
*(undefined2 *)(~maxed_out_ulong + 0x1042df) = 0x4141;
*(undefined *)((long)&password + ~maxed_out_ulong + 1) = 0;
strcat((char *)&password,(char *)&key2);
strcat((char *)&password,(char *)&key9);
maxed_out_ulong = 18446744073709551615;
address_of_password = &password;
do {
if (maxed_out_ulong == 0) break;
maxed_out_ulong = maxed_out_ulong - 1;
cVar1 = *(char *)address_of_password;
address_of_password = (undefined4 *)((long)address_of_password + (ulong)whatisthis * -2 + 1);
} while (cVar1 != '\0');
*(undefined4 *)(~maxed_out_ulong + 1065695) = 1128481346;
*(undefined2 *)((long)&password + ~maxed_out_ulong + 3) = 68;
return;
}
generate_password() manipulates and builds the password by concatenating specific key values and performing low-level memory operations.
It then stores values directly at specific memory offsets, which influence the resulting password.
The password validation algorithm checks each character individually. Here is the code for check_password:
undefined8 check_password(char *param_1)
{
int i;
i = 0;
while( true ) {
if (15 < i) {
return 1;
}
if (param_1[i] != *(char *)((long)&password + (long)i)) break;
i = i + 1;
}
return 0;
}
This function verifies each character in the user-provided param_1 against the corresponding character in the generated password, proceeding until a mismatch is found or the password is fully matched.
If all characters match up to the length of 16 (index 15), it returns a successful match, indicating the password is correct.
Dynamic Analysis
Unlike keyg3nme challenge that did not require dynamic analysis, I had to run the binary with gdb (but I use GEF).
That is because I need to figure out the generated concatenated password.
I also wanted to figure out the values of key1 through key9 despite only 3 keys were used.
Simply fire up the binary with gdb.
Run the binary to allocate memory for the process. This makes sure that we can set a breakpoint on the specific memory address.
Next, we want to put a breakpoint in the check comparison of the password.
Specifically, at the instruction 38 c2.
1221: 38 c2 cmp dl,al
Once that’s done, simply rerun the program and print the values of key1 through key9 and pass.
Finally, we have the password!
Of course it’s isAAthisFunBBCCD.
Let’s try it!
Consider this challenge solved!
Conclusion
While it was interesting and tiring to figure out the process of decrypting the algorithm for generating the password, it was not worth of my time at all. Simply put, reading the values of the password from the memory was easier.
It would be interesting to write a tool that automatically reads the values of variables and dump them on execution.
I thought ltrace would work for that but it did not show the entire string characters.