[EN] A-Z: Introducing SocketInjectingFuzzer and its first target - Teeworlds
The idea behind Socket Injecting Fuzzer is simple - fuzz applications working in the client-server architecture by mutating real data packets that are exchanged by them in real usage scenarios. Implementation is also simple because it is based on hooking network-related functions on one side (or both) of a connection and modifying outgoing data. Thanks to this approach, it is not required (but still may be helpful) to understand communication protocol or to prepare initial input for targets.
As my first target, I decided to take a real-time multiplayer action game Teeworlds. The only thing that I had to know is to what network functions are used by the game. It was only a matter of running
strace to find out that
recvfrom are used. With this information, it was enough to start implementing hooks.
In fact, at this stage, this code just hooks invocations of
sendto and passes all arguments to the real
sendto. It doesn’t perform any fuzzing yet, but it’s is important to know how it will be launched. To do it I use
LD_PRELOAD trick that relies on compiling the fuzzer as a shared object and running it along with the application.
If I run it this way, all outgoing data will go through the fuzzer.
As the whole mechanism is known, it is time to actually modify data. I decided to use radamsa, a fuzzer that we used in our previous sessions. Primarily, radamsa is an external library that takes any data, mutates it, and returns, but it can be also compiled as a library.
Its API is really simple, it should be initialized and later invoked with input and output buffers.
Now all data sent by the application will be mutated. Now you may ask why
len is returned instead of result from
sf_sendto. Radamsa’s output can be different in length than input (
len != n). The application probably will check if
len bytes were sent, otherwise, some error handling code may be triggered. So it is necessary to fool the application and make it believe that it sent the expected number of bytes.
I also implemented some additional features which may be really helpful, for example fuzzing and sending a single packet many times (repeating), not fuzzing some packets to deliver them in their original state (to avoid disconnecting if the second side complains about malformed data), logging received data for easier analysis of crashes. For the sake of simplicity, I’ll skip describing it, but if you are interested in how it’s done, I recommended looking directly into the repository.
Time for action
As the fuzzer is ready to go, now we can run it with the game compiled with Address Sanitizer and keep our fingers crossed.
In this scenario the client sends mutated data and the server is a victim. On the gif we can see that fuzzer works. I didn’t touch a keyboard, all of these movements were accidentally made by the fuzzer while mutating data.
If we wanted to fuzz from the server-side we should just move
LD_PRELOAD from the client running the command to the server running command. Or if we wanted to simultaneously fuzz from both sides, we should apply this variable to both commands.
After some hours of fuzzing in different configurations (also applying different repeating and skipping parameters), I found five unique crashes. All of them apply to the client’s code so in real scenario a player would connect to a malicious server. Fortunately, none of them seems to be exploitable in terms of code execution but it’s enough for the server to cause clients’ crashes.
All of the bugs were reported on the project’s GitHub. It’s impressive how quickly developers reacted. Most of the fixes were committed in just a few hours.
A simple idea for fuzzing resulted in a few bugs detected in the very first target. These results proved to me that even a naive approach in fuzzing can give some results. So stay tuned for more.