lunes, septiembre 07, 2009

Reto Hacking X: Solucionario (I de III)

*****************************************************************************************
- Reto Hacking X: Solucionario (I de III)
- Reto Hacking X: Solucionario (II de III)
- Reto Hacking X: Solucionario (III de III)
*****************************************************************************************

Introducción

Este documento describe una solución al Reto Hacking X de Informática 64, que se publicó el 24 de julio de 2009 en la siguiente dirección web: http://retohackingx.elladodelmal.com


Fase 1: Homer ante el Zurdorium... ¿Qué inyectar?

Pistas

En esta ocasión únicamente contábamos con la descripción general del reto, publicada un día antes de su comienzo, en el que aparecen algunos párrafos indicando que el reto trata de lo que se ha hablado en el último año en el blog, que hay que seguir un camino claro siendo algo imaginativo y poco más.

En nuestra opinión, ninguna pista nos orientaba claramente en ninguna de las fases, así que no nos detendremos más con las pistas y vamos directamente a su resolución.

Nivel 1

Tras darnos de alta y acceder con nuestro usuario registrado, nos encontramos ante el primer nivel, en el que vemos a Homer Simpson en la puerta del Leftorium, la tienda de Flanders. Si pulsamos con el ratón sobre el teclado rojo de la parte izquierda, nos aparecerá un cuadro de autenticación típico (usuario y contraseña).

Inspeccionando el código fuente de la página no encontramos nada raro, así que todo parece indicar que se trata de acertar el usuario y la contraseña. Tras pasar un buen rato intentando todo tipo de inyecciones, combinaciones de usuarios y contraseñas más o menos absurdas (nombres y palabras relacionadas con los Simpson, contraseñas típicas, etc) o buscando esteganografía en las imágenes, no conseguimos obtener ningún indicio, ningún mensaje ni respuesta diferente de “Acceso denegado”.

Al final, la solución resultó ser una sencillísima inyección SQL (bastante peculiar, eso sí) en el campo de la contraseña. Resulta que los textos que aparecían en la imagen eran pistas que teóricamente nos debían hacer pensar en que la página estaba diseñada por un zurdo (Flanders) y que los zurdos hacen las cosas “al revés”. Por tanto, en vez de programar una consulta SQL típica, un zurdo habría hecho algo así:

"SELECT * FROM Usuarios WHERE '" + txtContraseña + "' = Contraseña"

Llegados a este punto de extrema imaginación, simplemente tenemos que inyectar una cadena de texto que nos permita saltarnos la autenticación. Para ello no hay más que invertir la clásica inyección de bypass:

' or '1'='1 -> 1'='1' or ' ( o equivalentemente: '='' or ' )

Con esta inyección, conseguiremos una consulta bien formada cuya primera condición es siempre verdadera y que al ejecutarse sobre el motor de bases de datos nos devolverá todos los registros de la tabla en cuestión:

"SELECT * FROM Usuarios WHERE '1'='1' or '' = Contraseña"

Con la solución delante de nuestras narices la cosa parece muy fácil (y en el fondo lo es), pero en realidad podría haber sido una fase de tipo acertijo o de cualquier otra tecnología. Nada nos hacía pensar en que la autenticación estaba basada en una consulta SQL (podría haber sido también XPath, LDAP, ... o incluso “hardcoded” en el propio código de la página), ni que el campo de contraseña era el vulnerable (y no el de usuario), ni mucho menos que a alguien se le ocurriera programar una consulta en la que primero aparece el valor y después el campo a comparar (lo cual es perfectamente válido a nivel de sintaxis de cualquier SGBD y nos lo podríamos encontrar por ahí).

Por cierto, si os fijáis en el Hall of Fame, veréis que hay 4 usuarios que sacamos la primera fase prácticamente al mismo tiempo (nomasenviaoeso, samsa, pepeluis y yo mismo). Evidentemente eso no es casual, ya que tras las primeras 3 horas de pruebas desesperantes, en esta ocasión decidimos aliarnos unos cuantos (RoMaNSoFt, Uri, Dreyer y yo) para resolver el reto antes de 24 horas como fuera... ;-)

En medio de esa especie de brainstorming con las ideas de todos nosotros, el primero que dio con la solución correcta fue Uri y a partir de ahí los demás nos fuimos basando en sus pistas para llegar a la misma conclusión.

Nivel 2

Este nivel constaba de varias fases. En la primera de ellas veíamos a Homer sentado sobre un sofá, vistiendo una camiseta roja con un toro negro. En la página no hay ningún control con el que poder interactuar, así que analizamos a fondo toda la página y comprobamos que todo el código que hay sólo sirve para hacer que Homer parpadee de vez en cuando.


Homer español

Observamos que la página se compone de un mosaico de imágenes y que el nombre del fichero de la imagen central (la correspondiente a Homer) es un tanto aleatorio (todo indica que se ha aporreado el teclado para generarlo), así que intentamos buscar alguna relación con la sesión, con nuestro nombre de usuario, con el navegador usado, etc. Entre las cuatro personas que éramos, la casualidad hizo que al abrir la página desde otro de nuestros navegadores alternativos, Homer apareciera vestido de forma diferente (en esta ocasión con una camiseta del Tío Sam).


Homer apoyando al tío SAM

En realidad no era cuestión del navegador, sino de las preferencias del idioma, lo cual fue fácil de determinar por el método de prueba y error. Podemos intercalar algún proxy para ir variando los parámetros de la petición y ver lo que sucede (ej: User-Agent, Referer, etc). Seguramente lo más normal era encontrarse con la imagen de Homer desnudo tras alterar o borrar el contenido del parámetro Accept-Language, ya que era la imagen que aparecía por defecto cuando la referencia cultural no era ninguna de las contempladas por los desarrolladores de la aplicación web del reto.


Hommer desnudo

Ahora que ya tenemos la forma de conseguir respuestas diferentes, se tratará de averiguar en qué consiste esta fase. Para ello lo primero que observamos es que todo lo que se envíe después de un guión es ignorado por la aplicación (es decir, obtenemos la misma imagen al establecer las cadenas es-ES, es-US, es-AR, o incluso es-loquesea), así que todo lo que vayamos a inyectar tendrá que estar en la parte correspondiente al idioma y no en la de las subetiquetas que identifican la variante del idioma o país.

Si inyectamos alguna comilla simple, romperemos la estructura de la presunta consulta que está ejecutando la aplicación y se nos redirigirá a una página de error. Sin embargo, si inyectamos una cadena que forme una consulta sintácticamente válida, simplemente veremos la imagen de Homer desnudo. Por ejemplo, podríamos probar con estas inyecciones con el fin de determinar el tipo de tecnología utilizada:

- es' -> Página de error
- e' + 's -> Página de error
- es' or '1'='1 -> Homer desnudo
- es' and '1'='1 -> Homer desnudo
- es' AND '1'='1 -> Página de error


Entre otros detalles, nos llama la atención que la última consulta falle al escribir el operador AND en mayúsculas, lo cual nos lleva a pensar directamente que estamos ante una consulta de XPath, ya que en este lenguaje se tiene en cuenta este detalle (cosa que no ocurre en la sintaxis de SQL, por ejemplo). Ahora se trata de booleanizar la inyección, de forma que podamos controlar la imagen que se muestra según la condición que deseemos con el fin de obtener todo el documento XML sobre el que se están ejecutando todas las consultas de XPath. Lo normal hubiese sido que la cuarta consulta de ejemplo (es' and '1'='1) mostrara la imagen del toro, pero no ha sido así...

La razón es bien sencilla y es que nos encontramos ante una consulta un poco más compleja de lo normal, por lo que deberemos utilizar algún paréntesis para ubicar nuestra condición fuera de su ámbito local (para que ésta afecte a toda la consulta). Por ejemplo, comprobamos lo que ocurre al inyectar las siguientes cadenas:

- es') and (1=1 or 'a'=' -> Homer con la camiseta del toro
- es') and (0=1 or 'a'=' -> Homer desnudo


Ya tenemos la booleanización necesaria para extraer todo el documento XML mediante la técnica descrita por Amit Klein (Blind XPath Injection), de la que ya habíamos hablado en retos anteriores. En el caso de Dani fue relativamente fácil y rápido adaptar la herramienta que ya tenía desarrollada para este menester, de forma que inyectara directamente en el parámetro Accept-Language, pero algo falló, ya que la herramienta fue incapaz de obtener el contenido del fichero en primera instancia, debido a que ocasionalmente producía errores en la aplicación de forma sistemática.

Empezó fallando al contar el número de nodos de tipo PI (Processing Instruction). Tras omitir ese paso desde el código fuente, se obtenían correctamente el resto de nodos, pero el siguiente fallo apareció al obtener la longitud de las cadenas de texto. Dejando la herramienta de lado y probando con consultas más sencillas de forma manual, Dani se dio cuenta de que las consultas que estaban fallando tenían algo en común: el guión medio...

count(/child::processing-instruction())
string-length('abc')


Recordemos que la aplicación ignoraba todo lo que había detrás de ese guión, razón por la que estaba fallando la aplicación al quedar la consulta mal estructurada. Teóricamente podíamos haber adaptado la herramienta para que no utilizara ninguna subconsulta que contenga un guión, pero no fue necesario, ya que bastaba con codificar el guión (sustituyéndolo por %2D).

Sin embargo, la aplicación volvió a fallar al utilizar cualquiera de las funciones relacionadas con las cadenas de texto (por ejemplo: substring, starts-with, concat, etc). En un primer momento se pensó que se habían filtrado manualmente, pero de nuevo todas ellas tenían algo en común: todas ellas necesitan más de un parámetro y éstos van separados por comas. La solución pasaba por codificar la coma (%2C) y ya teníamos la aplicación completamente funcional, por lo que empezó a extraer todo el documento XML de principio a fin.

A pesar de tratarse de una aplicación multihilo y además con alguna que otra optimización, este proceso de extraer carácter por carácter sin búsqueda binaria es lentísimo y su ejecución requirió de varias horas. En cualquier caso, a medida que se iba extrayendo el fichero, se iban desvelando algunos datos ocultos hasta entonces. Por ejemplo, gracias al XML descubrimos que los ficheros CSS estaban en una carpeta llamada “estilosestilosos” o que había una imagen definida para el idioma Xhosa (con el que aparecía un curioso Homer algo tostado, pero no ayudaba a la solución)...

Si analizamos el contenido del fichero XML extraído, se observa la relación de los diferentes idiomas definidos junto con sus respectivas imágenes y hojas de estilo, así como algún que otro comentario más o menos cachondo, la mayoría de ellos para hacernos perder tiempo.

[?xml version="1.0" encoding="utf-8"?]
[idiomas]
           [!-- Este fichero contienen las imagenes para las camisetas de Homer... --]
           [idioma lang="es" name="Spanish"]
               [!-- Camiseta española --]
               [img]
               imagenes_l2/__4lo_67_wwwsassdf_juksidkdjfulloka_.png
               [/img]
               [?camiseta file="estilosestilosos/spain.css" ?]
           [/idioma]
           [idioma lang="en" name="English"]
               [img]
               imagenes_l2/55_112_ooosldkkfjjs1111XXXxxX___.png
               [/img]
               [!-- En un pueblo italiano al pie de la montaña vive nuestro amigo
               Marco en una humilde moradaaa. --]
               [?camiseta file="estilosestilosos/usa.css" ?]
           [/idioma]
           [idioma lang="xh" name="Xhosa"]
               [img]
               imagenes_l2/_sd_90992Aspp_lokiksdddes11230____a.png
               [/img]
               [!-- TOP SECRET! peticion_homer_simpson.doc redactada
               por Way.Smithers--]
               [?camiseta file="estilosestilosos/poralliabajo.css" ?]
           [/idioma]
           [idioma lang="nude" name="p0rn!"]
               [img]
               imagenes_l2/nude_homer_12293wfwrsgshdjf_2323232_xxxsdf_.png
               [/img]
               [!-- http://www.informatica64.com/TechNews/
               link.aspx?id=09ec203fbe930df8a53e17aaa5b022c9 --]
               [?camiseta file="estilosestilosos/nude.css" ?]
           [/idioma]
[/idiomas]

[Continuará...]

Autores del Solucionario: Dani Kachakil & RoMaNSoFt

*****************************************************************************************
- Reto Hacking X: Solucionario (I de III)
- Reto Hacking X: Solucionario (II de III)
- Reto Hacking X: Solucionario (III de III)
*****************************************************************************************

3 comentarios:

  1. Buena explicación del reto.

    Esperamos la siguiente(s) parte(s)... (Jo! yo algún día tb quiero ser así de listo y saber tanto... palabrita que estoy estudiando.).

    Ah y feliz vuelta al cole para los que tengais niñ@s.

    ResponderEliminar
  2. Excelente... debo admitir que la segunda fase por mas que sabia que era Blind XPath Injecion nunca imagine por donde sacar alguna pista... con la cantidad de veces que vi el video de Pedro Laguna no se me ocurria nada u.u

    bueh entretenido el reto lo repasare estos dias. ^^;

    Saludos

    ResponderEliminar
  3. ya no esta disponible el reto?

    ResponderEliminar