jueves, 18 de abril de 2013

El router de mi amigo (4 de 4)

La infraestructura

¡Hora de probar!

Me hice con un espacio web en cierto “hosting gratuito” que en adelante, y a efectos meramente didácticos, llamaré “malicioso.example.com”. Y en él creé tres ficheros

Fichero 1: El Javascript

El primero de los ficheros se llamaba “a”. Nombre corto, pero suficiente. Al igual que el código que contenía:



Ése era el script que quería ejecutar aprovechando la vulnerabilidad XSS. En pocas palabras, carga la página de cambio de contraseña y busca la línea que contiene “var pwdUser”, que es donde aparece el valor de la contraseña. Una vez la encuentra, la manda al servidor malicioso.example.com, al puerto 9999, donde un proceso está a la espera de recibir noticias.

Fichero 2: El formulario fantasma

Ahora necesitaba que la víctima sufriera las consecuencias del ataque XSS. Para ello me aproveché la vulnerabilidad CSRF, creando una página que simulara el formulario de configuración de impresora del router, pero sin tantas comprobaciones tontas.

Y, de camino, metiendo en el campo de la marca y el modelo el código que disparaba el XSS.
Una vez dispuesto el formulario, un poquito de JavaScript adicional se encargaría de enviarlo sin necesidad de intervención del usuario.

Eso es lo que tiene el fichero 1.html:


Fichero 3: La página del engaño

Para acabar, la única página que el usuario tiene que ver. Con la promesa de sacar algo útil del router, se pide al usuario que inicie sesión en dicho dispositivo y a continuación pulse un botón. Su nombre, 0.html. Y su contenido:


Y al pulsar el botón se carga, de forma invisible, el formulario fantasma.
… Y éste aprovecha el CSRF para activar el XSS
… … Y el XSS extrae la contraseña y la envía al sitio malicioso

¿Funcionará?

Fichero 4: A la caza del incauto

Para saberlo, nada mejor que probarlo. El usuario recibe la noticia de que hay un sitio web muy chulo que tiene que visitar. Quizá un correo. Quizá buscando en Internet. Quizá...



Siguiendo las instrucciones, la víctima abre otra pestaña, inicia sesión en el router y vuelve a la página engañosa.



Sin que él lo sepa, en el sitio web malicioso.example.com hay un proceso esperando que alguien se le conecte al puerto 9999. Para estas pruebas, basta con un pequeño shell script que extraiga el valor de la contraseña y la muestre en pantalla.



Cuando el usuario haga clic en el botón, algo le aparecerá en la pantalla al atacante: la contraseña de su víctima


Y, sí, esta imagen también la retoqué para que no apareciera la contraseña de mi amigo.

Conclusiones

Moraleja: Aunque no me gano la vida con estas cosas, debo decir que en alguna ocasión que otra me consiguen algo de comer. Y una buena cena siempre sabe mejor cuando te la paga otro.

Bueno, eso y que si tienes un router de éstos, yo no terminaría de estar tranquilo del todo. Sólo iniciaría sesión en él en un navegador sin plugins (porque los plugins pueden acceder al contenido de las páginas y hacer peticiones en tu nombre) y en el que no tuviera abierta ninguna otra pestaña (por si acaso alguna hace cosas como las vistas anteriormente). Y una vez terminada la configuración, cerraría sesión y navegador antes de ponerme a navegar por otros sitios. Y, aún así...

Y si tienes otro router... échale un vistazo. La experiencia me dice que cada uno debería dedicarse a lo que mejor sabe hacer. Y muchos fabricantes de hardware, cuando desarrollan software... bueno... eso.

El router de mi amigo (3 de 4)

Una idea de esas que nunca son buenas

Seguí mirando. Ya tenía un nuevo XSS y no debía olvidarme del CSRF de la vez anterior. De hecho, es más que posible que existan más problemas similares, pues es de esperar que todas las partes de la aplicación tengan un diseño parecido. ¡Apañados vamos!

Había una página que permitía cambiar la contraseña de acceso al router. Lo habitual: pedía una vez la contraseña anterior y dos veces la nueva


Es bueno eso de pedir la contraseña antigua cuando quieres poner una nueva, porque así proteges al usuario, no sea que se despiste y alguien le pille la sesión abierta y se le ocurra alguna travesura. Y también pones más difícil la explotación de algunos Cross Site Request Forgery.

Bueno, claro, eso siempre y cuando no pongas la contraseña antigua en la propia página. Y este router la ponía en el código fuente:



¡No estarías esperando que pusiera la contraseña de mi amigo, verdad! Esta foto la tuve que retocar un poco para no fastidiarle más de la cuenta.

Cuando menos, llamativo sí que es. La versión router del usuario que pega un postit en la pantalla con su contraseña. Y, me pregunté... ¿para qué querrá alguien poner eso ahí? Unas líneas que había un poco más adelante proporcionaban la respuesta:



¡Para hacer esa comprobación! Una vez más, la seguridad puesta en entredicho para realizar comprobaciones del lado del cliente.

Sea como sea, eso de poner una contraseña de acceso dentro del código JavaScript de una página, en texto claro como el agua, no es algo que yo suela recomendar a nadie.

La idea general

Con eso ya tenía bastante para dar el golpe de efecto que me hacía falta para ganarme un ágape por cuenta ajena. El guión, visto desde el punto de vista del atacante sería:

- Creo una página en un servidor gratuito que, aprovechando la vulnerabilidad CSRF, haga que el router registre una marca y modelo de impresora que me permita insertar código JavaScript en la página.

- Ahora, tengo que asegurarme de la víctima ha iniciado sesión en su router.

- Engaño a mi amigo para que visite a continuación mi página maliciosa (la anterior, la del CSRF y todo eso).

- Con eso, consigo que el código JavaScript que escribí se ejecute.

- Dicho código Javascript carga la página de cambio de contraseña. Analiza entonces su contenido y extrae de él la contraseña actual y me la envía a mi servidor.

Y... ¡voila! Ya tengo la contraseña del router de mi víctima.

Otros objetivos


Elegí la contraseña por lo llamativo de obtener algo que debería ser secreto pero... podría haber elegido cualquier otra cosa. La “graciosa” combinación de Cross Site Scripting y Cross Site Request Forgery me permitía obtener cualquier cosa de cualquiera de las páginas que ofrecía el router.

Y, mirando a ver qué tenían, me encontré con cosas llamativas. Cosas como:
- Dirección IP interna y externa (cara a Internet) del router
- Configuración de la WIFI
- Equipos conectados al router, con su IP y su MAC
- Marca, modelo, revisiones del firmware y otros detalles del hardware

Podría haber escogido cualquiera de ellas… o todas juntas. Y, la verdad, si alguien pudiera obtener todos estos datos de un montón de routers tendria acceso a una de las mayores redes Wifi que conozco. A partir de la dirección IP externa podría determinar aproximadamente la posición geográfica de los routers. Si éstos tienen una lista blanca de MAC que pueden conectarse, tendría algunas válidas y podría configurarlas en sus tarjetas de red. El SSID, la passfrase y todo lo demás también lo sabría...

Ideal para quien quiere conectarse a Internet sin estar demasiado controlado.

Pero había mucho más:

¡En una de ellas aparecía el número de teléfono de mi amigo!

Porque resulta que el router “controla” también su teléfono.

Supongo que la pondrán ahí por si se le olvida al dueño. Igual que la contraseña.

Y podía conseguir eso sin modificar nada en el router. Porque modificando su configuración se podría apoderar uno de la red a la que da servicio.

El router de mi amigo (2 de 4)

Comprobaciones en el cliente 


Necesitaba saber si esa comprobación se hacía sólo en la parte cliente, en el navegador, o si el router también lo miraba. De ser sólo en el cliente sería fácil saltarse la restricción. Por ejemplo, en Firefox se podría utilizar las herramientas para desarrollador web del navegador para localizar el punto en que se fija la longitud máxima...

… y cambiar el 16 por 1600...



Y con eso me podría poner a escribir a gusto. Probé entonces a poner como marca y modelo:
";alert("XSS");aaa="

… pero, a pesar de todo mi trabajo, no me hizo caso.


La página comprobaba si en la marca y el modelo había caracteres “raros”. Pero... ¿qué pasaría si yo realizaba la petición directamente al servidor y evitaba el código JavaScript de la página?

Para probar, utilicé un proxy que permite interceptar y modificar las peticiones que realiza el navegador. El que tenía más a mano era WebScarab. Un poco anticuado, sí, pero aún válido para muchas cosas. Lo inicié, indicándole que quería interceptar todas las peticiones, y configuré el navegador para que lo usara.

Después, rellené el formulario de configuración de impresora con valores válidos e hice clic en aceptar. Webscarab me presentó la petición y se puso a mis órdenes:


Yo, confiado, sustituí el valor de ippMake


… y le dije a Webscarab que siguiera adelante con la petición. La respuesta del router traía regalo:


¡Un XSS que permite inyectar directamente dentro del código JavaScript! Eso no se lo encuentra uno todos los días.

MORALEJA: Está muy bien eso de restringir la longitud de los campos de texto y los caracteres que pueden contener, porque así puedes poner difícil las cosas a quien quiera hacer un uso indebido del sistema. Pero si sólo lo haces en la parte cliente, si no realizas también esas comprobaciones en la parte del servidor, estos controles pueden ser fácilmente evitados.

Además, había otra cosita que no me entretuve en estudiar. Parece que el programa del router no comprueba adecuadamente los límites, los tamaños, de las variables, porque una prueba que hice fue poner una cadena un poco más larga de la cuenta como marca/modelo:
12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890

… y por la respuesta del router, parecía que me había metido en el principio de otra cadena, puesto que me mostraba el final de ésta (“/dev/printer0”) concatenada con lo que yo había puesto.



Un overflow que, bien aprovechado, quizá diera mucho juego. Pero eso es otra historia...

El router de mi amigo (1 de 4)

La apuesta


Hace ya tiempo que os conté como ayudé a un amigo que tenía problemas con el botón de activar y desactivar la WIFI en su router. El único problema fue que el apaño lo hice aprovechando una vulnerabilidad CSRF que le encontré al equipo.


Pues bien, hoy tuve tiempo y oportunidad de volver a ver a mi amigo. Todo iba bien hasta que me encaró y me dijo:

- ¿Sabes? Leí aquello que escribiste sobre el CSRF del router y todo eso. Y creo que hablas de “MI” router.

Parecía molesto. Yo, que no me lo esperaba, me las arreglé para poner cara de póker. Pero mi amigo se fue envalentonando:

- ¿Sabes? Creo que lo que cuentas no es verdad. Y si lo es, que no es del todo cierto. Y, aún así, que exageras. ¿Cómo va nadie a hacerse con el control de mi router desde Internet por visitar una página? Es que te gusta hablar por hablar...

Como, a estas alturas, uno ya empieza a ver claras algunas oportunidades, le miré a los ojos y le espeté:

- A que no eres capaz de apostarte una cena.

Eso fue todo lo que hizo falta para herir su orgullo y hacerle caer. Mientras él decía “¡apostada queda!”, yo llamaba por teléfono a casa y decía:

- Iros arreglando, que hoy comemos fuera.

Un XSS... y algo más

Entré al router con el usuario y la contraseña que ya conocía de la vez anterior y me puse a navegar por menús y opciones. Una cosa chula que tenía el cacharro era que podía compartir una impresora entre los equipos de la casa. El formulario para configurar esta opción era bastante sencillo




Cuando analicé el código fuente de la página, comprobé que había un código Javascript que rellenaba los campos con los valores almacenados en el equipo.


Y pensé: ¡No va a ser tan fácil!


Pero sí lo era. El router debe tener una instrucción que genera
var ippMake = "

… y a continuación pone el valor que corresponda a la marca y modelo de la impresora. Finalmente, cierra la cadena y la instrucción al añadir:
";


¿Qué pasaría si pongo una marca y modelo que contenga una comilla doble? Por ejemplo


marca"modelo


Pues... que el programita del router lo copia todo sin hacer más comprobaciones y terminaríamos teniendo un código JavaScript con errores de sintaxis por una cadena mal cerrada.
var ippMake = "marca"modelo";


Pero, oye, eso nos puede permitir insertar instrucciones nuevas dentro del código JavaScript. Si nuestra impresora fuera de una marca rara, digamos:
";alert("XSS");aaa="

Terminaríamos teniendo:
var ippMake = "";alert("XSS");aaa="";

Y eso nos permitiría ejecutar “cosas nuevas” en las páginas del router.

Claro que algún problemilla habría que superar. Y es que el campo en el que se pone la marca y el modelo sólo permite insertar un máximo de 16 caracteres. Pocos para lo que yo necesitaba.