A Fuzzing Framework for ESP32 applications
Does not work on WSL! Needs all prerequisites for honggfuzz, qemu and esp-idf!
-
Build Honggfuzz (make)
-
Configure QEMU
./configure --target-list=xtensa-softmmu
--enable-debug
--disable-strip --disable-user
--disable-capstone --disable-vnc
--disable-sdl --disable-gtk
--honggfuzz-path="$(pwd)/../honggfuzz/"
- Build QEMU (make)
-
For building example applications install ESP-IDF according to https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/linux-setup.html
-
Run idf.py menuconfig to configure app. Connection must be set to OpenCores Ethernet for networking within QEMU. Stack Protection and Heap Protection should be turned on and RTOS should be set to run on one core only for successful fuzzing.
-
Run idf.py build to build app
-
Run ./make-flash-img.sh tcp_server tcp_server.img to create flash image for QEMU
./honggfuzz/honggfuzz -f ./example_esp32_server/in --save_all --thread 1 --max_file_size 127 --
./qemu/xtensa-softmmu/qemu-system-xtensa
-nographic -machine esp32
-drive file=./example_esp32_server/tcp_server.img,if=mtd,format=raw
-global driver=timer.esp32.timg,property=wdt_disable,value=true
-nic user,model=open_eth,hostfwd=tcp::8081-:80
The coverage data can be very noisy, because the coverage of the whole system is respected. To isolate the coverage of specific functions, the first have to be roughly located within the address space for example by executing:
readelf ./example_esp32_server/build/tcp_server.elf -s | grep processData
The address space, that should be considered can be limited by changing the variables hfuzz_qemu_start_code and hfuzz_qemu_start_code in the file ./qemu/hw/xtensa/esp32.c
For the example application, setting the considered address space to 0x400d5000 - 0x400d5f00 speeds up the fuzzing process increadibly. A fault should then be found within minutes.
First, a setup-point has to be found. This is an address, that is reached, when the initializing of all os functions is finished. For example, the beginning of the function app_main which can be found by executing readelf ./example_esp32_server/build/tcp_server.elf -s | grep app_main
Next an entry point and multiple exit points need to be defined. The binary of an esp32 firmware image can be examined with radare2. If the ELF file is available, this can be done by r2 -a xtensa tcp_server.elf
In this example, the entry point is set to 0x400d5278 because it is the first instruction of the function processData. As exit point, the address right after the call to the processData function within the function do_retransmit is used: 0x400d53f0
Now, the state of the device, when reaching the entry address has to be dumped. Therefore, the device is connected to the debugger gdb and a breakpoint is set to the entry point by executing br *0x400d5278
.
When the breakpoint has it, the output of the command info registers
is copied to a text file and the command dump binary memory dump.bin 0x3FF80000 0x3FFFFFFF
is executed to dump the whole RAM.
Finally, the registers which hold the pointer and the lenght of the input data must be examined. Mostly, the can be discovered by reading the register dump file. In this case, register a10 is the length register and the value of register a11, 0x3ffbc43c, is the pointer to the input data.
Now, the fuzzing can be started by the command
./honggfuzz/honggfuzz -f ./example_esp32_server/in --save_all --max_file_size 127 --
./qemu/xtensa-softmmu/qemu-system-xtensa
-nographic -machine esp32
-drive file=./example_esp32_server/tcp_server.img,if=mtd,format=raw
-global driver=timer.esp32.timg,property=wdt_disable,value=true
-fuzz setup=0x400d560c,entry=0x400d5278,exit=0x400d53f0,len=a10,data=0x3ffbc43c,dump_file=./example_esp32_server/dump.bin,regs_file=./example_esp32_server/regs_dump.txt
Multiple exit points can be defined by sepparating them with '+' For example exit=0x400d53f0+0x400d5890