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}