domingo, 2 de junio de 2019

Migración a GitHub Pages

Hacía tiempo que quería hacer algunos cambios en el blog, y hoy he encontrado un hueco para hacerlo.

Se han migrado los posts en Inglés a GitHub Pages, pero no los blogs en Español, que quedarán en este blog a modo de consulta.

Los nuevos posts se pondrán solamente en Inglés (en principio) y en el dominio habitual https://www.pentester.es

martes, 14 de noviembre de 2017

Undo Five/Nine (Crypto 300, Lisbon CTF)

La semana pasada tuve la oportunidad de participar en la parte presencial de CTF de las Bsides de Lisboa. Hice equipo con algunos compañeros e intentamos resolver algunas de las pruebas.

Uno de los retos en los que estuve trabajando fue "Crypto 300: Undo Five/Nine". No anoté el enunciado del reto, pero basicamente te proporcionaban un trozo de código PHP "snip.php" y otros dos ficheros: "readme.txt" y "readme.txt.fsociety".

Un vistazo rápido a "snip.php" nos ayuda a entender como los otros dos ficheros son utilizados o generados:

$crypted = fopen($file . ".fsociety", "w");
$fp = fopen($file, "r+");
$clear = fread($fp, 2048);
// destroy original file
destroy_file($fp,strlen($clear));

// generate unique key
$key = gen_aes_key();
$aes = new Crypt_AES(CRYPT_AES_MODE_ECB);
$aes->setKeyLength(128);
$aes->setKey($key);

// create encrypted file
$clear = $aes->encrypt($clear);
fwrite($crypted,$clear,strlen($clear));

Como podemos ver, este código PHP lee un texto de "readme.txt" y posteriormente destruye el fichero de algún modo. Seguidamente una clave de cifrado es generada y el texto es cifrado usando AES-128 en modo ECB. El texto cifrado es guardado en el fichero "readme.txt.fsociety".

Por lo que parece, deberíamos ser capaces de recuperar el texto original de algún modo. Como la clave no es almacenada, es evidente que nos encontramos con algún tipo de debilidad en la generación de la clave. Echemos un vistazo:

function gen_aes_key() {
 $key = "";
 for ($i = 0;$i < 16;$i++)
   $key.= chr(mt_rand(0, 255));
 return $key;
}

Bueno, esto encaja con lo que pensabamos. La función "mt_rand" genera un valor aleatorio empleando el generador de números "Mersenne Twister". Esta función, tal y como su documentación avisa, no es criptograficamente segura. Después de "googlear" un poco, encontré más información sobre esta deficiencia, de donde se puede leer la siguiente información:

"Common misuses of mt_rand() include generation of anti-CSRF tokens, custom session tokens (not relying on PHP's builtin sessions support, which uses a different PRNG yet was also vulnerable until recently), password reset tokens, passwords, database backup filenames, etc. If one of these items is exposed and another is generated later without the web application or server reseeding the PRNG, then an attack is possible where the seed is cracked from the item generated earlier and is then used to infer the unknown item generated later."

Por lo que dice, según parece debería haber al menos otra llamada a "mt_rand" y deberíamos ser capaces de averiguar su resultado para ser capaces de explotar esta vulnerabilidad. Peguémosle un vistazo al código que define como se destruye el fichero original:

function destroy_file($fp,$len) {
  $random = "";
  for ($i = 0;$i < $len;$i++)
    $random.= chr(mt_rand(0, 255));
  fseek($fp, 0);
  fwrite($fp, substr($random, 0, $len));
  fclose($fp);
}

¡Bingo! El fichero es sobreescrito con valores "aleatorios" generados con la misma función, lo cual quiere decir que si somos capaces de obtener la semilla, podríamos regenerar toda la secuencia de números a partir de esta, y obtener la clave de cifrado.

¡Vamos a ello! Estuve leyendo la documentación del seed cracker, pero no resultó tan fácil como inicialmente pensé. Esta herramienta tiene varios modos de operación y no resulta obvio elegir los parámetros que necesitas. Finalmente, entendí que debía construir el comando de la siguiente forma:

./php_mt_seed [first_num] [first_num] 0 255 [second_num] [second_num] 0 255 ...

Pero como tenía unos cuantos bytes (los del fichero "readme.txt"), decidí generar los parámetros empleando algunas lineas de código PHP:

$fp = fopen("readme.txt", "r+");
$clear = str_split( fread($fp, 2048) );
foreach ($clear as $v) {
    echo ord($v) . ' ' . ord($v) . " 0 255 ";
}

Este código generó los parámetros que necesitaba, con los que lancé el seed cracker. Pasados un minuto o dos, obtuve la respuesta: "844114388". Ahora necesitamos generar toda la secuencia a partir de esta semilla. Para ello podemos usar de nuevo unas pocas lineas de código PHP:

mt_srand(844114388);
for ($i = 0;$i < 64;$i++)
    echo chr(mt_rand(0, 255));

Cuando generamos esta secuencia, podemos observar que los primeros valores coinciden con los valores contenidos en "readme.txt". Después de esos bytes podemos observar la clave de cifrado (16 bytes).

$ php gen.php  | xxd
00000000: 7b36 0ee9 f9b9 1cfe d0bb d0e6 1311 5828  {6............X(
00000010: fcfe 84a6 7453 03f6 85b6 e270 76c3 41f8  ....tS.....pv.A.
00000020: aec4 9ca5 f658 dda4 20f2 1c9f 5d14 b5b1  .....X.. ...]...
00000030: beb5 1669 3135 31f9 30bc 9438 d0ac d0d6  ...i151.0..8....

Ya solo nos queda descifrar el fichero cifrado:

$ openssl enc -aes-128-ecb -d -K "f658dda420f21c9f5d14b5b1beb51669" -in readme.txt.fsociety
flag{the_darkarmy_is_now_on_to_you}

¡Bang! ¡Conseguido! Desafortunadamente no fui capaz de puntuar con el flag ¿Por qué? Por dos motivos. El primero porque, por alguna razón, tenía en la cabeza que "snip.php" estaba generando enteros y luego truncandolos a byte, así que estuve cerca de una hora intentando entender el código y modificarlo para ajustarlo a esta circunstancia. El segundo porque el código que estaba escribiendo contra-reloj para descifrar el mensaje me dio un "syntax error" 10 segundos antes de que se acabara el tiempo del CTF, así que ya no pude arreglarlo a tiempo. En cualquier caso, me lo pasé muy bien en el CTF :)

lunes, 19 de diciembre de 2016

Ataques Evil-Maid con Hibernación


He colgado la charla que di en la última RootedCon Valencia sobre la técnica de ataque Evil-Maid explotando el fichero de hibernación de Windows.

Esta técnica no es nueva (ni la he descubierto yo), pero esta poco documentada.

También he hablado de este tipo de ataques, de forma más general en el blog de Areopago21.

En este post voy a centrarme más en la parte práctica.

Resumiendo: Si conseguimos acceso físico a un equipo y este esta encendido (pero bloqueado) o suspendido. Podemos intentar recuperar la información volátil critica (identificadores de sesión, contraseñas en claro, llaves de cifrado) del fichero de hibernación.

Para obtener el fichero de hibernación, tendremos que extraerlo del disco duro. Bien arrancando desde un dispositivo externo (Linux para forense, Hirens bootcd, etc.) o bien sacando el disco duro del equipo. Si el disco está cifrado, se complica la cosa.

Este fichero está en la raíz del disco: c:\hiberfyl.sys. Desde el propio Windows el fichero está oculto por defecto y bloqueado de forma que no puede ser leído.

El fichero de hibernación  nunca se borra, solo se modifica su cabecera cuando ha sido usado para reiniciar. De forma que si el equipo alguna vez se ha hibernado, vamos a tener este fichero ya creado. Sino, tenemos que forzar al equipo a hibernarse.

Esto es posible aunque el equipo este bloqueado, si el usuario tiene activa la opción de hibernación:


Por desgracia, a partir de Windows 7, la opción de hibernación viene deshabilitada por defecto. Aunque algunos fabricantes de portátiles la activan.

Por suerte, existe otra forma de forzar el hibernado. Si la batería llega a nivel crítico el equipo se hiberna de forma automática. Configurado por defecto en todas las versiones de Windows incluso en Windows 10.


Una vez tenemos el fichero de hibernación ya podemos trabajar sobre él.

La herramienta básica para esta tarea es Volatility, con ella podemos entre otras cosas:
• Obtener información sobre el fichero de hibernación: vol.exe hibinfo -f hiberfil.sys
• Convertirlo a formato raw: vol.exe imagecopy -f hiberfil.sys -O hiberfil.bin
• Convertirlo a formado DMP (compatible con Windbg): vol.exe raw2dmp -f hiberfil.sys -O hiberfil.dmp
• Obtener datos de navegación: vol.exe iehistory -f hiberfil.sys
• Obtener hashes de contraseñas locales: vol.exe hashdump -f hiberfil.sys
• Obtener llaves de cifrado Truecrypt: vol.exe truecryptpassphrase -f hiberfil.sys

Ejemplo de uso de Volatility:


También tenemos múltiples plugins de la comunidad para otras tareas: mimikatz, bitlocker, bitcoin, etc.

Para la conversión también podemos utilizar las herramientas de Matt Suiche (recién actualizadas), antes conocidas como MoonSols Windows Memory Toolkit. Funcionan mejor que Volatility y soportan todas las versiones de Windows hasta Windows 10.

A pesar de que tenemos un plugin de Mimikatz para Volatility es bastante limitado así que es mucho mejor trabajar directamente con Mimikatz. Para ello tenemos que:
• Convertir el fichero hiberfil.sys a un formato manejable por Windbg (DMP):
  o vol.exe raw2dmp -f hiberfil.sys -O hiberfil.dmp –profile=Win7SP0x64
• Cargar el DMP en Windbg:
  o .symfix => Configura los repositorios de símbolos de Microsoft
  o .reload => Recarga los símbolos necesarios
  o .load wow64exts => Carga el modulo para hacer debug de procesos WOW64
  o !wow64exts.sw => Activa las extensiones WOW64
• Cargar el módulo de Mimikatz en Windbg:
  o .load c:\Users\rpinuaga\Desktop\bad-hibernation\demo\mimilib64.dll => Carga el módulo de Mimikatz
  o !process 0 0 lsass.exe => Busca el proceso de lsass (Local Security Authority Subsystem Service)
  o .process /r /p fffffa800424e910 => Configura el contexto al proceso de lsass
  o !mimikatz

Y listo, aquí tenemos el resultado:


Nota: Volatility solo soporta ficheros de hibernación hasta Windows 7 (A partir de Windows 8, su formato cambia un poco). La nueva herramienta de Matt Suiche en teoría si lo permite, pero la última vez que probé el fichero resultante de la conversión tampoco era reconocido por Volatility.