On a recent hunt for bugs, I came across a buffer overflow condition in TallSoft’s newly released Quick TFTP Server 2.2. I thought I’d write a short guide as to how the bug was identified and how the denial of service was constructed against the application. Unfortunately for us, the application was compiled with ‘/GS’ which makes exploitation significantly more difficult. In short, at the start of each function, the compiler inserts a special ‘cookie’ value after the allocated buffer space. When the function returns, if the cookie is intact and matches the expected value, all is good, if not, we can assume that the buffer has been overrun and the application crashes. We can’t predict the expected value and just include it as part of our exploit as it is generated dynamically at runtime. Exploits already exist for previous versions of this application, and it seems that instead of patching the bugs, the vendor has simply recompiled with /GS enabled. Anyway, on to the bug – the first step was to fuzz the application.
The RFC and protocol description for TFTP is pretty simple – it’s a UDP service that runs over port 69. This allows us to build a template for SPIKE fuzzer. In the template, we are specifying bytes that must remain consistent, and then ‘variable’ bytes.
This is our SPIKE template. Here, we specify two fixed binary bytes ‘\x00\x02’ followed by a fuzz point i.e. the filename. We then require another fixed null binary byte ‘\x00’, followed by another fuzz point. Lastly, we terminate the payload with a final ‘\x00’ byte. This should maintain the integrity of the protocol whilst we examine how the application will handle different types and length of input placed at the two fuzz points. The next step is running SPIKE against our application and seeing if we observe anything interesting
Here we use the ‘generic_send_udp’ tool within SPIKE, specifying the target IP, port, script and variable options and sizes. Upon hitting enter, SPIKE will begin throwing mutated data at the application in the format that we specified. It is sometimes easier to add sleep(1); or usleep(100000); to the bottom of the SPIKE template to slow it down slightly so we can better observe what input causes the application to misbehave. After running, we get an almost immediate crash.
Whilst running SPIKE, I also run Wireshark. This will allow me to see exactly what SPIKE was throwing at the application at the time that it crashes. The application has crashed which is exactly what we were looking for. Now, the application itself crashes some time between displaying the request for the highlighted packet, and displaying the request for the packet after containing a string of As. It could be that after displaying the highlighted packet, the data is used in a way that crashes the application. Alternatively, it could be that before displaying the next packet, the data is used in a way that crashes the application. We’ll assume that it’s the latter – the highlighted packet looks rather innocuous, and the packet after contains a far larger payload, much more likely to overflow a fixed buffer. Let’s inspect the actual TFTP portion of this packet in wireshark:
Our next care is to take this packet and reconstruct it in Python, so that we can reproduce the crash on demand and investigate further. In my experience, Python is the fastest and easiest method for getting a basic proof of concept up and running:
We’ll now take a look at what’s actually going on within a debugger when we send our malicious packet TFTP packet:
Unfortunately, this isn’t the news we wanted. What we can see here is that we have managed to trigger a stack overflow, however the attempt has been detected and the application killed. In this case, it looks like game over as far as remote code execution goes, although we get one star for the denial of service.