
Exploit de prueba de concepto para CVE-2020-12124 dirigido al router Wavlink AC1200, que demuestra inyección de comandos no autenticada y desbordamiento de búfer en la pila en interfaces CGI.
originalmente en https://www.klogixsecurity.com/scorpion-labs-blog/anatomy-of-an-iot-exploit-from-hands-on-to-rce
por David E. Baker, publicado el 1 de junio de 2023
Este estudio trata sobre el firmware del router Gigabit inalámbrico Wavlink AC1200 de junio de 2020. Las vulnerabilidades aquí discutidas pueden o no haber sido parcheadas por el fabricante, pero este es un caso de investigación de vulnerabilidades que mostrará el viaje como recompensa más que su conclusión. El autor realizó esta investigación antes de la divulgación pública de las vulnerabilidades, pero después de que otros investigadores las descubrieran de forma independiente y las reportaran al fabricante.
El fabricante pone a disposición el firmware de sus productos en la sección de soporte de su sitio web; esta es una forma común de obtener firmware de IoT y una alternativa útil a extraerlo de la memoria del dispositivo. El firmware no está cifrado, por lo que se puede extraer fácilmente con binwalk. El análisis dinámico se realizó con acceso a un ejemplar físico del dispositivo, y el análisis estático se realizó a través de Ghidra.
La interfaz web del router Gigabit inalámbrico Wavlink AC1200 tiene varios endpoints vulnerables que permiten la copia sin restricciones de datos proporcionados por el usuario en la pila de la aplicación o incluso directamente en la línea de comandos para lograr la ejecución arbitraria de comandos.
Los escaneos iniciales del dispositivo sugieren que el único recurso expuesto era la consola web administrativa, accesible para usuarios autenticados en la interfaz LAN a través de HTTP en el puerto TCP 80. El dispositivo puede ofrecer más servicios, pero estos no están habilitados de fábrica y por defecto. Por lo tanto, esta investigación se centra únicamente en la interfaz web.
Un escaneo nmap del dispositivo ejemplar, mostrando solo la interfaz web escuchando.
Las pruebas habituales — como las típicas inyecciones de comandos que se encuentran en los paneles de diagnóstico de dispositivos que permiten una inyección de comandos en los parámetros de un comando ping o traceroute — no arrojaron resultados inmediatamente interesantes, lo que fue decepcionante.
Opciones de gestión disponibles después de autenticarse en el panel web administrativo. "USB Storage" se puede ver como la segunda opción.
La primera interfaz (finalmente explotable) que se examinó aquí se encontró en el panel "USB storage", que se puede ver como la segunda opción en la captura de pantalla anterior. El dispositivo tiene un puerto USB junto a sus conectores Ethernet 802.2, lo que sugiere que podría ofrecer funcionalidad de almacenamiento conectado a la red (NAS).
Una fotografía de la parte trasera del ejemplar real, mostrando la disponibilidad de USB.
Un principio simple en la investigación de vulnerabilidades es que cuantos más componentes interactúe un fragmento de código y más partes móviles tenga, más probable será que haya código explotable cerca. La presencia de capacidades NAS es prometedora porque indica la presencia de código que interactúa simultáneamente con la capa de software del dispositivo, la capa de hardware y la periferia conectada (el propio almacenamiento USB).
La interfaz de gestión de la consola de Almacenamiento USB se muestra a continuación. La presencia del campo "Workgroup" por sí sola es prometedora, ya que esto sugeriría que este router WiFi podría incluso intentar interactuar a través del Server Message Block (SMB) — una gran carga para un router IoT. No puedo contar las veces que he visto entrada proporcionada por el usuario enviada directamente a la línea de comandos como argumento a la función Unix smbpasswd.
Opciones de almacenamiento USB disponibles para usuarios autenticados.
Los intentos iniciales de manipular estos ajustes fallaron debido a que el dispositivo no detectaba una unidad USB, como se ve a continuación.
Los cambios de configuración en las opciones de Almacenamiento USB no se guardarán a menos que se conecte manualmente una unidad formateada adecuadamente al puerto USB del dispositivo.
Sin embargo, una vez que se conecta una unidad formateada correctamente, el dispositivo permitió establecer un nombre de usuario y contraseña FTP. Como se sospechaba, colocó esta entrada proporcionada por el usuario en la línea de comandos:
Una inyección de comandos en el campo 'password' otorga el primer acceso al shell directamente al sistema operativo del dispositivo.
Aunque interesante, esta vulnerabilidad es difícil de generar un entusiasmo abrumador: requiere no solo acceso con credenciales a la interfaz administrativa del dispositivo, sino también acceso físico al dispositivo para manipular su unidad USB. El exploit anterior permite a un investigador interactuar con los componentes individuales del sistema operativo (y exfiltrarlos con fines de ingeniería inversa).
El dispositivo era un sistema Linux centrado en busybox con una interfaz web impulsada por Lighttpd. La funcionalidad de Interfaz de Entrada Común (CGI) era proporcionada por binarios individuales en /etc\_ro/lighttpd/www/cgi-bin/, con solicitudes web a los URI de CGI que lanzaban estos binarios directamente. Una mirada rápida a nas.cgi en Ghidra muestra la inyección de comandos en la línea 38 a continuación, que envía una contraseña proporcionada por el usuario directamente a la función do\_system (que es simplemente un envoltorio alrededor de la llamada al sistema estándar de libc).
La entrada del usuario se coloca en la línea de comandos como argumento para el script chpasswd.sh en la Línea 38, resultando en una inyección de comandos y acceso al shell directamente al sistema operativo del dispositivo.
Mirando a través del directorio /cgi-bin/ se reduce la tarea de encontrar un exploit más interesante a enumerar las interfaces CGI disponibles para el usuario, mostradas a continuación:
Una lista exhaustiva de los binarios CGI puestos a disposición en el dispositivo, tomada en vivo desde el shell establecido por el exploit descrito en esta sección. La entrada del usuario se coloca en la línea de comandos como argumento para el script chpasswd.sh en la Línea 38, resultando en una inyección de comandos y acceso al shell directamente al sistema operativo del dispositivo.
Varias cosas destacan en el examen inicial. Lo primero importante a tener en cuenta es que los binarios CGI a menudo llaman a la función check_valid_user. Este método verifica si la dirección IP que realiza la solicitud está almacenada en un archivo temporal particular en el sistema de archivos. Pruebas mínimas muestran que el estado de autenticación de un cliente no se verifica hasta que se ha realizado una llamada a este método, por lo que la totalidad de la superficie de código en cada binario CGI antes de la invocación de esta función es accesible sin autenticación.
La descompilación de adm.cgi mostrando el método check\_valid\_user. Todo el código anterior a esta llamada se ejecuta antes de verificar el estado de autenticación del solicitante.
Otra observación interesante es la gran cantidad de métodos que copian la entrada proporcionada por el usuario directamente en la pila. Por ejemplo, la descompilación de wireless.cgi muestra el argumento del parámetro NewName tomado del cuerpo de una solicitud web en las Líneas 14 y 15, seguido de un strcpy sin protección de esta entrada del usuario en la pila en la Línea 34, como se muestra a continuación:
Las Líneas 14 y 34 demuestran un strcpy sin protección del parámetro NewName del cuerpo de la solicitud directamente en la pila del programa. La descompilación de adm.cgi mostrando el método check\_valid\_user. Todo el código anterior a esta llamada se ejecuta antes de verificar el estado de autenticación del cliente.
Esta copia de la entrada del usuario en la pila por sí sola sugiere un exploit de corrupción de memoria, que el siguiente comando puede verificar:
curl –XPOST --data "page=SetName&NewName=\`python3 –c 'print(\\"A\\"\*(512)'\`" http://target-ip/cgi-bin/wireless.cgi
Si bien es prometedor, un ataque de retorno orientado (ROP) no es óptimo aquí. Usando el acceso al shell del sistema operativo ya obtenido, el siguiente comando muestra '1', lo que indica que la Aleatorización del Diseño del Espacio de Direcciones (ASLR) se aplica débilmente, dejando una cadena ROP con una probabilidad de 1 entre 256 de aterrizar en el gadget deseado:
# cat /proc/sys/kernel/randomize\_va\_space
1
Además, el comando a continuación muestra que los binarios little-endian están compilados con el byte de protección de pila (stack-guard) configurado, por lo que solo el último gadget de una cadena puede aterrizar en una ubicación predeterminada dentro del binario. Si hay un proceso watchdog presente para reiniciar el servidor web después de un bloqueo, no es descabellado lanzar repetidamente un exploit de retorno orientado y esperar tener éxito eventualmente, pero también es posible que haya mejores errores acechando en otra parte del código.
xxd /etc\_ro/lighttpd/www/cgi-bin/wireless.cgi | head -n 10
Continuando la revisión de las funciones CGI, eventualmente se examinará live\_api.cgi. Este binario no realiza ninguna llamada a check\_valid\_user, por lo que cualquier solicitud web al URI /cgi-bin/live\_api.cgi ejecuta esta aplicación CGI sin autenticación. La Línea 9 en la Figura 11 muestra que la variable de entorno QUERY\_STRING, que (según la especificación CGI de Apache) es la parte del URI de la solicitud que sigue inmediatamente a un signo de interrogación y, por lo tanto, proporcionada por el usuario, se almacena en pcVar1 y en la Línea 19 de la Figura 11 se envía al método satellite_status.
Una descompilación de live\_api.cgi que muestra la entrada del usuario tomada del URI en la Línea 9 y siendo enviada a satellite\_status en la Línea 19. La descompilación de adm.cgi mostrando el método check\_valid\_user. Todo el código anterior a esta llamada se ejecuta antes de verificar el estado de autenticación del cliente.
La descompilación del método satellite_status, que se muestra en la Figura 12, muestra que la cadena de consulta en sí (ahora param\_1) se analiza en busca de los parámetros page, id e ip. El parámetro ip se copia mediante la función sprintf a una variable local en la Línea 38 de la Figura 12 y, en la Línea 39, se pasa a la función do\_system. La ausencia de una llamada a check\_user\_auth indica que la entrada arbitraria del cliente de un usuario no autenticado en el URI se colocará directamente en la línea de comandos en el parámetro ip del URI, confirmado a continuación:
Una descompilación de la función satellite\_status, que muestra la cadena de consulta (ahora param\_1) analizada en busca del parámetro ip en las Líneas 22 y 23, luego una llamada a do\_system en las líneas 38 y 39.
La prueba está en el pudín, mostrando el exploit siendo utilizado para efectuar una toma de control remota del dispositivo.
Encontrar exploits en un dispositivo IoT recién llegado al mercado puede parecer fruta madura, como se mencionó al comienzo de esta publicación, pero el valor de esta investigación estuvo en su viaje más que en su destino.
Una comprensión de la evasión de la autenticación y la ubicación de la inyección de comandos habría sido improbable, o incluso imposible, sin un análisis estático del firmware. Si no hubiera estado disponible en línea, habría requerido acceso dinámico al sistema operativo del dispositivo para obtenerlos, para lo cual habrían sido escasas las esperanzas de acceso dinámico al dispositivo. Este nivel de acceso en sí mismo dependía tanto del acceso físico al dispositivo como de hardware especializado o de la suposición guiada sobre los lugares probables en el código donde se podrían haber cometido errores.
Esperamos que hayas disfrutado este viaje, y que vuelvas por más. ¡Feliz hacking!
Autor:
David Baker, Consultor Senior de Seguridad, Pruebas, K logix