Latest Tweets

Una al Mes Mision 004: Write-Up

Introducción

El cuarto reto de Una al Mes está dividido en 2 partes. Durante la primera parte debemos ser capaces de acceder a un servicio web sin conocer ninguna credencial. Una vez resuelta esta primera parte, podremos descargar un ejecutable Windows para X86_64 que nos devolverá la FLAG.

Primera parte: “esto va de galletas

Accedemos a la URL del reto (http://34.253.233.243/inicio.php) y nos aparece el mensaje de error siguiente:

Mensaje de error durante el acceso a la URL del reto.

El mensaje de error podría indicar que el código del servidor comprueba el campo “User-Agent” enviado por el navegador del cliente. Sin embargo, esto es una falsa pista. Si miramos el código fuente de la página, observamos el siguiente código JavaScript:

 $(document).ready(function(){
    $('#b-login').click(function(){
      $.post('login.php', {"username": $('#fieldUser').val(), "password": $('#fieldPassword').val()}, function(val, status){
        if (val == "ok"){
          location.reload();
        }
        else{
          $("#error").show();
        }
      });

Ya hemos identificado, pues, la página de login (http://34.253.233.243/login.php). Tal y como se ve en el código anterior, la página recibe simplemente 2 parámetros, “username” y “password”. Aquí es donde podríamos pensar que se trata de un ejercicio de fuerza bruta, lanzando peticiones HTTP POST contra login.php hasta que logremos entrar como usuario “fry”. De nuevo, estamos equivocados. La función en JS obtenerCookie(), de la misma página, nos da una pista de que esto va de galletas:

 function obtenerCookie(clave) {
      var name = clave + "=";
      var ca = document.cookie.split(';');
      for(var i=0; i<ca.length; i++) {
          var c = ca[i];
          while (c.charAt(0)==' ') c = c.substring(1);
          if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
      }
      return "";
    }

Ejecutamos Burp, hacemos una petición a inicio.php, y después enviamos dicha petición al módulo Repeater. Modificamos la URI por “login.php” y el método por “POST”. Finalmente añadimos el payload (username=test&password=test). Aunque no sabemos si la clave será idéntica al usuario, por probar que no sea:

Hacemos un POST contra login.php y el servidor nos deveuleve una cookie de sesión.

El servidor nos devuelve una cookie de sesión:

token=qTImqPNtVPNtVN%3D%3D;

Esto cada vez parece más un clásico CTF donde, para poder seguir, debemos ser capaces de “cocinar” la cookie de sesión y suplantar al usuario “fry”. Usamos un gestor de cookies en nuestro Firefox y añadimos esta cookie de sesión. Después recargamos la página “inicio.php”. Esta vez, se nos saluda:

Acceso como usuario “test” en “inicio.php” con la cookie de sesión.

Por supuesto, para poder avanzar debemos entender como está generada la cookie. La función en JS “obtenerCookie()” mostrada unas líneas más arriba es la clave. Fijémonos que dicha función itera entre todas las posibles cookies y, por cada una elimina los espacios en blanco que puedan encontrarse al principio de la cookie (ejecuta un ltrim()). Finalmente, busca el valor pasado como parámetro (test en este caso) al principio de dicha cookie, y si lo encuentra retorna el valor después de dicha cadena. Por el valor codificado en base64 de la cookie, sabemos que la longitud en texto claro ASCII de la cookie debe ser de 10 bytes exactos.

Probamos de codificar en base64 la cadena ”      test”:

echo ” test”|tr -d ‘\n’|base64
ICAgICAgdGVzdA==

Fijémonos que ICAgI podría ser substituido por qTImq, pero luego no tenemos dos patrones que se repitan en la cookie: CAgd != GVzd. Por el formato de la cookie obtenida, parecería que hay algún algoritmo de substitución que cifra el código base64 de la cookie.

Probamos ahora de codificar la cadena “test      “ en base64:

echo “test      “|tr -d ‘\n’|base64
dGVzdCAgICAgIA==

Aquí tenemos una posible pista sobre el cifrado utilizado por la cookie. Fijémonos que dGVzd se traduce en qTImq en la cookie original; así vemos que d=>q; G=>T; V=>I; z=>m; d=>q …, y entre caracter y caracter tenemos una rotación de 13 caracteres. Además, tenemos los dos patrones claros CAgiCAgI. Así pues, probamos de cifrar nuestra cadena base64 usando ROT13:

Hemos descubierto la receta secreta.

Ahora que ya sabemos como “cocinar” nuestra cookie, cambiamos “test” por “fry”, añadiendo un espacio en blanco más (fry + 7 espacios en blanco). Codifcamos en base64 y ciframos usando ROT13:

echo “fry       “|tr -d ‘\n’|base64
ZnJ5ICAgICAgIA==

El resultado es la cookie que mostramos a continuación:

Nuestra cookie acabada de salir del horno.

Sólo nos queda modificar la cookie en nuestro navegador por esta otra y recargar la página “inicio.php”. Esta vez obtenemos una URL con el archivo a descargar, segunda parte del reto:

Primera parte superada.

Segunda parte: “r2 es como el algodón, no engaña”

Descomprimimos el ZIP descargado; tenemos un ejecutable (test.exe) y un archivo que, probablemente, sea la FLAG cifrada: secretcode. Vemos que el ejecutable es un Windows de 64 bits para “consola”:

file test.exe
test.exe: PE32+ executable (console) x86-64, for MS Windows

Antes de ejecutar el binario, abrimos radare2 para analizarlo:

r2 -A test.exe

Nos situamos en la función main y observamos su código desensamblado:

Dentro de la función main de test.exe.

Aparte de comprobar si hay un depurador presente (jojojo, yo ni he picado porque he ido directo a leer el código desensamblado y no lo he llegado ni a depurar ;-)), vemos que hay una llamada a la función “check” antes de ejecutar el retorno y salir. Vamos a esa función y nos fijamos en su flujo de ejecución gracias al modo visual de r2:

El control path del programa que nos descifrará “secretcode”.

Parece evidente que el control path del programa debería acabar en la llamada a decrypt() para poder descifrar el archivo “secretcode“. Para llegar a dicha función, debemos pasar por la reproducción del sonido en la función “cool_melody()”. La flag la podemos ahora obtener de tres maneras diferentes:

  1. Ejecutamos el binario en modo emulación (ESIL) para que se ejecute la función decrypt sin tener ni que pasar por la ejecución del resto del programa.
  2. Analizamos la función decrypt() con r2 y, a partir de lo que parece la clave “tvB3Cj4iNxw4rjMNxhmX3DaXAuMG2e”, desciframos la FLAG.
  3. Seremos buenos, y analizaremos con r2 como llegar a la llamada de la función “cool_melody” y, desde allí, a “decrypt”.

 Para llegar a “cool_melody” basta con introducir en el parámetro pedido (número de pista) el valor 101 en decimal (0x65):

Si introducimos 101 como track, seguimos la ejecución y caemos en “cool_melody()”

La ejecución del programa seguirá hasta reproducir el “cool_melody” y nos hará una pregunta que es fácil de contestar porque la respuesta está hardcodeada en ASCII en el propio ejecutable “futurama“:

La respuesta a la pregunta está hardcodeada en el propio binario.

Así pues, sabemos que debemos escribir 101 como primer parámetro, y “futurama” como segundo en el programa. Esto hará que el control path del programa acabe en el OFFSET 0x401b99, abra el archivo “secretode”, y lo descifre con una llamada a decrypt(). El resultado podemos verlo a continuación:

Finalmente, la flag aparece descrifrada ante nuestros ojos.

Finalmente, la FLAG es:

UAM{m4y_th3_f0rc3_b3_w1th_y0u}

Toni Castillo @disbauxes