Classic Password THM room
Table of Contents
Classic Password
In this challenge we are supplied an elf file and the goal is to find the flag, there are multiple ways to retrieve the flag for this challenge. A simple execution of strings on the file is not enough, however the easiest way seems to be to use ltrace. In this short post, I will use ltrace and afterwards also use the disassembler to solve the challenge.
Ltrace route
ltrace ./Challenge.Challenge
# Enter a bogus username to see the `strcmp` instruction and see the correct answer.
# Get the flag by supplying the correct username on second run
./Challenge.Challenge
Example ltrace run:
ltrace ./Challenge.Challenge
printf("Insert your username: ") = 22
__isoc99_scanf(0x7f617941601b, 0x7fffd2c39fb0, 0, 0Insert your username: asldfk
) = 1
strcpy(0x7fffd2c39f20, "asldfk") = 0x7fffd2c39f20
strcmp("asldfk", CORRECT USERNAME HERE) = 32
puts("\nAuthentication Error"
Authentication Error
) = 22
exit(0 <no return ...>
Disassembly route
Looking at the file with a disassembler shows that two functions are called from the main (at 0x000012f6).
mov eax, 0x0
call vuln
mov eax, 0x0
call gfl
mov eax, 0x0
In this binary the vuln function is responsible for checking that the correct username was supplied, while the gfl funtion outputs the flag. Therefore, we can either analyse the vuln function to find the password or we might be able the directly get the flag via the gfl function.
Disassembly view of vuln function
[...]
000011dd 48b8414742366a73 mov rax, 0x6435736a36424741
000011e7 488985c2fdffff mov qword ptr [rbp-0x23e {stored_username[0].q}], rax {0x6435736a36424741}
000011ee c785cafdffff3964 mov dword ptr [rbp-0x236 {stored_username[8].d}], 0x476b6439
000011f8 66c785cefdffff37 mov word ptr [rbp-0x232 {stored_username[0xc].w}], 0x37
00001201 488d3dfc0d0000 lea rdi, qword ptr [ask_username] {"Insert your username: "}
00001208 b800000000 mov eax, 0x0
0000120d e83efeffff call printf
00001212 488d85d0fdffff lea rax, qword ptr [rbp-0x230 {supplied_username}]
00001219 4889c6 mov rsi, rax {supplied_username}
0000121c 488d3df80d0000 lea rdi, qword ptr [format] {"%s"}
00001223 b800000000 mov eax, 0x0
00001228 e843feffff call __isoc99_scanf
0000122d 488d95d0fdffff lea rdx, qword ptr [rbp-0x230 {supplied_username}]
00001234 488d8540fdffff lea rax, qword ptr [rbp-0x2c0 {supplied_username_copy}]
0000123b 4889d6 mov rsi, rdx {supplied_username}
0000123e 4889c7 mov rdi, rax {supplied_username_copy}
00001241 e8eafdffff call strcpy
00001246 488d95c2fdffff lea rdx, qword ptr [rbp-0x23e {stored_username}]
0000124d 488d8540fdffff lea rax, qword ptr [rbp-0x2c0 {supplied_username_copy}]
00001254 4889d6 mov rsi, rdx {stored_username}
00001257 4889c7 mov rdi, rax {supplied_username_copy}
0000125a e801feffff call strcmp
0000125f 85c0 test eax, eax
As can be seen from the disassembly, the expected username is first stored on the stack and afterwards the user is asked to supply their username, which is than compared to the stored value. If those values are the same the program continues and later on prints the flag, otherwise the program exits. The username is not used in the decoding of the flag.
pseudocode of username check / vuln function at 0x00001185:
stored_username = HEX_value_of_username;
printf("Insert your username: ");
string supplied_username;
__isoc99_scanf("%s", &supplied_username);
string supplied_username_copy;
strcpy(&supplied_username_copy, &supplied_username);
if (strcmp(&supplied_username_copy, &stored_username) != 0) {
puts("\nAuthentication Error");
exit(0);
}
return puts("\nWelcome");
If the username was correct, the gfl function is called to print the flag.
pseudocode of flag / gfl function at 0x00001289:
for (int outer_index = 0x52c8d5; outer_index <= 0x77d088; outer_index++ ) {
if (outer_index == NUM1) {
for (int inner_index = 0x1474; inner_index <= 0x270e; inner_index++ ) {
if (inner_index == NUM2) {
printf("THM{%d%d}", outer_index, inner_index);
exit(0);
}
}
}
}
So basically this loop runs until the outer and inner loop both reached a specific value and than it prints those two values inside THM{ValuesHere}.
Alternative use of debugger or patching
As the gfl function directly outputs the flag, it is possible to attach a debugger and jump directly to the gfl function to get the flag, or the main function can be patched by replacing the call to vuln with nop instructions.
New main at 0x000012f6:
mov eax, 0x0
nop
nop
nop
nop
nop
mov eax, 0x0
call gfl
mov eax, 0x0
running the patched binary directly displays the flag (output redacted by replacing the actual value with NUM1NUM2)
./ChallengeNop
THM{NUM1NUM2}