Un blog de Seguridad Informática, desde un punto de vista tecnológico

Entradas etiquetadas como “ncn2013 conferencias ctf 2013

Reto 1 del CTF de las No cON Name 2013

Hola a todos, hola a tothom!

Mi nombre es Ignasi M. (ny4nyi) y he tenido el placer de ser invitado a participar en este blog. Es todo un honor. Espero que todo lo que pueda aportar os sea útil y disfrutéis!

Sobretodo, dar las gracias a Julian y a todo el equipo por darme esta oportunidad de poder participar con gente tan grande.

Ahora, a por materia.

El primer reto del CTF NoConName 2013 consistía en una página web que solicita una clave secreta para superar el reto.

Paso 1

La primera aproximación fue probar un número aleatorio para estudiar el comportamiento normal de la página. Obviamente, se produce un error de “Clave incorrecta” y se sigue en la misma página inicial (ya hubiera sido la ostia acertar a la primera ).

Una vez se conoce el comportamiento estándar de la página, se realizó un estudio del código fuente de la misma, y se encuentra lo siguiente:

   <head>

    <title>NcN 2013 Registration Quals</title>

    <link rel=”stylesheet” href=”../res/main.css” type=”text/css” media=”screen”/>

    <link href=’../res/UbuntuMono.css’ rel=’stylesheet’ type=’text/css’>

    <meta content=”Javier Marcos @javutin” name=”author” />

    <script type=”text/javascript” src=”crypto.js”></script>

  </head>

La página carga un “js” de nombre “crypto.js” que es el responsable de realizar la validación. Resaltar que al tratarse de un “js” se ejecuta en el navegador del cliente, no en servidor. Por lo tanto, el siguiente paso a seguir es realizar un estudio del código del “js” y entender su lógica/funcionamiento para identificar la posible manera de evadir o hallar la clave secreta de forma exitosa.

Se accede al archivo “cryto.js” para obtener su código fuente. Es algo tan bonito como esto:

var_0x52ae=[“\x66\x20\x6F\x28\x38\x29\x7B\x63\x20\x69\x2C\x6A\x3D\x30\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x38\x2E\x6C\x3B\x69\x2B\x2B\x29\x7B\x6A\x2B\x3D\x28\x38\x5B\x69\x5D\x2E\x73\x28\x29\x2A\x28\x69\x2B\x31\x29\x29\x7D\x67\x20\x74\x2E\x75\x28\x6A\x29\x25\x76\x7D\x66\x20\x70\x28\x68\x29\x7B\x68\x3D\x68\x2E\x71\x28\x30\x29\x3B\x63\x20\x69\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x77\x3B\x2B\x2B\x69\x29\x7B\x63\x20\x35\x3D\x69\x2E\x78\x28\x79\x29\x3B\x6D\x28\x35\x2E\x6C\x3D\x3D\x31\x29\x35\x3D\x22\x30\x22\x2B\x35\x3B\x35\x3D\x22\x25\x22\x2B\x35\x3B\x35\x3D\x7A\x28\x35\x29\x3B\x6D\x28\x35\x3D\x3D\x68\x29\x41\x7D\x67\x20\x69\x7D\x66\x20\x6E\x28\x38\x29\x7B\x63\x20\x69\x2C\x61\x3D\x30\x2C\x62\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x38\x2E\x6C\x3B\x2B\x2B\x69\x29\x7B\x62\x3D\x70\x28\x38\x2E\x71\x28\x69\x29\x29\x3B\x61\x2B\x3D\x62\x2A\x28\x69\x2B\x31\x29\x7D\x67\x20\x61\x7D\x66\x20\x42\x28\x39\x29\x7B\x63\x20\x32\x3B\x32\x3D\x6E\x28\x39\x2E\x64\x2E\x65\x29\x3B\x32\x3D\x32\x2A\x28\x33\x2B\x31\x2B\x33\x2B\x33\x2B\x37\x29\x3B\x32\x3D\x32\x3E\x3E\x3E\x36\x3B\x32\x3D\x32\x2F\x34\x3B\x32\x3D\x32\x5E\x43\x3B\x6D\x28\x32\x21\x3D\x30\x29\x7B\x72\x28\x27\x44\x20\x64\x21\x27\x29\x7D\x45\x7B\x72\x28\x27\x46\x20\x64\x20\x3A\x29\x27\x29\x7D\x39\x2E\x47\x2E\x65\x3D\x6E\x28\x39\x2E\x64\x2E\x65\x29\x3B\x39\x2E\x48\x2E\x65\x3D\x22\x49\x22\x2B\x6F\x28\x39\x2E\x64\x2E\x65\x29\x3B\x67\x20\x4A\x7D”,”\x7C”,”\x73\x70\x6C\x69\x74″,”\x7C\x7C\x72\x65\x73\x7C\x7C\x7C\x68\x65\x78\x5F\x69\x7C\x7C\x7C\x73\x74\x72\x7C\x66\x6F\x72\x6D\x7C\x7C\x7C\x76\x61\x72\x7C\x70\x61\x73\x73\x77\x6F\x72\x64\x7C\x76\x61\x6C\x75\x65\x7C\x66\x75\x6E\x63\x74\x69\x6F\x6E\x7C\x72\x65\x74\x75\x72\x6E\x7C\x66\x6F\x6F\x7C\x7C\x68\x61\x73\x68\x7C\x66\x6F\x72\x7C\x6C\x65\x6E\x67\x74\x68\x7C\x69\x66\x7C\x6E\x75\x6D\x65\x72\x69\x63\x61\x6C\x5F\x76\x61\x6C\x75\x65\x7C\x73\x69\x6D\x70\x6C\x65\x48\x61\x73\x68\x7C\x61\x73\x63\x69\x69\x5F\x6F\x6E\x65\x7C\x63\x68\x61\x72\x41\x74\x7C\x61\x6C\x65\x72\x74\x7C\x63\x68\x61\x72\x43\x6F\x64\x65\x41\x74\x7C\x4D\x61\x74\x68\x7C\x61\x62\x73\x7C\x33\x31\x33\x33\x37\x7C\x32\x35\x36\x7C\x74\x6F\x53\x74\x72\x69\x6E\x67\x7C\x31\x36\x7C\x75\x6E\x65\x73\x63\x61\x70\x65\x7C\x62\x72\x65\x61\x6B\x7C\x65\x6E\x63\x72\x79\x70\x74\x7C\x34\x31\x35\x33\x7C\x49\x6E\x76\x61\x6C\x69\x64\x7C\x65\x6C\x73\x65\x7C\x43\x6F\x72\x72\x65\x63\x74\x7C\x6B\x65\x79\x7C\x76\x65\x72\x69\x66\x69\x63\x61\x74\x69\x6F\x6E\x7C\x79\x65\x73\x7C\x74\x72\x75\x65″,””,”\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65″,”\x72\x65\x70\x6C\x61\x63\x65″,”\x5C\x77\x2B”,”\x5C\x62″,”\x67″];eval(function (_0x7038x1,_0x7038x2,_0x7038x3,_0x7038x4,_0x7038x5,_0x7038x6){_0x7038x5=function (_0x7038x3){return (_0x7038x3<_0x7038x2?_0x52ae[4]:_0x7038x5(parseInt(_0x7038x3/_0x7038x2)))+((_0x7038x3=_0x7038x3%_0x7038x2)>35?String[_0x52ae[5]](_0x7038x3+29):_0x7038x3.toString(36));};if(!_0x52ae[4][_0x52ae[6]](/^/,String)){while(_0x7038x3-)_0x7038x6[_0x7038x5(_0x7038x3)]=_0x7038x4[_0x7038x3]||_0x7038x5(_0x7038x3);} ;_0x7038x4=[function (_0x7038x5){return _0x7038x6[_0x7038x5];} ];_0x7038x5=function (){return _0x52ae[7];} ;_0x7038x3=1;} ;while(_0x7038x3–){if(_0x7038x4[_0x7038x3]){_0x7038x1=_0x7038x1[_0x52ae[6]]( new RegExp(_0x52ae[8]+_0x7038x5(_0x7038x3)+_0x52ae[8],_0x52ae[9]),_0x7038x4[_0x7038x3]);} ;} ;return _0x7038x1;} (_0x52ae[0],46,46,_0x52ae[3][_0x52ae[2]](_0x52ae[1]),0,{}));

Como se puede ver, el código del “js” esta ofuscado (lastima, habrá que currar más). Ha de quedar claro que esta ofuscado, NO cifrado.

Si se disecciona el código, a grandes rasgos se puede detectar lo siguiente:

  • Una variable de nombre “var_0x52ae” : Parece ser un vector donde el valor de cada posición está representado en código hexadecimal. Recordar que si un valor se escribe como “\x” significa que se representa en sistema hexa.
  • Una función eval: Definición purista, “Si el argumento es una expresión, eval () evalúa la expresión. Si el argumento es una o más sentencias JavaScript, eval () ejecuta las sentencias.”
  • La definición de funciones: Por ejemplo, “function (_0x7038x1,…)”
  • La llamada de funciones: Por ejemplo, “(_0x52ae[0],46,46,_0x52ae[3])”

Aclarar que en las definiciones de las funciones se ven nombres de parámetros tal que “_0x_7038x1” pero se desconoce su valor. Esto es porque en una declaración de función se puede dar cualquier nombre al parámetro, que luego éste recibirá el valor del parámetro usado en la llamada. En resumen, cuando se llame a la función, el valor del parámetro será el siguiente:

_0x7038x1=_0x52AE[0]=”\x66…..\7D”.

Paso 2

Para desofuscar el código js se pueden utilizar diferentes recursos online en la red, o simplemente, modificar la función “eval” por una función “alert” que se encargara de imprimir en una ventana la representación del código sin ofuscar, ya que interpretara todos los valores hexadecimales posibles como caracteres.

El resultado es el siguiente:

Imagen

El código sin ofuscar es:

Function simpleHash(str) {

var i, hash = 0;

for (i = 0; i < str.length; i++) {

hash += (str[i].charCodeAt() * (i + 1))

}

return Math.abs(hash) % 31337

}

 function ascii_one(foo) {

foo = foo.charAt(0);

var i;                              

for (i = 0; i < 256; ++i) {

var hex_i = i.toString(16);

if (hex_i.length == 1) hex_i = “0” + hex_i;

hex_i = “%” + hex_i;

hex_i = unescape(hex_i);

if (hex_i == foo) break

}

return i

}

 function numerical_value(str) {

var i, a = 0,

b;

for (i = 0; i < str.length; ++i) {

b = ascii_one(str.charAt(i));

a += b * (i + 1)

}

return a

}

function encrypt(form) {

var res;

res = numerical_value(form.password.value);

res = res * (3 + 1 + 3 + 3 + 7);

res = res >>> 6;

res = res / 4;

res = res ^ 4153;

if (res != 0) {

alert(‘Invalid password!’)

} else {

alert(‘Correct password :)’)

}

form.key.value = numerical_value(form.password.value);

form.verification.value = “yes” + simpleHash(form.password.value);

return true}

Paso 3

La lógica general de este código es la siguiente:

  1. La función “numerical_value” recibe el valor o cadena “X” que introduce el usuario en la página web y retorna un valor resultado que es la suma de cada carácter de la cadena X en su correspondiente valor decimal ASCII multiplicado por el resultado de la suma del valor de la posición del carácter en la cadena X +1
  2. La función “encryp” se encarga primero de aplicar varias operaciones sobre el valor retornado por la función “numerical_value” y después compara este valor “final” con 0. Si la comparación es cierta, el password (cadena “X”) introducida por el usuario es correcta.
  3. Estudiando con más detalle la función “encrypt” se determina que el valor “final” ha de estar entre el siguiente rango de valores :62540-62554.

Ya con esta información clara el último paso es introducir, mediante técnicas de “brute forcing”, una cadena de valores/caracteres que después de ser tratada por la función “numerical_value” de un valor que pertenezca al rango deseado.

En este caso se encontró la siguiente cadena:

ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZNR

Se introdujo esta cadena en la página web y tachan!:

Congrats! you passed the level! Here is the key:

23f8d1cea8d60c5816700892284809a94bd00fe7347645b96a99559749c7b7b8

Twitter: @Ny4nyi

Anuncios

CTF de la No cON Name 2013 (write-up)

Buenas a todos,

después de algún tiempo sin nuevas entradas en el blog, hemos pensado que una buena forma de regresar sería publicando nuestras soluciones a los retos de la ronda clasificatoria del primer CTF que se organiza en las conferencias No cON Name, en este año 2013.

Esta ronda de clasificación ha consistido en la resolución de un juego de estilo “Jeopardy”, consistente en 3 niveles. El primero de ellos relacionado con Web, el segundo con Android y el tercero con reversing de binarios.

Imagen

 

En este post describiremos cómo solucionamos el nivel 3, en el que se entregaba un binario de tipo ELF de 64 bits. Al ejecutarlo mostraba lo siguiente:

De forma que al escribir, el programa finalizaba indicando el siguiente mensaje:

Para resolver el desafío hicimos un desensamblado del binario, y analizamos su comportamiento. En primer lugar el programa imprimía por pantalla el texto que hemos podido ver anteriormente:

Imagen

 

A continuación entraba en un bucle de 10 iteraciones, en el que se van leyendo los caracteres introducidos por el usuario y se comparan con el contenido de la variable “facebookctf_rocks”. Esta variable es un array de DWORDs, en las que el byte más significativo de cada DWORD contiene el valor que va a comparar con el carácter introducido por el usuario, y los otros 3 bytes de la DWORD son ceros.

Imagen

 

El contenido de la variable “facebookctf_rocks” es el siguiente:

20 00 00 00

53 00 00 00

55 00 00 00

52 00 00 00

50 00 00 00

52 00 00 00

49 00 00 00

53 00 00 00

45 00 00 00

21 00 00 00

Y teniendo en cuenta lo explicado anteriormente, se corresponde con la cadena “\x20\x53\x55\x52\x50\x52\x49\x53\x45\x21”, o lo que es lo mismo ” SURPRISE!” (con un espacio al principio). 

Si introducimos esa cadena, el programa muestra el siguiente mensaje:

 

Una forma alternativa de obtener la clave (sin introducir el texto), es analizar la parte final del binario, en la que se aprecian tres caminos diferentes: que se haya presionado la tecla de salida (call seeyaaaa), que se haya introducido un carácter que no es el esperado (call game_over), o que se haya introducido la cadena esperada (call success).

Imagen

 

Por lo que si debugueamos el programa y modificamos el EIP por la dirección de memoria donde se llama a la función “success” (0x000000000040117B), obtendremos el mismo resultado. 

En breve publicaremos la solución de los otros 2 niveles 🙂

Saludos!!