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 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 or to . Once is called, it stores the value returned into variable which is passed as an argument to .
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 .
- If car choice is '2' and race choice is '1', our access code is .
- If car choice is '1' and race choice is '2', our access code is .
- If car choice is '2' and race choice is also '2', our access code is .
Before returnig, it calls the function with the as an argument.
begin_race():
We have seen how calls the function with the passed as an argument! In the function , 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 , it simply asks you to enter the navogation commands. We can also see a buffer overflow while reading navigation commands as it reads bytes of input, whereas the size of navigation is only 32 bytes. Thus in order to satisfy the check, our token key should be:
- if our . (Since )
- if our . (Since )
- if our . (Since )
- if our . (Since )
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 . 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 shellcode. The arguments to the shellcode are (shown in terms of registers eax, ebx, ecx and edx):
- = syscall number i.e for the execve shellcode 32 bit.
- should contain a pointer to the string .
- and 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 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 in order to find the size. Thus our payload will look something like this:
