In the previous part, we explored fuzzing simple IoT binaries with AFL++. These binaries accepted input from a file and were easy to fuzz. In this post, we will focus on fuzzing binaries that communicate over the network using sockets. Fuzzing such binaries is different from fuzzing file-based I/O binaries. While Vanilla AFL and AFL++ do not support fuzzing networked binaries, there are some projects like AFLNet and AFLNW that have modified versions of AFL for this purpose. In this post, we will learn how to use plain AFL++ to fuzz network programs.
One of the candidates for fuzzing is the httpd binary located at /usr/sbin/httpd, which is the web server for the firmware. To run httpd, we need to use sudo because it needs to bind to port 80. It’s important to note that qemu is started from the www/ directory, where the web resources (HTML, CSS, JS files) are located. Although it may display a bind error, running netstat confirms that httpd is indeed listening on port 80. We can open http://127.0.0.1 to verify that the web interface is accessible. Additionally, the web interface can be accessed using curl. By using an intercepting proxy like Burp Suite, we can view the actual HTTP requests being sent. When trying to login to the dashboard with the credentials admin:123456, a POST request is generated.
In the image provided, the webserver is running on port 8080 instead of port 80, which is achieved by appending -p 8080 to the qemu command line. From this point onwards, the goal is to modify this base request in subtle ways using a fuzzer to crash the web server. The naive approach is to send actual requests over the network, but this method would be slow. The smarter and recommended approach is to make the webserver read the HTTP request data from a file. We will explore both methods.
Naive fuzzing using Radamsa involves using a test case generator called Radamsa. It reads in a file and modifies it in subtle ways. The output from the file is then sent to the running web server. To achieve this, a Python script called fuzz-radamsa.py is used. The script opens a base login request from a file, generates a modified request using Radamsa, creates a socket connection to the webserver, sends the fuzzed request, and checks for a response. If the response isn’t received within 1 second, the input is saved to a file in the “interesting” directory. This method of fuzzing is highly inefficient, slow, and error-prone.
AFL++ offers a better approach to fuzzing. However, since we don’t have the source code of httpd and cannot modify it, we need to resort to binary-level modifications. This involves patching the assembly instructions and using LD_PRELOAD tricks to override network functions in libc, allowing them to accept input from a file instead. The desockmulti project on GitHub can be used for this purpose. Before using desockmulti, we need to make some modifications. Currently, httpd forks to the background using the daemon function, which we don’t want during fuzzing. We need to override daemon so that it returns 0 without forking. This can be achieved using LD_PRELOAD or by patching the assembly instructions. Another change we need to make is to have httpd process only one request before quitting, unlike a typical web server. To close a socket, httpd calls the close function, and we need to modify the specific location at 0x231c0 to call exit(0) instead of close.
To patch the instructions, we can use Cutter, a GUI for radare2. By navigating to 0x231c0 in Cutter, we can access the relevant disassembly code and modify it accordingly. Additionally, the daemon call at 0x22CB4 should be patched by changing the instruction to eor r0, r0. Once these changes are made, they can be saved in Cutter. The resulting patched binary can be tested by running it and verifying that it doesn’t fork to the background and quits after processing a single request.
To use desockmulti, an ARM cross compiler is needed to compile the project. The armv7-eabihf-uclibc toolchain from bootlin is recommended for this purpose. Desockmulti also uses threads, so we need to add a dependency to the library libpthread.so.0 using patchelf. The compiled desockmulti.so file can be copied to the squashfs-root directory. To test if desockmulti is working, gdb-multiarch can be used to debug httpd. By setting a breakpoint on fprintf and attaching to port 5555, we can inspect the contents of the register r2 and ensure that desockmulti is functioning as expected.