[Part 2][EN] Hacking NETGEAR JWNR2010v5 Router - Command Injection

[Part 1] Hacking NETGEAR JWNR2010v5 Router - Authentication Bypass
[Part 2] Hacking NETGEAR JWNR2010v5 Router - Command Injection


We already saw how to bypass the authentication so in this post I want to focus in the compromise of the underlying system. In order to do this we have to understend how the router works internally and the management of the administration interface.

 Checking the filesystem we got in the previous post I found that everything is handled by just one cgi called setup.cgi:


# ls -l | grep cgi
lrwxrwxrwx 1 root root     18 Jul 28 07:51 conf -> restore_config.cgi
lrwxrwxrwx 1 root root      9 Jul 28 07:51 htpwd_recovery.cgi -> setup.cgi
-rwxr-xr-x 1 root root  59784 Apr  1 01:34 restore_config.cgi
-rwxr-xr-x 1 root root 613588 Apr  1 01:34 setup.cgi
-rwxr-xr-x 1 root root 277152 Apr  1 01:34 setupwizard.cgi
lrwxrwxrwx 1 root root      9 Jul 28 07:51 upgrade_flash.cgi -> setup.cgi

One of the most common vulnerabilities we can see on SOHO routers are command injection through bad implemented functions, so my priority is to find where a command is executed to against the system and that allows me to control the input. For this I opened setup.cgi in IDA and search for keywords, in this case '/bin':




The ping command caught my attention as it meets both conditions. In this case the webpage that handles this is diag.htm which allows the user to perform different tests to validate the internet connectivity.



By opening diag.htm and reading the source code we find the first interesting thing. Input data is validated client side, so just changing return ping_ck() by return true or modifying the POST request we can send our crafted data.



As you can see above we tried to inject a command appending ';' to the IP address but the command never reaches the server. It's filtering it server side, so let's take a look at the cgi:



As you can see, it searches for a semicolon in the request and if exists the command is never executed...but..what happens here? It only filters the semicolon so if we replace it with '&&' we bypass the check and the command is successfuly executed...or it should...


If we run the command against the system we receive a 403 error but I noticed that setup.cgi gets a GET numeric parameter as follows...setup.cgi?id=0000000 So let's move again to IDA and search for 'id=' string:


First main() search for POST parameters and environment variables, if not found seek for GET parameters. In case it finds 'id' runs strtol() and if succeed extract the content of /tmp/SessionFile and compares against the id parameter where if doesn't match returns the 403 error.

 Finally we know that we need to sign the request with this parameter in order to run the command, so we use the auth bypass vulnerability from the previous post to access the web interface, scrap the source code to extract a valid sessionid before it changes, craft the injection and returns the response.

Here is a script to do this:


And finally we got the expected result in the response after running a 'pwd' command:


Unlike the previous vulnerability this only affects to two models JWNR2000v5 and JWNR2010v5.

In the following link you can find a PoC exploit:

 https://github.com/remot3/exploit-POC/blob/master/NETGEAR_JWNR2010v5.py

[Part 1] Hacking NETGEAR JWNR2010v5 Router - Authentication Bypass
[Part 2] Hacking NETGEAR JWNR2010v5 Router - Command Injection



It has been a long time since my last update so today I want to show you two vulnerabilities found while reversing the firmware from a NETGEAR router. The exploitation of these two vulnerabilities provides the attacker full remote unauthenticated root access to the device if it has WAN administration enabled.

First things first, the content you are about to read is intended as educative content, do not hack random thing out there, don't be stupid. Ok, let's start with this. In this case I analyzed the firmware 1.1.0.31 published on 05/11/2015 for the Netgear JWNR2010v5.

You can download the file here: http://www.downloads.netgear.com/files/GDC/JNR1010V2/ N150_N300_FW_V1.1.0.31_1.0.1.zip

From the binwalk output we know that the arch is MIPS and there is a SquashFS filesystem at 0x160000 which we extract using dd:



DECIMAL         HEX             DESCRIPTION
-------------------------------------------------------------------------------------------------------------------
60352           0xEBC0          U-Boot boot loader reference
[...]
131072          0x20000         uImage header, header size: 64 bytes, header CRC: 0x23F4018D, created: Thu Nov 21 21:18:53 2013, image size: 1266253 bytes, Data Address: 0x80000000, Entry Point: 0x8000C2F0, data CRC: 0xE8A06868, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
131136          0x20040         LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 4632092 bytes
1441792         0x160000        Squashfs filesystem, little endian, version 4.0, compression:lzma (non-standard type definition), size: 2376782 bytes,  1160 inodes, blocksize: 131072 bytes, created: Thu Nov 21 21:18:35 2013
[...]
4028341         0x3D77B5        End of Zip archive


# dd if=N300.bin bs=1 of=N300.squashfs skip=1441792
2752512+0 records in
2752512+0 records out
2752512 bytes (2.8 MB) copied, 2.73591 s, 1.0 MB/s

# file N300.squashfs
N300.squashfs: Squashfs filesystem, little endian, version 4.0, 2376782 bytes, 1160 inodes, blocksize: 131072 bytes, created: Thu Nov 21 21:18:35 2013

# ./unsquashfs_all.sh N300.squashfs
[...]
created 850 files
created 60 directories
created 144 symlinks
created 106 devices
created 0 fifos
File system sucessfully extracted!


I use to run a script that test every file in the webserver and returns the error code, so I did, and from this preliminary file access analysis I found that an unauthenticated user can access every static file located in the webroot which is interesting but not very useful, anyway reviewing the results of this test I found the tip of the iceberg. Every time I ran the script and reached one file the following files were available without authentication (Error 501 == 200):



501http://192.168.1.1:8080/401_access_denied.htm
501http://192.168.1.1:8080/401_recovery.htm
401http://192.168.1.1:8080/Add_WPS_Client.htm
200http://192.168.1.1:8080/advanced.js
401http://192.168.1.1:8080/adv_index.htm
[...]
401http://192.168.1.1:8080/BAS_basictop.htm
200http://192.168.1.1:8080/bas_bpa.js
200http://192.168.1.1:8080/base.gif
401http://192.168.1.1:8080/BAS_ether_h.htm
401http://192.168.1.1:8080/BAS_ether.htm
[...]
401http://192.168.1.1:8080/BRS_05_networkIssue.html
401http://192.168.1.1:8080/BRS_check_manulConfig.html
401http://192.168.1.1:8080/BRS_hijack_index.htm
401http://192.168.1.1:8080/BRS_hijack_success.htm
401http://192.168.1.1:8080/BRS_index.htm
501http://192.168.1.1:8080/BRS_netgear_success.html [From this point every 401 error becomes a 501]
501http://192.168.1.1:8080/BRS_plzWait.html
501http://192.168.1.1:8080/BRS_RUS_auto_account_input.html
501http://192.168.1.1:8080/BRS_RUS_auto_attention.html
[...]

And even worse...when I ran the script again every file returned a 501 error, so by doing this I disabled the authentication for the router somehow and with this we reached the "IDA Time!". By analyzing the router behaviour we can see that the authentication is handled by the webserver binary, so we import it to IDA and search for our reference string ( BRS_netgear_success.html ) which returns an intermediate function between 'main' and other function that we called 'auth_req_check' because of a .htpasswd reference.




This intermediate function will be called 'url_handle' as we can see a clasic staircase pattern comparing the reveived URL with hardcoded strings using strstr() function and executing different actions. Now we identified and isolated the authentication and url handler functions, lets move to our suspicious string. As you can see in the following image if the url contains the string ( BRS_netgear_success.html ) it moves to an interesting piece of code which sets two variables in the nvram to 0 (need_not_login and start_in_blankstate)




With this in mind it's easy to understand what is this doing, wen we access BRS_netgear_success.html the router redirects to NETGEAR webpage just to check whether we have internet access or not, and allows the user to access the router without credentials just for change the administrative password...but...this should be only allowed the first time the router is connected which is not the case... Finally lets confirm our assumptions checking where the bypass is done. For this, we have to move to the 'auth_req_check' function that we named previously ( offset 00403858 if you want to check) and read from the beginning where the function checks the existence of the .httpasswd file. If yes, we find a reference to start_in_blankstate where it's compared to be different to 1 and if yes the login is bypassed:



With the we confirmed that if start_in_blanckstate is 0 the .htpasswd is disabled and we have free access to the admin panel of the router.

So in short, if we hit this URL http://[IP_ROUTER]:[PORT]/BRS_netgear_success.html the login for every page in the router is temporary disabled.

 I could confirm that the following models are vulnerable to this: NETGEAR_JNR1010v2, NETGEAR_JNR3000, NETGEAR_JWNR2000v5, NETGEAR_JWNR2010v5, NETGEAR_N300, NETGEAR_R3250, NETGEAR_WNR2020, NETGEAR_WNR614, NETGEAR_WNR618

 But this is only the beginning and we want to get unauthenticated root access to the router.

 In the following post I explain a vulnerability that affects just these two models (JWNR2010v5 and JWNR2000v5) that allows us to fully compromise the router and also you can find a PoC to test with your own router.

[Parte 2] Hacking NETGEAR JWNR2010v5 Router - Command Injection

Ya hemos visto como podemos acceder al router bypasseando la autenticación por completo por lo que en esta entrada quiero centrarme en el compromiso del sistema subyacente. Para esto tenemos que entender como funciona el router internamente y la gestión del interfaz de administración.

Revisando el sistema de archivos que descomprimimos en el anterior post nos encontramos con que casi toda la lógica es gestionada por un unico cgi llamado setup.cgi:


# ls -l | grep cgi
lrwxrwxrwx 1 root root     18 Jul 28 07:51 conf -> restore_config.cgi
lrwxrwxrwx 1 root root      9 Jul 28 07:51 htpwd_recovery.cgi -> setup.cgi
-rwxr-xr-x 1 root root  59784 Apr  1 01:34 restore_config.cgi
-rwxr-xr-x 1 root root 613588 Apr  1 01:34 setup.cgi
-rwxr-xr-x 1 root root 277152 Apr  1 01:34 setupwizard.cgi
lrwxrwxrwx 1 root root      9 Jul 28 07:51 upgrade_flash.cgi -> setup.cgi


Una de las vulnerabilidades mas habituales es la inyección de comandos a traves de funciones mal controladas, por lo que mi prioridad es encontrar en que punto se ejecuta un comando contra el sistema donde nosotros tengamos control sobre el input. Para ello una vez abierto el cgi en IDA hago una búsqueda sobre palabras clave que pueden reportar el resultado deseado, por ejemplo el string '/bin' entre otros:


Como se puede ver en la imagen uno de los resultados mas interesantes es el comando ping que muchos routers utilizan en la página de diagnóstico para validar la conexión a internet y que en el caso concreto de este NETGEAR se encuentra en la página diag.htm, por lo que buscaremos como se realiza la llamada:



En la propia web nos encontramos el primero de los fallos. La integridad de los datos enviados por el usuario se realiza del lado del cliente por lo que simplemente sustituyendo return ping_ck() por return true o interceptando la petición POST podremos modificar los datos a nuestro antojo.


Como ya he dicho en mas de una ocasion uno de los aspectos en los que mas me gusta gastar mi tiempo es en la investigación de sistemas embebidos. Así que como ya hace mas de un año que no actualizo el blog, hoy os traigo el analisis de dos vulnerabilidades que afectan a un gran numero de dispositivos NETGEAR. La explotación de estas dos vulnerabilidades en conjunto llevan al compromiso total del router de manera remota si tienen el acceso WAN activado.

En este caso el firmware a analizar es la versión 1.1.0.31 del modelo JWNR2010v5 publicado el 11/05/2015 y disponible para su descarga en el siguiente enlace http://www.downloads.netgear.com/files/GDC/JNR1010V2/N150_N300_FW_V1.1.0.31_1.0.1.zip

A partir de la lectura de binwalk sabemos que el router tiene una arquitectura MIPS y que el sistema de archivos se encuentra en el offset 0x160000 por lo que lo extraemos usando dd:



DECIMAL         HEX             DESCRIPTION
-------------------------------------------------------------------------------------------------------------------
60352           0xEBC0          U-Boot boot loader reference
[...]
131072          0x20000         uImage header, header size: 64 bytes, header CRC: 0x23F4018D, created: Thu Nov 21 21:18:53 2013, image size: 1266253 bytes, Data Address: 0x80000000, Entry Point: 0x8000C2F0, data CRC: 0xE8A06868, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
131136          0x20040         LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 4632092 bytes
1441792         0x160000        Squashfs filesystem, little endian, version 4.0, compression:lzma (non-standard type definition), size: 2376782 bytes,  1160 inodes, blocksize: 131072 bytes, created: Thu Nov 21 21:18:35 2013
[...]
4028341         0x3D77B5        End of Zip archive


# dd if=N300.bin bs=1 of=N300.squashfs skip=1441792
2752512+0 records in
2752512+0 records out
2752512 bytes (2.8 MB) copied, 2.73591 s, 1.0 MB/s

# file N300.squashfs
N300.squashfs: Squashfs filesystem, little endian, version 4.0, 2376782 bytes, 1160 inodes, blocksize: 131072 bytes, created: Thu Nov 21 21:18:35 2013

# ./unsquashfs_all.sh N300.squashfs
[...]
created 850 files
created 60 directories
created 144 symlinks
created 106 devices
created 0 fifos
File system sucessfully extracted!


Reviviendo GoPro con Hard-Brick aka GoPrisapapeles


Como muchos sabéis uno de mis intereses son los dispositivos embebidos, pese a esto uno de los dispositivos a los que nunca me hubiese gustado meterle mano era mi querida GoPro, hasta ayer...

Resulta que justo ayer (Martes 13 para mas inri) se me ocurrió actualizar el firmware de la cámara para conseguir alguna de las últimas mejoras que trae pero para mi desgracia la cámara nunca mas encendió, tras el pánico inicial empecé a hacer pruebas con ella probando las combinaciones de teclas para forzar un hard reset, con SD, sin ella...etc sin resultado hasta que al conectarla por USB y sin batería vi que el led frontal parpadeaba muy levemente, al ver que al menos daba alguna señal de vida y conectada a USB  probé a encenderla y si bien la cámara seguía muerta el ordenador me la reconoció como un "Dispositivo USB desconocido".

En este punto sabemos que al menos la cámara tiene algo de vida y que existe algún tipo de comunicación por USB pero que durante la actualización fallida la memoria se ha borrado por completo.

Tras una llamada al soporte técnico me confirman que esto es un error habitual y que me la reemplazan en garantía, pero claro...el verano está aquí y no quiero quedarme sin ella 2 meses, así que una rápida búsqueda en Google me devuelve la salvación:

https://github.com/evilwombat/gopro-usb-tools

Este set de herramientas nos proporciona diferentes maneras devolver la vida a nuestra cámara, la primera y mas sencilla es restaurar el bootloader, en mi caso no era válido porque todo el firmware había sido wipeado. La segunda (El que yo he usado) se encarga de recargar el Bootloader, HAL y RTOS, a grandes rasgos lo que hace es parchear el bootloader para que apunte a lo que nosotros cargamos en unas direcciones de memoria altas para evitar que cargue cualquier archivo corrupto de la actualización fallida. Por último este set de herramientas nos proporciona una shell interactiva (en 10.9.9.1) con la que podemos movernos dentro de la cámara y verificar la integridad del sistema y la memoria flash.

Bueno, ahora que ya os he presentado la herramienta vamos a ver los pasos para revivir la pequeña.

AVISO: Sigue estos pasos bajo tu responsabilidad, no me hago cargo de cualquier daño que le puedas causar a tu cámara. Si aún está en garantía no seas como yo y pide el reemplazo.

Distribución de malware móvil a través de anuncios insertados

A menudo cuando navegamos por internet cientos de anuncios se nos despliegan para ofrecernos productos que ninguno necesitamos. Hace un par de dias alguien se acercó a mi a preguntarme por que mientras visualizaba contenido a través de la aplicación "Flipboard" en Android se le abría sin él hacer nada una web que le ofrecía descargarse una aplicación pornográfica, en ese momento me picó la curiosidad pero no tenía ninguna muestra del tráfico para saber desde donde se estaba sirviendo esa página. Por casualidades de la vida hoy me tocó a mi ser la víctima a la que se le ofrecía esta aplicación mientras navegaba con mi lector de feeds "Feedly".

Réplica de GPlay servida para Android
Rápidamente preparé Wireshark y repliqué la petición para capturar el tráfico generado y tener un punto por el que empezar. El resultado fue satisfactorio y la página se mostró de nuevo por lo que empecé a analizar la captura en orden inverso, así leyendo la cabecera "Referer" de las peticiones sabía de donde venía el tráfico. No fue dificil descubrir al culpable:

Ahora sabemos que el anuncio insertado en pastebin.com (Página legítima a la que accedí en mi navegacion normal) redirecciona a la página réplica de google play desde donde se distribuye el malware, pero intentando acceder a través del navegador de escritorio la página que se muestra es totalmente diferente y solo despliega una imagen con un enlace a una página legítima.

Pagina mostrada al navegador de escritorio

La explicación es sencilla. El servidor que nos entrega el anuncio detecta la plataforma desde la que se hace la petición leyendo el User-Agent de la request así que si navegamos desde el ordenador se nos sirve un anuncio válido insertando el siguiente código:

Código entregado por la página a un navegador de escritorio
En cambio si engañamos a la página haciéndole creer que estamos navegando a través de un dispositivo Android, la página nos entrega el siguiente código que nos redirecciona a la web maliciosa:

Código entregado por la página utilizando un useragent de Android
Inmediatamente se nos abre la página réplica de Google Play que nos sirve el apk troyanizado y trata de iniciar una descarga, podéis comprobar como la url que abre es la misma a la que nos redirige el anuncio:


El comportamiento en un terminal Android es todavía peor ya que no te solicita permiso para la descarga (Al menos en Firefox) y se almacena el apk en el teléfono automáticamente.

Por último antes de analizar manualmente el comportamiento del APK lo pasamos por VirusTotal para comprobar si ya había sido analizado, dandonos el resultado del analisis:


Como ya ha sido analizado nos ahorramos el trabajo de reversear el APK, el troyano que contiene es detectado por varios antivirus como TrojanSMS.Erop., su comportamiento consiste en enviar SMS desde los terminales infectados a numeros premium.

Así que ya sabeis, cuando naveguéis desde un terminal móvil intentad utilizar siempre un bloqueador de anuncios, ademas si utilizas lectores de feeds como Feedly o Flipboard no tienes esta posibilidad por lo que espero que la lectura de este artículo te ayude a estar alerta ante estos engaños.

Premios Bitacoras 2013

Como cada año llegan los Premios Bitacoras, y en ShellShockLabs también participamos en ellos. Si te gustan nuestras publicaciones puedes votarnos para darnos un empujoncito a seguir mejorando y escribiendo contenido.

Puedes acceder a las votaciones haciendo click en la imagen:



¡Gracias!

Shell Shock Labs

ShellShock Labs es un blog dedicado a seguridad y hacking en general, un lugar donde buscamos que la gente participe con sus opiniones.

Síguenos




Posts Populares