Reflected XSS a través de SQL Injection
Hoy me tocó analizar una nueva página, y como siempre, con cada revisión uno aprende a rebuscarselas para lograr lo que desea probar.
En esta oportunidad la página que me tocó analizar contaba con serias vulnerabilidades de SQL Injection, pero yo buscaba realizar algún XSS para no sentirme tan vacío =P Si bien no encontré variable donde inyectar HTML directamente, sí detecté que era posible hacer que MySQL me retorne el string que yo deseaba, es decir, un <script>alert('XSS');</script>

Hacer que MySQL retorne un string armado a mano es tan simple como hacer un SELECT '<script>alert(String.fromCharCode(88, 83, 83));</script>'. El problema es encontrar una sección de la página que tome el resultado de ese SELECT y lo inserte dentro de la misma.

Un caso bastante clásico de éste problema es en las páginas que obtienen el contenido de una sección a través de una consulta SQL. Si podemos hacer que dicho pedido incluya nuestro pedido, estaríamos logrando lo que deseamos.
Para ejemplificar la falla, supongan que tenemos el siguiente código:

<?php
$db = mysql_connect('localhost', 'usuario', 'password');
mysql_select_db('test', $db);

if(isset($_GET['id']))
{
$query = "SELECT title, content FROM content WHERE id=".$_GET['id'];

if(($result = mysql_query($query, $db)) !== FALSE)
{
$row = mysql_fetch_row($result);
$title = $row[0];
$content = $row[1];
}
}

?>
<A HREF="http://localhost/pagina.php?id=1">Home</A><A HREF="http://localhost/pagina.php?id=2">Productos</A><A HREF="http://localhost/pagina.php?id=3">Partners</A><A HREF="http://localhost/pagina.php?id=4">Links</A>

<BR><BR>

Section: <?php print $title; ?><BR>
<?php print $content; ?>
A través del código anterior tenemos un menú armado que cuenta con las secciones Home, Productos, Partners y Links. Lo que hace la parte PHP es, a través de la variable id, obtener el título y el contenido de la sección e incluirlos en la página. Todas las secciones están almacenadas en una tabla que he llamado content.

Este código es altamente susceptible a SQL Injection, ya que nada prohíbe al usuario cambiar el contenido de la variable id y realizar cosas realmente peligrosas.
En el funcionamiento normal, la consulta que trae los datos del contenido luciría así:
SELECT title, content FROM content WHERE id=1
si es que utilizamos la URL http://localhost/pagina.php?id=1

Pero si por ejemplo un atacante ejecuta la URL http://localhost/pagina.php?id=1%20and%201=0 la consulta será:
SELECT title, content FROM content WHERE id=1 and 1=0
dando como resultado que no se obtenga sección alguna.

Una vez probado que podemos inyectar SQL, necesitamos buscar la forma de insertar nuestra consulta XSS. Para ello haremos uso de la función SQL UNION. UNION nos permite unir los resultados de una consulta con los resultados de otra consulta, siempre y cuando ambas consultas tengan como resultado la misma cantidad de columnas.
Por ejemplo, supongamos que tenemos la tabla usuarios y la tabla contenido. Podríamos hacer una consulta como esta:
SELECT nombre, apellido FROM usuarios WHERE usuario_id=1 UNION SELECT titulo, contenido FROM contenido WHERE contenido_id=2
Si existe el usuario con ese id y el contenido con ese id, el resultado serán dos filas, una con los datos del usuario y la otra con los datos del contenido.

Ahora relacionemos esto con lo que queremos hacer. Para el ataque queremos utilizar la consulta SELECT '<script>alert(String.fromCharCode(88, 83, 83));</script>', pero no podemos evitar la consulta original, así que haremos un UNION. Dado que la consulta que se encuentra en la página devuelve un valor si ingresamos un id válido, deberemos ingresar un id con un valor que no exista en la base de datos, como por ejemplo 1000. A esto le unimos nuestro SELECT que siempre retorna un valor, y obtendremos una consulta donde lo obtenido es nuestro script.

Si prestaron atención a lo que iba diciendo, necesitamos que nuestra consulta devuelva la misma cantidad de columnas que la original, así que debemos averiguar cuántas columnas tiene la consulta original. Para esto podemos hacer dos cosas: probar consultas arbitrarias con UNION hasta que nos da un resultado válido, o utilizar la función ORDER BY. ORDER BY nos permite organizar los resultados por columna, así que si le especificamos un valor de columna mayor al posible, obtendremos un error. Por ello, podemos ir reduciendo el valor de ORDER BY hasta que tengamos una consulta válida, o bien incrementar desde 1 hasta que tengamos una inválida.
Para nuestro ejemplo, si probamos la consulta "SELECT title, content FROM content WHERE id=1 ORDER BY 1", funcionará, al igual que lo hará con "SELECT title, content FROM content WHERE id=1 ORDER BY 2", pero si utilizamos "SELECT title, content FROM content WHERE id=1 ORDER BY 3", no obtendremos resultado. La URL de esta consulta sería http://localhost/pagina.php?id=1%20order%20by%203

Con esto ya sabemos que la consulta tiene 2 columnas, así que ahora armamos nuestra nueva consulta de acorde a esto. La consulta debería quedar algo así:
SELECT title, content FROM content WHERE id=1000 UNION SELECT '<script>alert(String.fromCharCode(88, 83, 83));</script>', 'lala'
para lo cual utilizamos la URL http://localhost/pagina.php?id=1000%20union%20select%20%27%3Cscript%3Ealert(String.fromCharCode(120,%20115,%20115));%3C/script%3E%27,%20%27lala%27

Como nuestra tabla no tiene una entrada con el id 1000, el resultado será una fila con las columnas '<script>alert(String.fromCharCode(88, 83, 83));</script>' y 'lala', las cuales se asignarán a las variables $title y $content, y subsiguientemente se imprimirán en la página, generando efectivamente un XSS.


Como verán, existen muchas formas de realizar XSS, esta es una más para añadir a las que ya nombré en mis anteriores artículos: Cruzando información, Cross-Site Scripting (XSS), Rompiendo a lo grande: XSS avanzado y Combatiendo el Cross Site Scripting.


EDIT (30/01): Zerial publicó en su blog otra forma de utilizar SQL Injection para realizar ataques XSS, les recomiendo su lectura: Explotar XSS mediante SQL Injection.

2 comentarios:

JaviZ dijo...

Muuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuy bueno!

Zerial dijo...

Hola d3m4s1@d0v1v0, hace tiempo que he practicado el XSS tal como dices y además tengo otra forma de hacerlo, me inspire en tu articulo y escribi uno yo, explicando mi otra manera de hacerlo. Si quieres puedes leerlo en mi blog.
Muy buen articulo.
saludos!

Publicar un comentario