Se me cayo un exploit

SEGURIDAD INFORMÁTICA

HACKING . PROGRAMACIÓN . ELECTRÓNICA . GNU/LINUX

Se me cayó un exploit en tu server.... por @UnaPibaGeek

[email protected]:~# ./Web-Hacking

La especialidad de la casa... :D

Bypass de filtros anti-XSS en PHP

bypass-filtros-xss.jpg

Definitivamente el momento de meditación y reflexión sobre la vida y los proyectos para los geeks es mientras un programa está compilando. Aprovechando este momento, y debido al gran recibimiento de mi blog por parte de colegas y amigos (¡gracias!) me puse a pensar cual sería mi próxima nota ;). Recordé que en algunos hacklabs tuve la oportunidad de mostrar cómo saltar los filtros más utilizados para evitar el ataque de Cross-Site Scripting (XSS), y la mayoría desconocía al menos una de las técnicas que mostré. Por lo tanto me pareció una buena idea detallarlas aquí.

Empecemos por explicar el titulo de esta nota, cuando hablo de filtros anti-XSS en PHP, me refiero a códigos en este lenguaje que intentan evitar el ataque de Cross-Site Scripting limpiando (filtrando) la entrada de datos por parte del usuario, eliminando o codificando las etiquetas HTML.

No es el fin de esta nota explicar un ataque tan infinitamente documentado como XSS, sino mostrar cómo muchas de las medidas de prevención para este ataque que se implementan desde código, puntualmente en PHP, pueden ser evadidas.

Las funciones de PHP que pueden ser utilizadas para prevenir XSS son: str_replace(), str_ireplace(), strip_tags() y htmlentities().

Veamos un ejemplo de filtro con cada una de estas funciones y la manera en que pueden ser saltadas, logrando ejecutar sin problemas el ataque de Cross-Site Scripting.

Filtro con str_replace():


<?php
$entrada = $_GET["datos"];
$filtrar = array("<script>","</script>","<iframe>", "etc");
$filtro = str_replace($filtrar,"",$entrada);

print $filtro;
?>


Esta función recibe tres argumentos: una primer cadena (se le puede pasar un array también), que será reemplazada por los caracteres que indiquemos en un segundo argumento, en este caso es "" o sea nada, por lo tanto eliminará la primer cadena, si la encuentra en el tercer argumento que es donde buscará la string del primer argumento, para este ejemplo, en la entrada de datos.

Por lo tanto si hacemos una inyección de código con la etiqueta <script>, la misma será eliminada por ende no se ejecutará la inyección... siempre y cuando el atacante no vaya a tener la mayúscula activada y escribir <SCRIPT> porque str_replace() es sensible a mayúsculas y minúsculas así que ahí sí que nos saltan :p entonces con <SCRIPT>alert("xss");</SCRIPT> este filtro quedaría saltado.

Ahora me dirán, "bueno, usamos str_ireplace() que no es sensible a mayúsculas y minúsculas y listo", bueno... veamos:

Filtro con str_ireplace():



<?php
$entrada = $_GET["datos"];
$filtrar = array("<script>","</script>","<iframe>", "etc");
$filtro = str_ireplace($filtrar,"",$entrada);

print $filtro;
?>


Es cierto, <SCRIPT>alert("xss");</SCRIPT> ya no funciona, sin embargo existen al menos dos formas de saltar este filtro:

1) Poniendo un espacio al final del tag, asi: <script >alert("xss")</script >
2) Poniendo un tag dentro del otro, asi: <scr<script>ipt>alert("xss");</scr</script>ipt>

En la primer forma rompemos la cadena exacta de lo que filtra la función pero no rompemos el tag por lo tanto la inyección se ejecuta. En la segunda forma str_ireplace() eliminará los primeros tags encontrados uniendo al resto de la cadena formando nada mas y nada menos que otro <script> ;)

¿Entonces? Nos quedan las funciones strip_tags() y htmlentities() que en este ejemplo lograrían evitar el ataque de Cross-Site Scripting.



<?php
$entrada = $_GET["datos"];
$filtro = strip_tags($entrada); //o htmlentities($entrada);

print $filtro;
?>


Si aplicamos este filtro, strip_tags() elimina todas las etiquetas HTML (¡y de forma insensible y recursiva!) por lo tanto las técnicas anteriores no nos servirán para evadirlo. Lo mismo sucedería si usamos htmlentities(), si bien htmlentities no elimina los tags, los codifica a entidades HTML por lo tanto veremos lo que inyectamos pero no lograremos que se ejecute como código en el navegador.

¿Nos ganaron? No del todo ;)

El ejemplo anterior es un código muy simple, veamos una parte del código de una aplicación real que filtra con htmlentities:



<?php
$pagina = htmlentities($_GET["pagina"]);

switch($pagina){
case "servicios.php":
include("servicios.php");
break;
case "contacto.php":
include("contacto.php");
break;
//Mas cases...
default:
print "Error al cargar. <a href='$pagina'>Reintente</a>";
}
?>


La variable filtrada con htmlentities se está utilizando dentro de un tag <a>.
La URL del sitio es la siguiente: www.vulnerable.com/index.php?pagina=servicios.php.

Nada me impide inyectar un atributo en la etiqueta <a> para poder ejecutar código luego de un evento del usuario.
Por ejemplo: www.vulnerable.com/index.php?pagina=servicios.php' onclick='alert(/XSS/).

Si inyectamos eso, el código fuente queda de la siguiente manera:
<a href="servicios" onclick="alert(/xss/)">Reintente</a>
Por lo tanto, cuando el usuario de click en "Reintente" la inyección de código será ejecutada, logrando saltar el filtro de htmlentities (de la misma forma se hace si la función fuera strip_tags).

Por consiguiente, mi recomendación es utilizar htmlentities con el flag ENT_QUOTES activado de manera que las comillas simples también sean convertidas a entidades HTML. Esto se hace de la siguiente forma:


<?php

$pagina = htmlentities($_GET["pagina"], ENT_QUOTES);
?>


Si así lo hacemos, la inyección de código anterior no funcionará, evitando el Cross-Site Scripting.

Mirá el video con todas las técnicas de bypass llevadas a la práctica: http://youtu.be/Cgl3lUnxgJg.

¿Te cansaste de leer? Tomate un café (share):

Fecha: 25/06/2015

¡Suscribite!

Aprendé más en...