sábado, 30 de abril de 2011

WriteUp - Desafío 12 - H4ckc0nt3st GSIC 2011

En este desafío nos encontramos ante la siguiente página:

Introduciendo cualquier cosa en el primer campo y dando a “continuar” nos da el mensaje “No es lo que busco…”, mientras que rellenar el segundo campo y pulsar “desvelar” no tiene efecto aparente.

Recargando la página va cambiando la imagen del conejo, que vemos que tiene un nombre con un patrón numérico, por lo que decidimos descargar todas las imágenes y ver a dónde nos llevan.
import urllib2

base='https://10.20.63.1:6666/desafios/12/conejos/rabbit' #01.png

for a in range(1,9):
 fich=open('rabbit0'+str(a)+'.png','wb')
 page=urllib2.urlopen(base+'0'+str(a)+'.png')
 cont=page.read()
 fich.write(cont)
 fich.close()


for a in range(10,100):
 fich=open('rabbit'+str(a)+'.png','wb')
 page=urllib2.urlopen(base+str(a)+'.png')
 cont=page.read()
 fich.write(cont)
 fich.close()

De esta forma llegamos hasta el conejo 40. Mi script cutre no trata los errores, así que no ha seguido más allá, por lo que decido probar algún valor más manualmente y enseguida localizo una imagen algo rara en el número 100. Además, mirando alrededor de esta última encontramos también al conejo 99.

Viendo la pinta que tiene rabbit100.png, decidimos mirar su contenido mediante un editor hexadecimal, aunque aquí sólo mostramos la salida ASCII:

Parece que hay algún tipo de script dentro de la paleta de colores de la imagen, ahora sólo falta saber qué hacer con él.

Para ello miramos el código fuente de la página y vemos que los dos botones presentes en la página hacen ejecutan funciones javascript:
  window.onload = function(){
   document.getElementById("gtk").onclick = getKey;
   document.getElementById("dec").onclick = decrypt;
  }
 </script>
 <br/>
 <br/>
 <br/>
 <div class="tCentrado">
  <img width="150px" height="150px" src="./desafios/12/conejos/rabbit03.png"><br/>
  1. sigue al conejo para encontrar la llave de la primera puerta<br/>
  <br/>
  llave: <input type="text" value="" id="source">
  <input id="gtk" type="button" value=" continuar ">
  <input id="key" type="hidden" name="key" value=""><br/>
  <br/>
  2. tras la primera puerta encontrarás una segunda, que se abre con una nueva llave<br/>
  <br/>
  llave: <input type="text" id="dkey" name="dec">
  <input id="dec" type="button" value=" desvelar ">
 </div>

De momento necesitamos superar el primer paso, así que nos fijamos en la función getkey, que coge el dato introducido en el campo de texto y se lo pasa a la función loadData:

function getKey(){
  var source = document.getElementById("source").value;
  loadData(source,function(x){eval(x);estego();});
}

Viendo la definición de esta función parece que el primer parámetro que espera es un nombre de fichero:

function loadData(strFilename, fncCallback)
Como no tenemos ningún fichero más que los propios conejos, introducimos en el cuadro de texto la url del conejo número 100 y al darle a continuar recibimos el mensaje “funciona!”.
Esto tiene buena pinta, y como habíamos visto que el contenido de la imagen recordaba a un script, revisamos los que contiene la página web en el navegador y vemos que se ha añadido uno correctamente:

El script añadido completo es:

var xFakeOne = function (){alert("Lets make the image bigger!");}; estego = function(){document.getElementById("key").value="jjss11";};var z = function whatElseYouExpect(){alert("Let");}
En la función getKey ya habíamos visto que como callback de loadData se ejecutaba estego, y podemos comprobar que ha añadido el valor de la variable correctamente:

Sin más dilación, introducimos “jjss11” en el segundo cuadro de texto y pulsando desvelar superamos el desafío.


Para más información, el botón ‘desvelar’ llamaba a la función decrypt:

function decrypt(){
 var message = "KwLMGF6yZU0iCkCrWAJgmM5hKFvimZao6TWQR15jCbvy86ctBAjnlZ8u5h8idzjcXvpEHmpXz8gwxMMq5QqYWUvAZBN3pq5k1xk9G0KiydDN/v4poUXNRSu2rkLaChAS1MOfiuIx/GrZTEwMp4VoLgLmL5K8sTtiy3U+FQ==";
 var key = document.getElementById("dkey").value;
 var dec = Aes.Ctr.decrypt(message,key,256);
 eval(dec);
 exec();
}


Como se ve, descifra mediante AES-CTR el contenido de ‘message’, lo ejecuta y llama a la función exec. Aunque durante el concurso lo dejamos tras superar el desafío, si alguien tiene curiosidad, el contenido de ‘message’ es el siguiente:

var exec = function(){document.getElementById("respuesta").value="torrijin!";document.forms["formulario"].submit();}
Es decir, completa un formulario que no es visible en la página con el valor “torrijin!” y lo envía al servidor.

Por curiosidad, posteriormente he revisado qué pasaba con rabbit100.png con más detalle: la imagen se carga en un objeto canvas, que nos permite interactuar desde javascript con ella; para generar el script se coge un pixel de cada cuatro de la imagen; cada pixel referencia a un color de la paleta, y al ser una imagen en tonos de grises, cada color se codifica en un byte, en nuestro caso en un carácter. Como las secciones IDAT de un PNG van comprimidas, para verificar que esto cuadraba aproximadamente con lo obtenido, he descomprimido la sección con el siguiente script:

import zlib
import binascii
import re

seccion='08 99 01 D2 00 2D FF 00 01 03 05 07 09 0B 0C 0E 10 12 14 15 16 18 00 19 1B 1D 1E 20 22 24 26 27 28 2A 2C 2E 2F 00 31 32 33 34 35 37 39 3A 3B 3D 3E 40 41 42 00 43 44 45 47 48 49 4A 4B 4C 4E 4F 50 52 53 00 54 55 56 57 59 5A 5B 5D 5F 60 61 62 63 64 00 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 00 73 74 75 77 78 79 7A 7B 7C 7D 7E 80 81 82 00 83 85 86 87 88 89 8A 8B 8D 8F 91 92 93 94 00 95 96 97 98 99 9A 9C 9D 9E 9F A0 A1 A2 A4 00 A5 A6 A7 A9 AA AB AC AD AE AF B0 B1 B2 B4 00 B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C2 C3 00 C4 C5 C6 C7 C8 C9 CB CC CD CE CF D1 D2 D3 00 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF E0 E1 00 E2 E3 E4 E5 E7 E8 E9 EA EB EC ED EE EF F0 93 19 63 39 03 61 4D 0C'
p=re.compile(' ')
seccionsinespacios=p.sub('',seccion)
seccionbinaria=binascii.unhexlify(seccionsinespacios)
salida=open('salida1.raw','wb')
salida.write(zlib.decompress( seccionbinaria[2:] , -15))
salida.close()

No hay comentarios:

Publicar un comentario