Hey folks! As I am getting more and more involved with exploit development I am practising on various vulnerable by default software and one of them is VulnServer.
In a nutshell, VulnServer is a tiny server which exposes a set of commands with several bugs here and there ready to be exploited.
One of its vulnerabilities lies within the TRUN command logic and this is exactly what I will be targeting in this blog post.
As this is a Windows x86 software, the environment I will be working on for this exploitation is Windows 10 with IDA Pro, WinDbg debugger and Python 2 for building the exploit.
I will try to explain as I go so feel free to follow along.
Although I have the full source code of the server, I will be reversing the logic of the TRUN command because is more fun! (And we learn more..right?) The only assumption that I am having here is that there is a vulnerability in the TRUN command. The type and the location of the vulnerability will be unknowns to be found!
Opening up vulnserver.exe in IDA I can quickly trace through the creation of sockets and connection handling. Yet, all these are irrelevant for now so I skim through the strings of the binary to find “TRUN”
If following along, press ENTER and then Ctrl+X to display all the references to the string.
Now press ENTER again to jump that one and only reference in the code.
From the disassembled code, it looks like the program checks a buffer (probably the input) if it is equal to “TRUN” by calling _strncmp. Conveniently, IDA does some smart parameter naming by itself for the _strncmp call.
But let’s put a breakpoint at the start of the code block by pressing F2 and debug through the code to examine what is actually doing with test values. Make sure you have WinDbg installed and selected as the debugger in IDA and click Run.
The server is launched and now we need something to send data to the socket. Since Windows OS does not have netcat installed by default, download it from here, unzip the zip(password: nc) and copy it to your System32 folder.
Use netcat to send a TRUN command with some test data to the server. (The default port is 9999)
Stepping over the first several lines using F8, we can see that the program indeed checks whether the first 5 characters of the input are equal to “TRUN ”. If they are, it allocates 3000 bytes to the heap and sets them to 0.
Then there is an interesting line where it checks whether the first character of the remaining input is 2E which if converted to ASCII is the ‘.’ character. While a “.” character is not found, the program loops through the buffer and exits if not found. Let’s debug again but now with an input that starts with a “.”.
Now the first check passes and the program copies out input to the heap spaced allocated before. By stepping over a bit further there is an interesting function being called which is named “_Function3”. Using F7 to step into the function we can see that the function copies the contents of the heap space into a buffer in the stack, BUT there are not any checks for buffer boundaries so, in theory, it should be possible to overwrite the buffer and thus the return address! (Note that the allocated space in the heap was 3000 bytes and the whole stack buffer in Function3 is 2024 (7E8h) bytes.)
Since we’ve come so far and reversed the program, let’s be even braver and execute the overflow without fuzzing and finding the offset but by calculating it. Given that the destination address passed to _strcpy is $EBP-7D8h (7D8 is 2008 bytes) we need 2012 (+4 bytes to fill the address that the destination currently points to) bytes to fill the stack up until the EBP and then, the next word (4 bytes) is the return address!
Let’s try that out! Use the following python code to send the payload to the server:
Back to IDA, it is right on point! The RIP has been overwritten with 0x42424242!
The next task now is to create some shellcode inject it in the stack as part of our payload and jump to it. However, we cannot directly hardcode a stack address in EIP as this would be different each time. Thus, a reliable gadget needs to be found that allows us to jump to the shellcode. A “jmp esp” instruction would be exactly what we need as it points directly right after the return address we overwritten.
IDA can be used to search for instructions in the same manner as it can be used to search for text. press Alt+T and type in “jmp *esp”. Unfortunately, vulnserver.exe does not have such an instruction but what about the library ( esfunc.dll) that comes with it? Indeed, esfunc.dll has a “jmp esp” instruction at 0x625011AF.
Finally, let’s craft a shellcode that pops a calculator (so classic!) to be used as a proof of concept. Since we will be using msfvenom for that, make sure you have the metasploit framework installed. Run the following command to get the shellcode.
msfvenom -p windows/exec CMD='calc.exe' exitfunc=thread -a x86 -f python -b '\x00'
Note the -b parameter which sets the bad characters. Considering that the input buffer is copied using strcpy and strncpy even before that, we don’t want our shellcode to contain any null bytes as they will terminate the copying midway.
Finally, with that, we have all the pieces needed to build our proof of concept exploit.
Although returning to “jmp esp” jumps straight on our shellcode, it won’t work, if you don’t include a large enough NOP sled. For example, an 8-byte NOP sled won’t work. This is because the shellcode needs some padding for the encoding and therefore you need to give it some space through NOPs.
It’s show time! Run the server and then the python exploit.
Boom! Our malicious calculator popped up! 😈
Till next time folks.