Extrayendo contraseñas en memoria de Truecrypt bajo Windows

Truecrypt actua como software de cifrado "al vuelo" (en inglés “on the fly” encryption, en adelante OTFE). Esto quiere decir que de una forma transparente se descifran los datos cuando se requieren del disco y se cifran antes de escribirse en el disco. Para ello y a grandes rasgos, una vez que se monta un volumen con Truecrypt, la clave simétrica (contraseña o keyfile) se transfiere al filtro criptográfico que la almacena en memoria y genera claves derivadas (header key y master key) para automatizar este proceso de cifrado/descifrado. Si no lo hiciera, el usuario tendría que escribir manualmente la contraseña cada vez que accediera al volumen cifrado y por lo tanto su uso no sería demasiado versátil.


Sin embargo y dada su naturaleza se dice que el OTFE (incluido Truecrypt) sólo protege los datos cifrados “en descanso”. Es decir, una vez que un volumen cifrado se monta permanecerá accesible a los usuarios del sistema como si de texto plano se tratara hasta que se desmonte dicho volumen o se apague el sistema. El por qué está claro: en definitiva la contraseña para acceder al volumen cifrado se encuentra en la información volátil de la memoria física (RAM) y, si la obtenemos (un técnica forense muy común), nuestro único problema será sólo identificarla. Véamos cómo.

Obteniendo un volcado de la memoria RAM


En nuestro ejemplo utilizaremos un equipo con sistema operativo Windows 7 de 64 bits, 4 gb de memoria RAM y TrueCrypt 7.1a instalado. 


Lo primero que haremos será obtener una foto o adquisición de la memoria física.
Existen varias herramientas que pueden ayudarnos a obtener el volcado. En nuestro caso por sencillez utilizaremos DumpIt de Moonsols que funciona en máquinas en x86 (32-bits) y x64 (64-bits):
D:\Python27\Tools\volatility-2.1>DumpIt.exe
  DumpIt - v1.3.2.20110401 - One click memory memory dumper
  Copyright (c) 2007 - 2011, Matthieu Suiche <http://www.msuiche.net>
  Copyright (c) 2010 - 2011, MoonSols <http://www.moonsols.com>


    Address space size:        5234491392 bytes (   4992 Mb)
    Free space size:         110475653120 bytes ( 105357 Mb)

    * Destination = \??\D:\Python27\Tools\volatility-2.1\W7PTVMOTOS-20130430-125054.raw

    --> Are you sure you want to continue? [y/n] y
    + Processing... Success.

El resultado como podéis comprobar es un fichero en crudo o raw de algo menos de 5 Gbs. Podría parecer que el volcado de memoria obtenido es un conjunto de datos desordenados pero la realidad sin embargo es que está altamente estructurado. Además Truecrypt especifica que los datos sensibles no son almacenados en la memoria paginada (por seguridad y rendimiento), por lo que siempre la contraseña estará en la memoria física.

Para la búsqueda de la contraseña usaremos principalmente Volatility, un framework con una buena colección de herramientas en Python para el análisis y extración de datos de memoria volatil (RAM). Antes de comenzar comprobamos la existencia del proceso TrueCrypt.exe, si bien aviso que de nada os servirá recorrer las strings de un volcado del proceso:

D:\Python27\Tools\volatility-2.1>vol.py -f W7PTVMOTOS-20130430-125054.raw --profile=Win7SP0x64 volshell
Volatile Systems Volatility Framework 2.1
Current context: process System, pid=4, ppid=0 DTB=0x187000
Welcome to volshell! Current memory image is:
file:///D:/Python27/Tools/volatility-2.1/W7PTVMOTOS-20130430-125054.raw
To get help, type 'hh()'

>>> ps()
Name             PID    PPID   Offset
[...]
TrueCrypt.exe    2132   2036   0xfffffa800abbb060
...]
>>>

Reducir el espacio de búsqueda

Una vez que ya tenemos el volcado de memoria, lo siguiente será reducir el espacio de búsqueda de direcciones virtuales para centrarnos sólo en la información útil concerniente a Truecrypt.

Como cualquier otro software de cifrado completo de discos o contenedores, Truecrypt posee componentes en modo usuario y en modo kernel. Los primeros hacen referencia al interfaz mediante el cual el usuario interactua con el programa y los segundos se presentan normalmente como un driver de dispositivo que maneja todas las operaciones de cifrado/descifrado. Como comentaba, el driver esencialmente intercepta las peticiones de lectura y escritura "al vuelo" y se encarga de automatizar el proceso para que el resto de aplicaciones y el sistema operativo acceda al disco como si de un volumen normal se tratara. En consecuencia, podemos asumir que la contraseña estará en el espacio de direcciones de este driver. Por lo tanto, si reducimos el espacio de búsqueda a modo kernel descartaremos más del 50% del total de información a analizar.

Ahora bien, la forma más rápida es centrarse en los datos correctos es mediante la identificación de etiquetas o Pool tagging que debería utilizar el driver si sigue las recomendaciones de Microsoft. En Windows, cualquier dispositivo Truecrypt tiene asociado un bloque de memoria llamado DEVICE_EXTENSION, que es el espacio que debe tener residente para llevar a cabo todas las operaciones de E/S y que por ende contendrá la clave correspondiente. El puntero hacia la estructura de DEVICE_EXTENSION se encuentra en DEVICE_OBJECT/DRIVER_OBJECT, que es el ejecutable que Windows tiene que localizar para usar el driver.

Mediante el plugin devicetree de Volatitity obtendremos las relaciones del objeto del driver de Truecrypt con su correspondiente dispositivo:

D:\Python27\Tools\volatility-2.1>vol.py -f W7PTVMOTOS-20130430-125054.raw --prof
ile=Win7SP0x64 devicetree > test.txt
Volatile Systems Volatility Framework 2.1

DRV 0x1353e4aa0 \Driver\truecrypt
---| DEV 0xfffffa800abca080 TrueCryptVolumeX FILE_DEVICE_DISK
---| DEV 0xfffffa800af4f080 TrueCryptVolumeW FILE_DEVICE_DISK
---| DEV 0xfffffa8006946e30 TrueCrypt FILE_DEVICE_UNKNOWN

Como véis, el offset del driver de Truecrypt es 0x1353e4aa0 y mediante IoCreateDevice ha creado varios objetos de dispositivo de tipo FILE_DEVICE_DISK (particiones de disco) con sus direcciones de memoria virtuales correspondientes.
En nuestro ejemplo nos centraremos en obtener la clave del volumen que se montó con la letra X. Dentro de volshell, exploraremos con dt (Display Type) los type information de los objetos del dispositivo para conocer su estructura de datos:

>>> dt("_DEVICE_OBJECT", 0xfffffa800abca080)
[_DEVICE_OBJECT _DEVICE_OBJECT] @ 0xFFFFFA800ABCA080
0x0   : Type                           3
0x2   : Size                           1840
0x4   : ReferenceCount                 67
0x8   : DriverObject                   18446738026506635936
0x10  : NextDevice                     18446738026579423360
0x18  : AttachedDevice                 0
0x20  : CurrentIrp                     0
0x28  : Timer                          0
0x30  : Flags                          80
0x34  : Characteristics                256
0x38  : Vpb                            18446738026463075744
0x40  : DeviceExtension                18446738026575733200
0x48  : DeviceType                     7
0x4c  : StackSize                      7
0x50  : Queue                          18446738026575732944
0x98  : AlignmentRequirement           0
0xa0  : DeviceQueue                    18446738026575733024
0xc8  : Dpc                            18446738026575733064
0x108 : ActiveThreadCount              0
0x110 : SecurityDescriptor             18446735964904258368
0x118 : DeviceLock                     18446738026575733144
0x130 : SectorSize                     512
0x132 : Spare1                         0
0x138 : DeviceObjectExtension          18446738026575734704
0x140 : Reserved                       0
>>>

El resultado nos muestra el DeviceExtension del dispositivo, así que mostraremos el espacio de direcciones en hexadecimal con db(address, length=128, width=16, space=None):

>>> db(18446738026575733200, 1600)
[...]
0xfffffa800abca460  00 f0 52 e1 01 00 00 00 00 10 00 00 00 00 00 00   ..R.............
0xfffffa800abca470  00 00 53 e1 01 00 00 00 00 10 00 00 00 00 00 00   ..S.............
0xfffffa800abca480  00 c0 da 0a 80 fa ff ff 00 00 00 40 06 00 00 00   ...........@....
0xfffffa800abca490  00 00 00 00 00 00 00 00 01 00 06 00 00 00 00 00   ................
0xfffffa800abca4a0  a0 a4 bc 0a 80 fa ff ff a0 a4 bc 0a 80 fa ff ff   ................
0xfffffa800abca4b0  00 00 00 00 00 00 00 00 01 00 06 00 01 00 00 00   ................
0xfffffa800abca4c0  c0 a4 bc 0a 80 fa ff ff c0 a4 bc 0a 80 fa ff ff   ................
0xfffffa800abca4d0  00 32 56 05 00 00 00 00 00 2c 02 0a 00 00 00 00   .2V......,......
0xfffffa800abca4e0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xfffffa800abca4f0  00 00 00 00 00 00 00 00 01 00 06 00 00 00 00 00   ................
0xfffffa800abca500  00 a5 bc 0a 80 fa ff ff 00 a5 bc 0a 80 fa ff ff   ................
0xfffffa800abca510  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xfffffa800abca520  01 00 00 00 00 00 00 00 5c 00 3f 00 3f 00 5c 00   ........\.?.?.\.
0xfffffa800abca530  46 00 3a 00 5c 00 4d 00 69 00 6f 00 5c 00 63 00   F.:.\.M.i.o.\.c.
0xfffffa800abca540  69 00 66 00 72 00 61 00 64 00 6f 00 32 00 2e 00   i.f.r.a.d.o.2...
0xfffffa800abca550  74 00 63 00 00 00 00 00 00 00 00 00 00 00 00 00   t.c.............
0xfffffa800abca560  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xfffffa800abca570  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xfffffa800abca580  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
[...]

Como veis, al comenzar a explorar la memoria desde la dirección del DeviceExtension (18446738026575733200) ya obtenemos un dato muy útil: el contenedor del volumen de Truecrypt es F:\Mio\cifrado2. 


Ahora bien, si queremos obtener la contraseña para descifrar este contenedor mediante una revisión manual resultará prácticamente imposible dada la cantidad de datos que manejamos, así que tendremos que volcar todo el espacio ejecutable del driver de Truecrypt y realizar posteriormente una búsqueda basada en formato.
La forma fácil para encontrar donde "vive" el driver de Truecrypt es mediante el módulo driverscan de Volatility, sin embargo, podemos utilizar el DriverObject (18446738026506635936) mostrado anteriormente:

>>> dt("_DRIVER_OBJECT", 18446738026506635936)
[_DRIVER_OBJECT _DRIVER_OBJECT] @ 0xFFFFFA80069E4AA0
0x0   : Type                           4
0x2   : Size                           336
0x8   : DeviceObject                   18446738026575732864
0x10  : Flags                          18
0x18  : DriverStart                    18446735827444617216
0x20  : DriverSize                     266240
0x28  : DriverSection                  18446738026505988352
0x30  : DriverExtension                18446738026506636272
0x38  : DriverName                     \Driver\truecrypt
0x48  : HardwareDatabase               18446735277674784088
0x50  : FastIoDispatch                 0
0x58  : DriverInit                     18446735827444871912
0x60  : DriverStartIo                  0
0x68  : DriverUnload                   18446735827444650112
0x70  : MajorFunction                  -

Y ahora que conocemos donde empieza exactamente el driver y cual es su tamaño podemos volcarlo en hexadecimal para centrar nuestro análisis:

>>> db(18446735827444617216, 266240)

Current context: process System, pid=4, ppid=0 DTB=0x187000
>>> Memory unreadable at fffff880044ed000
>>> 

Realmente el mensaje devuelto significa que algunos bytes no son legibles. Este error suele ocurrir cuando existen datos del objeto paginados pero, como hemos comentado, Truecrypt no pagina nada, por lo que el problema tiene que deberse a algún bug en el proceso de adquisición de la memoria. No obstante si ajustamos los límites podremos volcar correctamente la información que necesitamos:

echo "db(0xfffff880044ed000, 0x3c7ff)" | python vol.py -f W7PTVMOTOS-20130430-125054.raw --profile=Win7SP0x64 volshell > volcado.txt

Búsqueda de la contraseña basada en formato

Analizando el código fuente de Truecrypt, concretamente el fichero de cabecera Password.h, la contraseña debe tener el siguiente formato:

     typedef struct
    {
          // Modifying this structure can introduce incompatibility with previous versions
          unsigned __int32 Length;
          unsigned char Text[MAX_PASSWORD + 1];
          char Pad[3]; // keep 64-bit alignment
    } Password;

Esta estructura fue introducida en la versión 4.0 y posteriormente en la versión 5.0 se añadió el padding. Lo que deberíamos buscar entonces es una contraseña con valor de longitud (un valor entre 1 y 64 almacenado en el primero de los cuatro bytes), 65 bytes de datos de contraseña y luego 3 bytes de relleno o padding para mantener la alineación de 64 bits. Los datos deben contener exactamente los caracteres ASCII de la contraseña y el resto de bytes restantes deben ser ceros exceptuando el padding que tiene un valor aleatorio.

Una opción podría ser recorrer el volcado en hexadecimal mediante un editor de texto normal tipo Notepad++ y por ejemplo buscar patrones "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00". En mi caso existen 950 ocurrencias y es fácil encontrar la contraseña, o mejor dicho las contraseñas en plural porque de golpe podemos obtener las contraseñas de los dos volumenes cifrados que estaban montados en el equipo en el momento de la adquisión de memoria:
 
La otra opción sería escribir un script que busque ese patrón (en hexadecimal o en raw) o no reinvertar la rueda y utilizar alguna herramienta ya hecha (llamarme script kiddie) como cryptoscan o incluso Forensic Disk Decryptor de Elcomsoft con la que sin ningún conocimiento y por menos de 300 euros se pueden descifrar volúmenes TrueCrypt y también de BitLocker y PGP.

Conclusiones

La conclusión principal es que, una vez que un volumen cifrado de Truecrypt es montado en el equipo objetivo, la contraseña para descifrarlo queda residente en la memoria RAM y cualquier atacante puede obtenerla "fácilmente" mediante la adquisión de la misma y posterior análisis.

Recordemos que el volcado de memoria puede realizarse directamente o mediante hiberfil.sys si el equipo ha sido hibernado y, sobretodo, que no necesariamente tenemos que estar delante del equipo y validados con un usuario para obtener un volcado de la memoria.

Por ejemplo, exiten ataques DMA a través de Firewire, ExpressCard, Thunderbolt o cualquier puerto de expansión como PCI o PCI-Express que permiten el acceso directo a la memoria evadiendo las restricciones del sistema operativo debido a la especificación OHCI 1394 (spoofeando un dispositivo SBP2). O imaginad un artefacto de malware residente en el equipo que vuelque la memoria de forma oculta al usuario y envíe los datos de vuelta al servidor C&C.

Existen varios vectores de ataque y muchas posibilidades... ¿a qué hora no te sientes tan seguro sólo por tener tus datos en un volumen cifrado? ;)

Referencias:
Cyber Defense Exercise 2013: Extracting cached passphrases in Truecrypt
RAM is Key. Extracting Disk Encryption Keys From Volatile Memory
Volatility Command Reference  
TrueCrypt Volume Format Specification
Truecrypt Encryption Scheme
Truecrypt Header Key Derivation, Salt, and Iteration Count
Updated Cryptoscan for the Volatility Framework

17 comentarios :

  1. Fenomenal, mis respetos lastima que no se nada de programacion, solo de administracion.

    Muchas gracias por ilistraciones.

    ResponderEliminar
  2. Y al desmontarlo, se rompe del cache de memoria o sigue quedando hasta su reinicio?

    Saludos,
    AnonimoK

    ResponderEliminar
  3. anonimok, al desmontarlo (o apagar el equipo) la contraseña ya no reside en memoria y por lo tanto no es posible obtenerla. Saludos!

    ResponderEliminar
  4. ¿Podría quedar la pass de TC en pagefile.sys y obtenerla a posteriori incluso habiendo apagado el PC?
    Saludos !

    ResponderEliminar
  5. teóricamente sí, de hecho es una de las razones por las que TC especifica que la clave simétrica no permanezca nunca en memoria paginada. Saludos!

    ResponderEliminar
  6. Buenos días.

    Buena entrada aunque el titulo me deja con ganas de mas, "Extrayendo contraseñas en memoria de Truecrypt " pero solo esta el procedimiento en windows, que paso con los demás s.o? porque el titulo esta generalizando.

    De resto muy buen aporte :) aquí seguiré leyéndolos como siempre.

    ResponderEliminar
  7. tienes razón @anónimo, he añadido al título "bajo Windows" para especificarlo..

    Saludos!

    ResponderEliminar
  8. Hola!

    Muy bueno en procedimiento lo estoy intentando reproducir en un volcado de Ram que he generado.

    Tengo una duda:

    Cuando dices "ajustamos los límites..." y pones:

    echo "db(0xfffff880044ed000, 0x3c7ff)"..............

    No entiendo de donde sale "0x3c7ff"

    Gracias!!!

    ResponderEliminar
  9. hola @oscar,

    se trata del valor 247807 en hexadecimal y se consigue ajustando los límites a la baja (prueba y error) a partir del valor 266240 (tamaño del driver)

    Saludos,

    ResponderEliminar
  10. Gracias Vicente

    Es que estoy probando de obtener un volcado y no hay forma.

    Yo pongo:
    echo "db(0xaa0ac000, 0x3237f)" | python vol.py -f imagen.raw volshell > volcado.txt

    Pero el fichero volcado me saca:

    Current context: process System, pid=4, ppid=0 DTB=0x2ce000
    >>> 'db(0xaa0ac000, 0x3237f)'
    >>>

    Quizas es un problema al obtener el valor a la baja(prueba y error). He usado una cantidad aleatoria a la baja similar a la tuya....
    Mi tamaño de driver es: 224128

    ResponderEliminar
  11. has probado con distintos tamaños y nada?

    ResponderEliminar
  12. Pues no nada..

    Debe ser algo de la versión .

    Lo he dejado por imposible de momento

    Gracias

    ResponderEliminar
  13. Muy buen artículo. Lamentablemente lo he probado con una imagen de un XPSP3 y un Windows 7 (la primera en VMWARE, la segunda no) y aunque he seguido todos los pasos (incluido la prueba de acierto error para dar con el tamaño a mostrar con el último db tras el error de "memory unredeable" que también se produce en ambas imagenes) en el fichero resultante no encuentro la contraseña del volumen de truecrypt montado para la prueba.

    ResponderEliminar
  14. Y que sucede si la unidad ha sido desmontada y después el equipo ha sido apagado? Ahí ya no se encontraría en la RAM. Como se puede encontrar la clave?

    ResponderEliminar
    Respuestas
    1. en ese caso no se podría encontrar la clave mediante este método

      Eliminar
  15. hey vicente y si yo olvide la contraseña de truecrip que puedo hacer para desistalarlo aunque pierda todo los documentos que tenia?

    ResponderEliminar
    Respuestas
    1. supongo que te refieres a que tienes la partición del sistema entera cifrada.. para descifrarla y desintalar TC necesitarías la password y seguir las instrucciones: http://www.truecrypt.org/docs/removing-encryption. Si no tendrás que formatear..

      Eliminar