jueves, enero 28, 2016

Cómo proteger tu password cuando esté en memoria RAM

Desde hace un par de semanas estamos jugando con las técnicas de análisis forense y pentesting que tienen que ver con la memoria RAM y los trucos que rodean a este elemento volátil. En los artículos anteriores hemos visto “Cómo sacar la contraseña de Gmail de la memoria del proceso de Firefox usando Metasploit” o “Cómo volcar credenciales del proceso KeePass con Meterpreter”, lo que podría dar a un atacante datos jugosos o indicios que nos ayuden en una auditoría de seguridad. Puede que para muchos esto sea algo normal, pero lo cierto es que los sistemas aún podrían haber estado diseñados de forma mucho más robusta para garantizar la protección de la memoria contra este tipo de ataques, así que hemos hecho una prueba en Eleven Paths para mostraros "Cómo proteger tu password cuando esté en memoria RAM".

Figura 1: Cómo proteger tu password cuando esté en memoria RAM

Para hacer esta prueba vamos a hacer uso de los SecureString. Según indica Microsoft en su MSDN Library, el SecureString es un objeto similar al String - es decir, para almacenar cadenas de texto, pero que se almacena en memoria RAM con un mecanismo de cifrado ofrecido por el propio sistema operativo. Este tipo de objetos que protegen en el contenido en la memoria RAM por medio de cifrado se crearon en los sistemas operativos y lenguajes de programación para que la información sensible como contraseñas o datos personales, utilicen SecureString, mientras que el resto de información podría ser almacenada, sin más, en Strings.

Figura 2: ¿Cómo de seguros son los SecureString en .NET?

Hay que tener en cuenta que la memoria RAM de un proceso puede ser accesible no solo por medio de Metasploit como hemos visto en los casos anteriores, sino que un volcado de memoria producido por un fallo en el sistema operativo - como cuando se produce un BSOD - puede hacer que todos los datos que haya en la RAM terminen en un fichero de texto donde puede quedar toda la información expuesta. En el ejemplo que poníamos de Firefox, lo que quedaba no era directamente la contraseña, pero sí el usuario y la contraseña introducida por el usuario, que probablemente, si es un error de mecanografía, puede llevar a la password.

Un proceso con SecureString y otro con String

Para esta prueba de concepto se ha utilizado un código .NET escrito en C#, que permite ejemplificar dos situaciones: En la primera situación el programa utiliza SecureString para gestionar su clave en memoria RAM. La clave no se imprime por pantalla y si buscamos la información de la contraseña en RAM utilizando Metasploit no debemos encontrar nada. En la segunda situación el usuario gestiona su clave en un tipo de datos String. Cómo ya hemos visto en anteriores artículos, la información será mostrada cuando se haga un dump de memoria.

Figura 3: Funciones de protección de Strings con SecureString

El trozo de código anterior muestra dos funciones, una que permite convertir un String a SecureString y la otra realiza el proceso inverso. Una correcta gestión de los SecureString puede ayudarnos a solventar problemas de data leaks a través de información en la RAM.

Para realizar el volcado de memoria RAM del proceso .NET utilizado, en este caso el que solo gestiona la información con SecureString, se utiliza el script de Meterpreter llamado proc_memdump para obtener el volcado remoto.

Figura 4: Volcado de memoria de un proceso protegido con SecureString

Cómo puede verse en la imagen, si buscamos cadenas como “System.Security” se encuentra Password: System.Security.SecureString.  En el caso de que se utilizará un String ya tendríamos el leak y el valor de la contraseña como veremos en la siguiente prueba, pero con esta configuración, nada de nada.

Figura 5: La cadena de la contraseña está protegida por SecureString

Conociendo la contraseña que hemos almacenado con SecureString realizamos una búsqueda sobre el volcado de memoria a ver si está en algún lugar almacenada la cadena de la password en texto plano. El resultado es el que se puede ver en la imagen, nada. No encontramos información sobre la contraseña en el volcado, se está gestionando de forma correcta y segura.

Figura 6: La cadena de la contraseña no está en volcado de memoria

Ahora vamos a ejecutar el proceso nuevamente pero haciendo que la contraseña se gestione a través de un String, algo muy común. Tal y como se puede ver en la imagen se ejecuta process_memdump y se obtiene el volcado en crudo del proceso.

Figura 7: Si no está protegida, la contraseña está accesible en el volcado

Realizando la búsqueda sobre la captura de la palabra “Pass” podemos encontrar la contraseña en texto plano en la memoria RAM. Esta información ha sido mostrada por pantalla, por lo que se está almacenando de manera insegura en la memoria del proceso. En el siguiente vídeo se puede ver la demostración completa.

Figura 8: Vídeo demostrativo de SecureString

Cómo conclusión decir que una correcta gestión del uso de Strings sensibles y unas buenas pautas a la hora de programar nos ayudan a hacer nuestro desarrollo más seguro y que la fortificación de la aplicación sea mayor en cualquier entorno. En este ejemplo se ha visto con una aplicación en .NET, pero en otros lenguajes existen otras soluciones. En Java, por ejemplo, el tipo de datos para hacer algo similar a esto se llama GuardedString, y dependiendo de la plataforma de desarrollo que uses puedes buscar soluciones similares.

Figura 9: GuardedString en Java

Este tipo de protecciones, por supuesto, no son 100% seguras, pero si se accede al volcado de memoria sin poder utilizar las funciones criptográficas del sistema operativo con las claves de descifrado, obtener las cadenas protegidas es un trabajo de fuerza bruta.

Autor: Pablo González Pérez (@pablogonzalezpe)
Escritor de los libros "Metasploit para Pentesters", "Ethical Hacking" y “Pentesting con Powershell

2 comentarios:

  1. Muy bueno para reforzar las cadenas, no conocía la existencia de esa clase Java

    ResponderEliminar
  2. Quizas moleste un poco el necroposting pero me he encontrado esta entrada por casualidad y tengo que decir que la implementacion del codigo en .NET aqui expuesta es insegura.

    Si partimos de una contraseña en un String, para convertirla a SecureString, estamos almacenando un objeto en el Heap que escapa a nuestro control para eliminarlo a nuestra conveniencia. Igualmente de forma inversa al pasar un SecureString a String.

    Debemos utilizar siempre tipos de datos por valor, no por referencia (que van al heap siempre) y asegurarnos que que esten en el Stack del proceso. Los tipos por valor se almacenan en el stack cuando el valor es una variable local o parametro de entrada y esta no es utilizada en una funciona anonima, una expresion Lambda o esta dentro de un iterador. De esta forma podemos limpiar la variable en cuanto hayamos terminado con ella para minimizar el riesgo de filtrado en un volcado de memoria.

    La estrategia correcta es tener en memoria un solo caracter de la contraseña en el Stack del proceso en cada momento y comparar las credenciales caracter por caracter y al finalizar limpiar la variable y no retornar o aceptar por parametro ningun String con la contraseña en ningun momento. Esto requiere mas codigo no administrado y cargarse pero a base de bien el tener un codigo bonito, desacoplado y con responsabilidad unica, pero es lo que hay.

    PD: En el mejor de los casos, ademas, ha olvidado limpliar la SecureString al final del metodo ConvertToUNSecureString llamando a secstrPassword.Dispose() porque si confia en el GC se puede tirar en memoria (aunque sea cifrada) un buen rato.

    ResponderEliminar