Author: d1g174l_f0r7r355
This was one of the medium challenges I made for inctfj qualifiers. It is based on ret2shellcode.
Preliminary checks:
It is a 32 bit, dynamically linked, non-stripped binary.
For this challenge all protections are disabled and making it easier for us to pwn. However one thing that piques our interest is NX
bit being disabled. As a result, we can inject a shellcode and obtain a shell. No source code was given for this challenge, therefore we will make use of ida/ ghidra to decompile.
Ghidra decompilation and analysis:
main():
In main, we see that it provides us with two choices, to Buy car
or to Race car
. Once buy_car
is called, it stores the value returned into variable car
which is passed as an argument to race_car
.
buy_car():
In this function, we are again provided with two choices as to which car we need to select! Accordingly a leak is provided. Since all protections are disabled and in order to inject shellcode, we may find the leak useful.
race_car():
In this function, firstly we are again given a choice as to what race we want to select. However if no car has been selected, it will simply ask you to select a car first and then exits.
- If car choice is '1' and race choice is also '1', our access code is
0xdeadbeef
. - If car choice is '2' and race choice is '1', our access code is
0xd0d0face
. - If car choice is '1' and race choice is '2', our access code is
0xc0cac0de
. - If car choice is '2' and race choice is also '2', our access code is
0xcafebabe
.
Before returnig, it calls the function begin_race()
with the access_code
as an argument.
begin_race():
We have seen how race_car()
calls the function begin_race()
with the access_code
passed as an argument! In the function begin_race()
, you are asked to enter your token key. Thereafter, your token key is xored with param_1 (i.e your access code), and if the result is equal to 0x1337c0de
, it simply asks you to enter the navogation commands. We can also see a buffer overflow while reading navigation commands as it reads 0x50
bytes of input, whereas the size of navigation is only 32 bytes.
Thus in order to satisfy the check, our token key should be:
3449454129
if ouraccess code = 0xdeadbeef
. (Since0xdeadbeef ^ 0x1337c0de = 3449454129
)3286710800
if ouraccess code = 0xd0d0face
. (Since0xd0d0face ^ 0x1337c0de = 3286710800
)3556573184
if ouraccess code = 0xc0cac0de
. (Since0xc0cac0de ^ 0x1337c0de = 3556573184
)3653859936
if ouraccess code = 0xcafebabe
. (Since0xcafebabe ^ 0x1337c0de = 3653859936
)
Once we pass the check, we can simply inject shellcode with the help of the given leak.
Exploitation:
Now that we have understood the binary, let's begin with the exploitation part! In my exploit I have chosen car '1' and race '2'. Thus my access code is 0xc0cac0de
. You can choose any other car or race, however your token key will be different for each case, depending on your access key.
The shellcode we wish to inject can either be found online here or one may choose to write the shellcode! For the sake of writeup, below I have explained how you can write your own shellcode!
Shellcode:
While writing the shellcode, it is necessary to note which syscall we are going to call. In this case I will be making use of an execve
shellcode. The arguments to the execve
shellcode are (shown in terms of registers eax, ebx, ecx and edx):
eax
= syscall number i.e0xb
for the execve shellcode 32 bit.ebx
should contain a pointer to the string/bin/sh
.ecx
andedx
must be nulled out.
Thus our shellcode may look something like this:
Another factor we haven't looked into is the leak. Once we place our shellcode on the stack, while overflowing, we might have to give the location where our shellcode is present, so that the program can jump to that location and begin executing the shellcode. The leak provided is a stack leak. And the stack size is 0x30
bytes.
We can find our shellcode in memory like this.
Thus we note that our shellcode is present at location $ebp-0x2c. With the given leak we can find that eip is located 0x13 bytes below the leaked address.
Since eip is 4 bytes above ebp, while giving the buffer we will have subtract the length of our shellcode from 0x30
in order to find the size.
Thus our payload will look something like this: