miércoles, 9 de enero de 2013

Mi solución al Holiday Challenge 2012, parte 2

Seguimos en nuestro empeño de ayudar a salvar la Navidad, y tal como antes nos saltamos los diferentes controles que impedían el acceso al "SCADA" de Heat Miser esta vez le toca el turno a su gélido hermano: Snow Miser. La cosa no pinta fácil así que toca arremangarse y poner a funcionar todos los sentidos.

¡A por el SCADA de Snow Miser!

El interfaz del sistema de control del tiempo sigue siendo muy simple; los enlaces a los distintos niveles en la parte izquierda y un mensaje disuasorio en el centro bajo el que se sitúa el mecanismo correspondiente al nivel y que uno tras otro nos permitirán "apagar" el artilugio.


Partimos con acceso de sólo lectura y el mensaje correspondiente a la SNOW Alert nos dá la primera parte del enlace al siguiente nivel:
zone-1-D2E31380-50E6-4869-8A85-XXXXXXXXXXXX

Obtendremos primero el flag y comprobaremos si se han dejado algo olvidado en el código fuente HTML de la página:
<!-- The flag for this level is 3b5a630fc67251aa5555f4979787c93f -->

No hemos tenido suerte, así que tocará mirar en otro lado ... en los tweets de @sn0w_m1s3r por ejemplo:


Si observamos detenidamente la imagen parece que en el vaso de agua (sí, soy muy inocente) aparece algo reflejado, supuestamente proyectado desde el monitor, y tiene toda la pinta de ser lo que buscamos.


Tal cual hicimos con el info leak de heat miser, utilizando cualquier programa de edición de gráficos volteamos la imagen verticalmente y jugando un poco con el zoom, brillo y contraste obtendremos la parte del enlace que nos falta.


¡A por el siguiente nivel!

Snow Miser Zone 1

El interfaz ahora ya con acceso al mecanismo que permite "apagar" el "motor" correspondiente al nivel:


Como siempre lo primero anotaremos el flag "oculto" en el código fuente:
<!-- The flag for this level is 38bef0b61ba8edda377b626fe6708bfa -->

Nos centraremos ahora en el mensaje de la SNOW Alert, concretamente donde dice: If you have access to this level you can analyze the images and access the next zone.

Si analizamos las imágenes que incluye la página nos llama la atención que todas son PNG, exceptuando off.jpg, que sólo se muestra cuando accionamos el botón Disable que "desactiva" el mecanismo.


Repasemos ahora las preguntas planteadas para resolver el Challenge y correspondientes a este nivel: What is the key you used with steghide to extract Snow Miser's Zone 2 URL? Where did you find the key?

Queda claro que la imagen que buscamos es el JPG ya que es el formato gráfico utilizado por steghide, una herramienta de esteganografia, para incluir contenido oculto.

Como no tenía ni idea de la passphrase utilizada se me ocurrió currarme un script en python que implementara un ataque de diccionario de forma que pudiese ir incluyendo posibles palabras clave tal como se me fueran ocurriendo. El script puede descargarse desde aqui, y su uso es muy simple:
$ python stegcrack.py -h
Usage: stegcrack.py -p <passwordfile> -j <jpegfile> -o <outfile>

Options:
-h, --help show this help message and exit
-p PFILE dictionary file
-j JFILE jpeg stego file
-o OFILE output data file

También funciona sobre Windows y el único requisito además, evidentemente, de Python, es lanzarlo desde el mismo directorio donde tengamos el binario de steghide o que éste último esté en el PATH.

Después de haber incluido no pocas palabras en el diccionario andaba comentando con mi hermano los pormenores del problema, por eso de aclararme un poco las ideas, cuando me hizo la pregunta del millón: ¿Y no puede ser que la clave esté dentro de la propia imagen?

¡Qué bueno eres Diego! Desde Linux hubiera podido tirar de la línea de comandos tal que:
$ xxd off.jpg | less

pero dado que estoy en Windows, utilizando cualquier editor hexadecimal se descubre el pastel:


Justo antes de JFIF, desde el offset 0x000000e8 hasta el 0x000000f1 podemos leer la palabra "IceIceBaby", así que si la incluimos en el diccionario y lanzamos stegcrack:
$ python stegcrack.py -p dictionary.txt -j off.jpg -o oculto.txt
[*] Trying beeee-u-tiful
[*] Trying ROE
[*] Trying blizzard
[*] Trying IceIceBaby!

[+] Password found! IceIceBaby!
[+] Hide data wrote in oculto.txt

¡Ya tenemos el acceso al siguiente nivel!

Snow Miser Zone 2

Nuevamente mismo interfaz con los botones Enable/Disable:


Obtenemos el flag del nivel y curioseamos por el código fuente en busca de pistas:
<!-- The flag for this level is b8231c2bac801b54f732cfbdcd7e47b7 -->

Nada por ahí. Analizando ahora la SNOW Miser Alert otra vez se nos proporciona sólo una parte del enlace:
zone-3-EAB6B031-4EFA-49F1-B542-XXXXXXXXXXXX

Agotadas varias ideas recurrimos a los tweets de @h34t_m1s3r, enfrascado en la misma tarea que nosotros para intentar vulnerar la seguridad del "SCADA" de su hermano:


Así que parece que nuestro frío muchacho perdió su smartphone en la última visita al volcán de su cálido hermano, y éste último nos proporciona una imagen del sistema. Descargando que es gerundio, ya sea desde el website de Heat Miser o desde aquí (no vaya a ser que perdamos el acceso desde el otro).

En un primer vistazo localizamos varias bases de datos sqlite, y una contiene lo que buscamos:
$ tar xvzf android.data.tgz
$ cd data/data/com.android.browser/databases
$ sudo apt-get install sqlite3
$ sqlite3 -noheader browser2.db "select url from history where _id = 5"
http://snowmiser.counterhack.com/zone-3-EAB6B031-4EFA-49F1-B542-30EBE9EB3962/

Los comandos anteriores, ejecutados desde Linux, son la parte "chula" del proceso después de varias pruebas obteniendo las tablas para cada una de las bases de datos y analizando su contenido hasta dar con lo que buscaba. Ya hablamos un poco sobre sqlite cuando analizamos el historial de firefox.

Sea como fuere lo importante es que ya tenemos el enlace para el siguiente nivel.

Snow Miser Zone 3

El aspecto general, como hasta ahora:


Así que siguiendo nuestra ritual primero obtendremos el flag del nivel:
<!-- The flag for this level is 08ba610172aade5d1c8ea738013a2e99 -->

Toca prestar atención al SNOW Alert, el cual dice:

To reduce the impact of URL exposure or modification we have added a new mechanism to distribute changes to the URL (thanks to that minion that broke Zones 2+). Those of you with with access to Zone 4 should have received an encryption key. This key can be used to decrypt the URL for Zone 4. This allows us to securely communicate it to you without risk of unauthorized access.

Se nos proporciona el antiguo enlace en texto plano:
zone-4-F7677DA8-3D77-11E2-BB65-E4BF6188709B

el antiguo enlace una vez cifrado con la clave, que no tenemos:
20d916c6c29ee53c30ea1effc63b1c72147eb86b998a25c0cf1bf66939e8621b3132d83abb1683df619238

y el nuevo enlace cifrado con la clave que, seguimos sin tener, pero que parece ser la misma que la utilizada para cifrar la cadena anterior:
20d916c6c29ee54343e81ff1b14c1372650cbf19998f51b5c51bf66f49ec62184034a94fc9198fa9179849

Me cagüento los mengues ... ¿por qué no habré leido nada sobre criptografia hasta ahora? Ya lo decía mi padre "pon un kachakil en tu vida". Menos mal que Tim Medin nos echa un capote a través de twitter:


Así que tirando de Google llego al siguiente enlace: "Ataque a los cifrados de flujo", y siguiendo sus instrucciones realizo los siguientes pasos:
  1. Codificar el enlace descartado en texto plano a hexadecimal, separando el resultado en bloques de 2.

  2. Para cada uno de estos bloques hacer un xor con el texto cifrado correspondiente, también separado en bloques de 2, para obtener la key.

  3. Separar el texto cifrado correspondiente al enlace válido en bloques de 2 caracteres.

  4. Para cada uno de estos bloques hacer un xor con la key obtenida en el paso 2, y que también he separado previamente en bloques de 2.

  5. Convertir cada uno de los bloques de dos caracteres obtenidos en su equivalente en ASCII para obtener el texto descifrado.

Como soy un poco perrete, cuando llevaba un rato pensé que sería mejor que python trabajase por mí, así que éste es el script y éste el resultado:
$ python sm_zone3.py
zone-4-9D469367-B60E-4E08-BDF1-FED7CC74AF33

¡A por el siguiente nivel!

Snow Miser Zone 4

Seguimos con un interfaz similar, con la particularidad que en esta ocasión incluye un formulario para introducir una contraseña que nos permita acceder al siguiente nivel:


Nosotros a lo nuestro, primero el flag:
<!-- The flag for this level is de32b158f102a60aba7de3ee8d5d265a -->

y ahora revisaremos el texto de la SNOW Miser Alert:

Zone 5 requires top security. We are updating the code using svn 1.7 and have implemented One-Time-Password (OTP) functionality to access Zone 5.

The passwords are in a SHA1 format, so they are unguessable.

Lo cierto es que me sonaba algo el tema de la seguridad de los repositorios SVN, incluso de la mano de alguien de SANS, pero nada mejor para confirmarlo que la pista incluida en twitter:


A leer se ha dicho: "All your svn are belong to us".

Resumen: el problema de las prisas es que en ocasiones no se limpia el contenido de la aplicación al moverla al entorno de producción dejando restos utilizados por el sistema de control de versiones para gestionar las modificaciones y progresos en el código. Resulta que desde la versión 1.7 de Subversion existe una base de datos sqlite que guarda los nombres utilizados para almacenar los ficheros de código fuente. Y resulta también que estos ficheros se almacenan con una extensión que impide que sean interpretados, dejándonos verlos en texto plano.

Lo primero descargar la base de datos de sqlite ya sea directamente desde la web del Challenge o desde aqui.

Utilizando ahora una consulta SQL mencionada en el documento sobre la base de datos recién descargada obtendremos los nombres originales de los ficheros y los nombres utilizados por SVN para almacenarlos en el servidor:
$ sqlite3 wc.db 'select local_relpath, ".svn/pristine/" || substr(checksum,7,2) \
|| "/" || substr(checksum,7) || ".svn-base" as alpha from NODES;'
|
noaccess.php|.svn/pristine/41/4134e0e954d144ed932fd639b5a897f9ad47fff9.svn-base
index.php|.svn/pristine/7d/7d63810b0da679648fc20b4f1c84680ac08ec872.svn-base

Tal como hicimos antes con la base de datos podemos descargar los ficheros desde la web del Challenge o desde mi repositorio personal:

Analizando el código del fichero index.php con la poca idea que tengo de PHP, todo sea dicho, dejo sólo la función encargada de generar la password de un solo uso y le agrego unas lineas más que me permitan obtener la fecha y hora del servidor desde la pagina noaccess.php, tal como mencionan los propios comentarios del código fuente.

Para parsear la página devuelta al solicitar la url correspondiente a noaccess.php utilizo la librería PHP Simple HTML DOM parser, que debe ubicarse en la misma carpeta del script genpass.php, por llamar de alguna forma mi copia/arreglada cutre salchichera.

Si se quiere ejecutar desde Windows deberán descargarse los binarios de PHP, desempaquetarlos en un directorio ubicando allí tambien el script y la libreria. Por último renombraremos el fichero php.ini-production a php.ini y eliminaremos el comentario para que la línea (878 en mi caso utilizando php-5.4.10-nts-Win32-VC9-x86.zip) quede tal que así:
extension=php_mbstring.dll

Ahora sólo quedará ejecutarlo para obtener una contraseña de acceso al último nivel válida durante unos 3 minutos:
$ php genpass.php

6bb43caffe5a192ccd1c9f7522b0b8dfe18bf144

Así que si la introducimos en el formulario y pulsamos sobre Authenticate llegaremos al último nivel.

Snow Miser Zone 5

Ya está, ya hemos llegado al final, ya podemos buscar que no aparece ningun desafío más:


así que obtenemos el flag:
<!-- The flag for this level is 3ab1c5fa327343721bc798f116be8dc6 -->

y a buscarnos otro "wargame" :)

2 comentarios:

Manuel dijo...

Me he quedado impresionado con la claridad de tus explicaciones.

Gracias por compartir el proceso tan bien explicado.

neofito dijo...

Me alegra que te haya gustado :-)

Saludos