Classic Passwd

Platform : TryHackMe
Type : Challenge
Difficulty : ⭐⭐⭐☆☆

Table of contents

Basic analysis

First, I used file Challenge.Challenge to know what type of file it is :

┌──(attacker㉿AttackBox)-[~/Bureau/CTF/Classic_Passwd]
└─$ file Challenge.Challenge 
Challenge.Challenge: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b80ce38cb25d043128bc2c4e1e122c3d4fbba7f7, for GNU/Linux 3.2.0, not stripped

Reversing with Ghidra

Looking at the functions in the executable, we can see 3 interesting functions :

  • main <- The main function
  • gfl <- Seems to stand for « Get flag »
  • vuln <- Vulnerable function ?

First, let’s see the source code of the main function :

undefined8 main(void)

{
    vuln();
    gfl();
    return 0;
}

We see in the main function (this function is the entry point of the program) that there is 2 functions called. vuln() and then gfl(). I think gfl stands for ‘get flag’, and obviously, vuln stands for vulnerable. Let’s take a look at the vuln function :

void vuln(void)

{
  int iVar1;
  char local_2c8 [130];
  undefined8 local_246;
  undefined4 local_23e;
  undefined2 local_23a;
  char local_238 [512];
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined2 local_20;
  undefined local_1e;
  undefined8 local_15;
  undefined4 local_d;
  undefined local_9;
  
  local_15 = 0x207962206564614d;
  local_d = 0x6e6f6e34;
  local_9 = 0;
  local_38 = 0x2f2f3a7370747468;
  local_30 = 0x632e627568746967;
  local_28 = 0x69626f306e2f6d6f;
  local_20 = 0x3474;
  local_1e = 0;
  local_246 = 0x6435736a36424741;
  local_23e = 0x476b6439;
  local_23a = 0x37;
  printf("Insert your username: ");
  __isoc99_scanf(&DAT_0010201b,local_238);
  strcpy(local_2c8,local_238);
  iVar1 = strcmp(local_2c8,(char *)&local_246);
  if (iVar1 == 0) {
    puts("\nWelcome");
    return;
  }
  puts("\nAuthentication Error");
                    /* WARNING: Subroutine does not return */
  exit(0);
}

So what is happening in this function ? First, there are some variable declarations. We can skip this for the moment. Then, the program asks a username to the user using the scanf function. The program copies the variable local_238 (which contains the string the user entered before) to the variable local_2c8.

Then, it compares the variable local_2c8 to the variable local_246 (so we can assume that local_246 contains the right username). If they are equal, the program prints Welcome, else, it prints Authentification Error.

Resolving the challenge

First method

Knowing that local_246 contains the right username, we can look at its value and try to decode it. So local_246 is equal to 0x6435736a36424741. But remember, this is an ELF 64-bit **LSB** pie executable, which means that we have to start interpreting this value from the least significant bit. So let’s reverse it like so : 41 47 42 36 6a 73 35 64
Now let’s decode this to ASCII : AGB6js5d

If you try to use this as a username, it won’t work. Why ? Because it’s only a part of the username to find. Looking at the C code in ghidra don’t help. So let’s take a look at the assembly code :

We can see that there are 3 hexadecimal values here, the first one is the one we already decoded, and the two others seems to be the missing parts of the username. You may be asking « Why those three values ? ». To answer this question, we must know something about the strcmp function. This function will read strings till it encounters a null byte. But as you can see, those 3 values are not separeted by a null byte.

The first null byte strcmp will encounter when reading memory from the first string is right after the third string (0x37). So let’s try to decode the two values we are missing and see if it works :

  • 0x476b6439 -> 9dkG
  • 0x37 -> 7

So if we take the 3 values we found and put them together, we have AGB6js5d9dkG7. Let’s use it in the program to get the flag :

attacker@AttackBox:~/Bureau/CTF/Classic_Passwd$ ./Challenge.Challenge 
Insert your username: AGB6js5d9dkG7

Welcome
THM{<REDACTED>}
attacker@AttackBox:~/Bureau/CTF/Classic_Passwd$

Second method

We can use ltrace to recover the value of local_246 variable (the variable that contains the right username). I used ltrace ./Challenge.Challenge. When the program asked for a username, I just entered a random string test. And I got the value of variable local_246 :

attacker@AttackBox:~/Bureau/CTF/Classic_Passwd$ ltrace ./Challenge.Challenge 
printf("Insert your username: ")                                         = 22
__isoc99_scanf(0x55b7e298d01b, 0x7ffda64a7a60, 0, 0Insert your username: test
)                     = 1
strcpy(0x7ffda64a79d0, "test")                                           = 0x7ffda64a79d0
strcmp("test", "AGB6js5d9dkG7")                                          = 51
puts("\nAuthentication Error"
Authentication Error
)                                           = 22
exit(0 <no return ...>
+++ exited (status 0) +++

Now we have the right username which is AGB6js5d9dkG7. To get the flag, we just have to execute the binary like this : ./Challenge.Challenge. When the executable asks for a username, we just need to enter AGB6js5d9dkG7, and we have the flag !

attacker@AttackBox:~/Bureau/CTF/Classic_Passwd$ ./Challenge.Challenge 
Insert your username: AGB6js5d9dkG7

Welcome
THM{<REDACTED>}

Third method

There is a third method to get the flag, but this time without finding the right username. Let’s take a look at the gfl function :

void gfl(void)

{
    int local_10;
    int local_c;
    
    local_c = 0x52c8d5;
    do {
    if (0x77d088 < local_c) {
        return;
    }
    if (local_c == 0x638a78) {
        for (local_10 = 0x1474; local_10 < 9999; local_10 = local_10 + 1) {
        if (local_10 == 0x2130) {
            printf("THM{%d%d}",0x638a78,0x2130);
                    /* WARNING: Subroutine does not return */
            exit(0);
        }
        }
    }
    local_c = local_c + 1;
    } while( true );
}

Let’s resume what happens in this function :

  • There is some variables declarations of course. After that, there is a while loop.
  • In this while loop, if the variable local_c is greater than 0x77d088, we exit the gfl function and it returns nothing.
  • Then, if the local_c variable is equal to 0x638a78, we enter in a for loop.
  • At the beginning of the for loop, we give the local_10 variable an initial value of 0x1474, and since it is lesser than 9999, we increase this variable by 1 and we continue to go throughout the for loop.
  • In the for loop, we check if local_10 is equal to 0x2130, if true, we print something in the console.

Let’s take a closer look at this print. It looks like it is the flag we are looking for. The two hexadecimal values passed as arguments in the function are interpreted as integer values (because of the %d in the print), so let’s try to convert them to integer values :

  • The first one (0x638a78) is equal to 6523512
  • The second (0x2130) is equal to 8496

So we have the flag : THM{<REDACTED>}.

Tools used

ToolPurpose
fileAnalyse the binary headers
ltraceDisplay calls to shared libraries
Ghidra– Analyse the binary assembly code
– Rebuild the C code of the binary
RapidTablesConvert hexadecimal values to string or integers

Sources

Retour en haut