Within this post I’ll be doing a write up of the Space Pirate: Entrypoint
challenge from the HackTheBox Cyber Apocalypse 2022 CTF competition
(14/05/2022). This write up will be written according to my thought process
whilst I was trying to complete the challenge.
Reconnaissance
First look
I started off by downloading the provided zip file from HackTheBox’s CTF
platform and unzipping it. Within the zip file was a binary called
‘sp_entrypoint’ along with a glibc folder providing runtime .so libraries to
allow the binary to run. Firstly, I decided to take a look at the binary file
using the tool ‘strings’ to view all the printable strings present in the file.
The output of strings with sp_entrypoint as the input file
The ‘0nlyTh30r1g1n4lCr3wM3mb3r5C4nP455’ string seemed interesting, so I tried
to run the program and enter it when prompted for a password!
A failed attempt to gain privileged access to the program
Damn, it seemed that it wouldn’t be that easy. I had to take a closer look at
the program.
Analysis
After opening up the binary with radare2, analysing it, seeking to the ‘main’
function and printing the disassembled function, I began looking through the
contents of the function. The following instructions in particular stood out to
me:
The ‘sym.open_door’ function reference looked interesting to me, so I decided
to trace the jump instruction that lead to it. It seemed the function would be
triggered if ‘[var_40h]’ variable was equal to the hex value ‘0xdead1337’. So
perhaps it was possible to manipulate the value of that variable to get access?
[var_40h]
I looked further up in the function, namely near the start and saw that the
‘[var_40h]’ variable was being set there.
Unfortunately, it was being set to a hardcoded value of 0xdeadbeef and there
doesn’t seem to be any way of modifying this variable myself, so this seemed
like a dead end.
sym.check_pass
So I instead started looking around in other areas of the function and noticed the following function call.
1
││││0x00000dafe895feffffcallsym.check_pass
Looking inside the ‘sym.check_pass’ function, the following instructions were of interest to me.
Within this function I could see it was configured to do as the name implies,
check the given input against a hardcoded password value. However, looking
closer, I realised there was something wrong with the logic in the program.
First, it was comparing my inputted string with a hardcoded string using
strncmp, which returns 0 in ’eax’ if the strings are the same. Then the ’test’
instruction is checking if ’eax’ holds a value of zero, as it performs a
bitwise AND and then sets the ‘ZF’ (zero flag) if the result is zero. This
works because the only possible input values which result in zero with a
bitwise AND is two zeroes. Then, instead of the expected ‘je’ (jump equal)
instruction, which would jump if the ZF is set (meaning the strings are the
same), there is a ‘jne’ (jump not equal) instruction. This means that it will
trigger if the strings are not equal, instead of being equal, which is likely
an error, as it’s the opposite of traditionally expected functionality. This
jump instruction went right to a function call:
; CALL XREF from sym.check_pass @ 0x55e85ea00cda
; CALL XREF from main @ 0x55e85ea00df5
┌82:sym.open_door();
│; var int64_t var_8h @ rbp-0x8
│0x55e85ea00b8355pushrbp│0x55e85ea00b844889e5movrbp,rsp│0x55e85ea00b874883ec10subrsp,0x10│0x55e85ea00b8b64488b042528.movrax,qwordfs:[0x28]│0x55e85ea00b94488945f8movqword[var_8h],rax│0x55e85ea00b9831c0xoreax,eax│0x55e85ea00b9a488d35170300.learsi,str.e_1_32m; 0x55e85ea00eb8
│0x55e85ea00ba1488d3d701900.leardi,str._n_s___Door_opened__you_can_proceed_with_the_passphrase:_; 0x55e85ea02518 ; "\n%s[+] Door opened, you can proceed with the passphrase: "
│0x55e85ea00ba8b800000000moveax,0│0x55e85ea00bade8eefcffffcallsym.imp.printf; int printf(const char *format)
│0x55e85ea00bb2488d3d991900.leardi,str.cat_flag; 0x55e85ea02552 ; "cat flag*"
│0x55e85ea00bb9e8d2fcffffcallsym.imp.system; int system(const char *string)
│0x55e85ea00bbe90nop│0x55e85ea00bbf488b45f8movrax,qword[var_8h]│0x55e85ea00bc3644833042528.xorrax,qwordfs:[0x28]│┌─<0x55e85ea00bcc7405je0x55e85ea00bd3││0x55e85ea00bcee8adfcffffcallsym.imp.__stack_chk_fail│└─>0x55e85ea00bd3c9leave└0x55e85ea00bd4c3ret
So it seems this function will provide us with the flag when called because of
the system call on line 16! And all I needed to do to trigger this was enter a
string not equal to the hardcoded one due to the inverted logic! Brilliant!
Exploitation
So now all I need to do is exploit the binary. I connected to the container
running on the CTF platform and set about exploiting it.
A successful attempt in exploiting the challenge
Bingo! All I had to enter was 2 to choose to enter a password and any string I
wanted after as the password to get access because of the inverted logic,
great!
Mitigations
Inverted password check
The behaviour of the ‘check_pass’ function should be changed to only call
‘open_door’ if the entered password is equal to the
“0nlyTh30r1g1n4lCr3wM3mb3r5C4nP455” string. This can be achieved by replacing
the ‘jne’ (jump not equal) instruction with a ‘je’ (jump equal) instruction
instead.