jueves, mayo 08, 2008

Solucionario Reto Hacking VII (I de II)
por Dani Kachakil


Hola amigos, el Reto Hacking VII lo ganó Dani Kachakil, así que os dejo su solucionario aquí publicado, más de un mes después de su apertura. La segunda fase, y la crítica del reto irán en la siguiente entrega, pero mientras tanto podéis ir leyendo como había que pasar la primera fase...

Después os pondré alguna cosa mia sobre el reto y pondremos disponible el PDF del solucionario completo.

Saludos Malignos!

Introducción

Este documento describe una solución al Reto Hacking VII de Informática 64, el segundo de la segunda temporada, que se publicó el 4 de abril de 2008 en la siguiente dirección web: http://retohacking7.elladodelmal.com

La pecera de Kachackil

Pistas

En esta ocasión no teníamos pistas a nuestra disposición, a no ser que a alguien le sirviera la frase que aparecía en el post que anunciaba el comienzo del reto:

La fase 1, será como una boda y la fase 2 como un mal sueño, así que nada, salud y suerte a los toreros!

Fase 1: Llenando la pecera

Como viene siendo habitual en los últimos retos, para acceder a la primera fase teníamos que registrarnos con un nombre de usuario y una dirección de correo válida, donde recibiremos la contraseña correspondiente generada aleatoriamente. Accediendo con estos datos en la página de login, aparecían los siguientes elementos en el menú:

Login: Para darnos de alta o validarnos con nuestro usuario y contraseña.
Seguimiento: Lista de participantes, desde la cual se puede acceder al progreso de cada uno, incluso sin necesidad de autentificarnos.
Léeme: En este punto se nos explica un procedimiento para superar la primera fase, llenando nuestra pecera de peces (eso sí, armados de paciencia).
Pecera: Aquí vemos los peces que tenemos en nuestra pecera, pudiendo añadir un nuevo pez cada 5 minutos. En total "solo" tenemos que añadir 3960 peces…
Diviértete: De momento no tenemos acceso a esta sección. Una vez superada la primera fase, en este punto nos aparecerá la segunda.

Lógicamente, esta primera fase del reto consiste en buscar una forma alternativa de llenar la pecera sin tener que añadir los 3960 peces uno por uno, esperando a que pasen los 5 minutos de rigor entre un pez y el siguiente. De entrada esto nos lleva a pensar en dos posibles alternativas: o bien buscar alguna forma de evitar el retardo y automatizar las casi 4000 peticiones necesarias, o bien encontrar una forma de añadir un número arbitrario de peces en un solo paso.

Resulta curioso que en nuestra pecera aparezca un desplegable que solo contiene el nombre de nuestro usuario, por lo que no sería tan ilógico crear otro usuario para añadirle peces al nuestro, pero vemos que de esta forma no evitamos el retardo y no llegamos a ningún sitio. Por otro lado, las primeras pruebas parecen indicar que dicho campo no es vulnerable a inyección SQL (aunque más adelante veremos que sí lo era).

Por tanto, en principio nos decantaremos por la segunda opción, ya que parece más viable si conseguimos ejecutar alguna instrucción UPDATE o algo por el estilo con el fin de actualizar a nuestro antojo el contador de peces.

Desde la opción Seguimiento, podemos visualizar la pecera de cualquiera de los participantes. Llama la atención que para ello tengamos que superar un pequeño mecanismo de CAPTCHA, escuchando un fichero de audio y escribiendo el texto que contiene. ¿Por qué nos querrán dificultar el acceso a esta información tan irrelevante?

Observando la URL vemos un parámetro de tipo texto (user), que se le pasa a la página por GET. Hacemos un par de pruebas típicas para comprobar si dicho parámetro es vulnerable a inyección SQL:

verPecera.aspx?user=UsuarioExistente
verPecera.aspx?user=UsuarioExistente' and '1'='1
verPecera.aspx?user=UsuarioExistente' and '0'='1


Las dos primeras peticiones muestran la pecera del usuario especificado, mientras que la tercera petición muestra siempre una pecera en blanco. Este pequeño detalle debería ser suficiente para poder extraer toda la información de la base de datos, siempre que tengamos acceso a su catálogo. Aunque todavía no está claro que este sea el camino que vayamos a tomar, ya que para conseguirlo serían necesarias muchas peticiones al servidor, por lo que tendríamos que buscar la forma de evitar el CAPTCHA. Antes de automatizar nada, por el momento vamos a ver lo que conseguimos averiguar de forma más artesanal.

En primer lugar, intentamos determinar el gestor de bases de datos subyacente, inyectando diferentes instrucciones con variaciones en la sintaxis, objetos de sistema y funciones características de cada uno de los gestores más extendidos. Observamos, por ejemplo, que no fallan funciones como substr o uniones con la tabla dual, que no se ejecutan varias instrucciones separadas por punto y coma, que la comparación de cadenas diferencia entre mayúsculas y minúsculas, que el doble guión comenta el resto de la consulta, etc. Todo esto nos lleva a pensar que estamos ante una base de datos de Oracle, así que nos documentaremos un poco sobre su catálogo y su sintaxis.

Por cierto, como curiosidad, comentar que era muy fácil llenar la pecera (al menos de forma visual), aunque con ello no conseguíamos nada útil:

verPecera.aspx?user=UsuarioInexistente' union select 3960 from dual--

En todo caso, no era difícil adivinar algunos de los campos involucrados por simple deducción, aplicando el método de prueba y error. Por ejemplo, probando inyecciones como estas:

verPecera.aspx?user=UsuarioInexistente' or usuario='UsuarioExistente'--
verPecera.aspx?user=UsuarioInexistente' or numeropeces>20--


Ahora que sabemos el nombre de dos columnas, en lugar de extraer todo el catálogo iremos directamente a localizar el nombre de las tablas que contengan alguna columna llamada numeropeces desde ALL_TAB_COLUMNS. Para ello podemos ejecutar la siguiente consulta para determinar el número de tablas involucradas:

verPecera.aspx?user=' and 0=1 union select count(*) from ALL_TAB_COLUMNS where lower(COLUMN_NAME)='numeropeces'--

Como era de esperar, vemos que aparece un único pez, por lo que solo nos falta determinar el nombre de la tabla que contiene dicha columna, aprovechando la pecera como si se tratara de un visualizador numérico. Para determinar cualquier valor (que no supere el máximo de 3960), lo haremos simplemente contando el número de peces de cada tipo y multiplicando cada uno por el valor correspondiente a su tamaño (según la leyenda que aparece en la sección Léeme del menú principal). Ahora veamos cómo obtener la primera letra el nombre de la tabla mediante este procedimiento:

verPecera.aspx?user=' and 0=1 union select ascii(substr(TABLE_NAME,1,1)) from ALL_TAB_COLUMNS where lower(COLUMN_NAME)='numeropeces'--

Al inyectar esta consulta vemos que en la pecera resultante aparecen 7 peces de color morado y 10 peces de color naranja, valor que interpretamos como 80, que según las tablas ASCII corresponde a la letra "P". Procedemos entonces con el siguiente valor:

verPecera.aspx?user=' and 0=1 union select ascii(substr(TABLE_NAME,2,1)) from ALL_TAB_COLUMNS where lower(COLUMN_NAME)='numeropeces'--

En este caso, contamos 6 peces morados y 9 naranjas, valor que se interpreta como 69, o lo que es lo mismo, la letra "E" en ASCII. Si continuamos con el proceso hasta el final, determinamos que el nombre de la tabla que buscamos es "PECES". Sin embargo, si intentamos ejecutar subconsultas que involucren dicha tabla veremos que no obtenemos los resultados esperados. Por ejemplo, intentaremos determinar el número de registros que contiene dicha tabla con la siguiente inyección SQL:

verPecera.aspx?user=' and 0=1 union select count(*) from PECES--

Parece que algo no va bien, ya que a pesar de que la consulta aparenta no tener ningún problema sintáctico, su ejecución falla y, por tanto, la pecera aparece vacía. Esto es debido a que falta un pequeño detalle: prefijar el nombre de la tabla con el propietario de la misma. Ya que tenemos acceso al catálogo, podemos obtener el nombre de dicho propietario utilizando el mismo procedimiento que en el caso anterior, simplemente modificando la columna a la que estamos accediendo:

verPecera.aspx?user=' and 0=1 union select ascii(substr(OWNER,1,1)) from ALL_TAB_COLUMNS where lower(COLUMN_NAME)='numeropeces'--

Esta consulta devuelve el valor 83 ("S"). Continuando con el proceso determinamos que el propietario que estamos buscando es "SYS", por lo que esta consulta ya es válida y devolverá el número de participantes registrados en el reto:

verPecera.aspx?user=' and 0=1 union select count(*) from SYS.PECES--

La verdad es que con un poco de suerte nos podríamos haber ahorrado todo el procedimiento descrito anteriormente, ya que los nombres de todos los objetos que hemos obtenido eran bastante lógicos y perfectamente deducibles. De cualquier forma, ahora que ya tenemos comprobados todos los detalles relativos a la tabla, solamente nos falta encontrar la forma de ejecutar la consulta de actualización.

Toda la documentación que podemos encontrar sobre inyección SQL en Oracle coincide en que de forma general no se pueden ejecutar dos instrucciones diferentes en un solo paso (como sí se podría hacer en SQL Server, separándolas por un punto y coma). Esto solamente es factible para casos muy concretos. Si dejamos de lado las técnicas que involucran otro tipo de vulnerabilidades (como desbordamientos de buffer, etc.), solamente nos quedará la posibilidad de inyectar dicha instrucción dentro de un bloque PL/SQL y todo apunta a que las consultas que hemos estado ejecutando hasta el momento no se encuentran dentro de ninguno de esos bloques.

Aquí es cuando entra en juego ese misterioso desplegable que especifica el usuario destinatario del pez a añadir. Si intentamos las típicas pruebas para determinar si el parámetro es vulnerable, veremos que algo falla y que no conseguimos añadir el pez:

UsuarioExistente' and '1'='1

Sin embargo, si modificamos el contenido del desplegable con otro tipo de instrucciones observaremos que sí conseguiremos añadir el pez, como por ejemplo el nombre de nuestro usuario concatenado de esta manera (o de cualquier otra que se nos hubiera ocurrido con la finalidad de comprobar si se están procesando las funciones propias del gestor de bases de datos subyacente):

'||CHR(ASCII('U'))||CHR(ASCII('s'))|| CHR(ASCII('u'))||'arioExistente

Por cierto, para modificar el contenido del cuadro de lista desplegable podemos optar por varias alternativas. Por ejemplo, podemos intercalar un proxy (como Odysseus), o usar algún complemento para el navegador (como Web Developer para FireFox, o la Developer Toolbar para Internet Explorer), etc.

Ahora que hemos asumido que estamos dentro de un bloque de PL/SQL y que hemos conseguido que funcionen ciertas inyecciones propias de Oracle, vamos a intentar inyectar una consulta de actualización. Para ello podemos ponernos en la piel del programador, imaginando que tenemos que implementar la invocación de algún procedimiento dentro de un bloque PL/SQL. En el caso más simple, lo primero que tendremos que hacer es declarar el bloque con instrucciones "BEGIN" y "END" y lo segundo es la invocación del procedimiento dentro de dicho bloque. Por ejemplo:

"BEGIN procedimiento('" + parámetro + "'); END;"

Por tanto, nuestra inyección deberá cerrar la cadena, el paréntesis y terminar el bloque PL/SQL, comentando el resto de la instrucción con un guión doble (sin olvidar la confirmación de la actualización con la instrucción COMMIT):

UsuarioExistente'); update SYS.PECES set NUMEROPECES=10; COMMIT; END;--

La inyección mostrada establece en 10 el número de peces de todos los usuarios (de ahí que apareciera ese mensaje de aviso de la sección Léeme que decía "… y ten cuidado no te quiten peces"). Para actualizar únicamente el número de peces de nuestro usuario, evidentemente tendremos que añadir una cláusula WHERE a la UPDATE.

Pues hasta aquí llegaba la primera fase del reto. ¿A que no era tan difícil? Bueno, en realidad tampoco era tan difícil dejarse algún pequeño detalle en cualquiera de los pasos, que es lo que nos suele hacer perder más tiempo del necesario a todos.

6 comentarios:

TaYoKeN dijo...

Genial!

Me ha encantado, pero si la segunda parte es mucho más difícil haré como en esta y esperaré a que se publique la solución :)

Saludetes!!!

SealTeam dijo...

Con razón no conseguía nada de nada, me encabezoné con verPecera.aspx y mira que no se a quien fue le estuve metiendo peces en su pecera.
A parte de que de Oracle no tengo ni pajolera idea...a ver si consigo sacar la segunda parte antes de que publiques el solucionario.

g30rg3_x dijo...

La parte 2 fue/es una verdadera idea feliz (como le dicen por aquí).
Requería mucha pero mucha imaginación sobre todo el texto-2 de la parte 2. Increíble que aun teniendo al respuesta a esta parte en mi correo me quede atorado al menos un par de días en la misma >_<.

Saludos

elperroVerde dijo...

Ale con dos cojones...

Según OpenSolaris -España esta en Africa- Enlacea

Personalmente nunca he montado un Opensolaris, pero si esto es cierto, los tienen muy puestos...

Iver dijo...

Hablando de retos hacking .. que les parece este?

http://aviv.raffon.net/2008/05/07/HappyBirthdayIsrael.aspx

Saludos.

Dani Kachakil dijo...

Os dejo el PDF del solucionario aquí:

Soluciones de algunos retos de hacking

Saludos!

Entradas populares