Introducción
No cabe duda de que el fuzzing se ha convertido en una de las principales técnicas a aplicar a la hora de identificar vulnerabilidades y fallos en productos software. Esta técnica consiste en ejecutar un software un gran número de veces por segundo, aportando en cada ejecución unos datos de entrada que van siendo mutados progresivamente por herramientas conocidas como fuzzers. La idea principal es encontrar unos datos de entrada para los cuales el software no sea capaz de gestionarlos correctamente, dando lugar así a problemas como corrupción de memoria o cuelgues.
El fuzzing se ha popularizado en la última década gracias a su gran efectividad y facilidad de uso. Vulnerabilidades archiconocidas como Shellshock o Heartbleed las cuales afectaron a millones de dispositivos por todo el mundo, fueron identificadas mediante fuzzing.
Aunque esta técnica es ampliamente utilizada para poner a prueba software en plataformas más orientadas al propósito general, presenta una serie de retos cuando se trata de implementar en el desarrollo o investigación de software para sistemas empotrados e IoT. Pero, ¿por qué querríamos aplicar fuzzing al internet de las cosas? La respuesta es simple, estos dispositivos trabajan comunicándose entre sí y reciben una gran cantidad de información del exterior. Si dicha información no es validada adecuadamente pueden originarse vulnerabilidades en el software. El fuzzing nos permitirá automatizar ese proceso de generar datos de entrada posiblemente mal formados para poner a prueba un software.
En este artículo hablaremos sobre cómo aplicar fuzzing a software desarrollado para dispositivos empotrados e IoT a través de técnicas como la emulación y la instrumentación dinámica, con el objetivo de aprender una forma innovadora de evaluar aspectos de la seguridad de aparatos como routers, bombillas inteligentes, IoT de aplicación industrial, etc.
Fuzzing con emulación e instrumentación dinámica
Dado que este tipo de dispositivos suele caracterizarse por tener unos recursos muy limitados, querremos recurrir a métodos alternativos para evitar realizar el fuzzing sobre el hardware original. Una posible solución es emular en un ordenador de propósito general el firmware o los componentes software del sistema. Desgraciadamente, todo el que haya intentado alguna vez emular software diseñado para plataformas específicas sabe de los problemas de inestabilidad y compatibilidad comúnmente asociados a esto. Este caso no es una excepción, una gran mayoría del software IoT emulado con herramientas como QEMU no se ejecutará correctamente debido a la existencia de tareas con fuertes dependencias sobre otros procesos o sobre el hardware original como antenas o microprocesadores auxiliares. Para solucionar esto podríamos:
Caso práctico: Netgear R7000
Para demostrar el funcionamiento de Qiling y su uso junto a fuzzers, reproduciremos una vulnerabilidad descubierta por la firma de ciberseguridad GRIMM en abril de 2022. La vulnerabilidad se trata de un desbordamiento de pila producido en el proceso de actualización de firmware del router Netgear R7000. Durante este proceso se extraen una serie de parámetros de la cabecera del firmware como su tamaño o el modelo del dispositivo para el que está destinado el firmware. Dado que el parámetro que indica el tamaño de la cabecera no es validado antes de realizar operaciones de memoria (memcpy), un usuario podría proporcionar un paquete de actualización modificado con el que se escriba más allá de los límites del buffer de destino, alterando así el valor de los registros del procesador y consiguiendo ejecución remota de código.
Pongámonos en la situación de que deseamos evaluar el proceso de actualización de dicho router mediante fuzzing. Utilizar el hardware real del dispositivo para ir aplicando paquetes de actualización mutados puede ser lento y tedioso, por lo que recurrimos a la emulación. Para ello empezaremos por obtener el firmware del dispositivo en su versión 1.0.11.128 desde su portal oficial de soporte. En caso de no tener acceso al firmware se podría recurrir a crear un volcado desde el propio dispositivo a través de JTAG o con un programador EEPROM. Una vez tenemos el firmware, extraemos su sistema de archivos SquashFS utilizando Binwalk sobre la imagen del firmware (fichero con extensión .chk) para obtener acceso a los distintos binarios que contiene.

Ahora es necesario identificar qué binario y qué funciones de código gestionan la actualización de firmware mediante ingeniería inversa. Tras esto, descubrimos que nuestro binario de interés se trata del demonio UPNP del router (usr/bin/upnpd), el cual posee una función que recibe el paquete de actualización y se encarga de extraer los parámetros de la cabecera de este para realizar comprobaciones en base a ellos. Analizamos el binario con Ghidra para ver el código decompilado de la función y la llamada a memcpy insegura en el paso 3.






Conclusión
Aunque el fuzzing es una técnica efectiva ampliamente utilizada a día de hoy, no suele ser aplicada al mundo del IoT y los sistemas empotrados debido a los retos y dificultades que esto supone. Combinar los conocimientos sobre emulación, instrumentación dinámica y fuzzing que hemos tratado en este artículo nos permite ir un paso más allá a la hora de poner a prueba la seguridad de todo tipo de dispositivos inteligentes para identificar vulnerabilidades que de otra manera podrían pasar desapercibidas.
Todo el código utilizado puede ser consultado desde aquí.