martes, septiembre 10, 2024

Cómo crear un volcado de procesos en GNU/Linux y buscar "leaks" de información

Hacía tiempo que no pasaba por aquí para poder escribir sobre inquietudes tecnológicas o pequeñas pruebas de concepto que pueden surgir en el día a día. Todos conocemos diferentes herramientas de volcado de procesos de memoria. Por ejemplo, Volatility, que es una de mis favoritas por todo lo que ofrece y aporta en el análisis de un volcado de memoria.

Figura 1: Cómo crear un volcado de procesos en GNU/Linux
y buscar "leaks" de información con Volatility

Volatility no es la única herramienta que se tiene, se tienen otras muchas que aportan en mayor o menor medida diferentes posibilidades, otra opción es gcore. Revisando un poco lo que ofrece /proc en los sistemas GNU/Linux se pueden sacar muchas cosas interesantes. Entre ellas, se puede leer directamente de la memoria de un proceso. Casi como si fuera un fichero. No exactamente como en un fichero, pero le estuve dando vueltas. Al final es una forma sencilla de poder leer qué información hay dentro de un proceso.

Figura 2: Linux Exploiting

Se sabe que, en un proceso, hay elementos que están compartidos con otros procesos. Se sabe que hay páginas de memoria que tienen ciertos permisos y otras otros. Aquí es donde hablamos de lectura, escritura, ejecución. Decidí revisar más sobre este tema y preparar una pequeña prueba de concepto con Bash (un ejemplo de lo que se puede hacer con este lenguaje en GNU/Linux).

Gestión del memoria en sistemas GNU/Linux   

Antes de hablar de la prueba de concepto, vamos a pararos a estudiar un poco sobre /proc y lo que ofrece en términos de información sobre los procesos. Sabemos que /proc es un directorio especial, ya que almacena información sobre elementos volátiles (como es la memoria). Lo primero que nos puede llamar la atención es que si hago un ls sobre el directorio podremos visualizar un montón de “carpetas” con números. Estas carpetas son el ID de los procesos que hay en ejecución en la máquina.

Figura 3: /proc en GNU/Linux

¿Qué información se puede sacar de la memoria de un proceso? Cualquier cosa que esté ahí y no esté protegida (y mucha no lo estará). Para esta pequeña prueba de concepto vamos a abrir un mousepad (el bloc de notas al uso en GNU/Linux) y vamos a introducir algo de texto. Todo lo que se escribe debe estar en memoria. De eso no hay duda. Hay que recordar que la memoria es un elemento volátil, por lo que puede pasar que esté, pero con el tiempo no lo esté. En este caso, el proceso del mousepad recibe el PID 2477 por parte del sistema operativo. Vamos a acceder al directorio de /proc/2477, que es el que corresponde al proceso que hemos ejecutado.

Figura 4: /Proc/2477

Aquí encontramos bastante información interesante sobre el proceso, que nos daría para otro artículo, pero vamos a centrarnos en mem y en maps. El fichero mem nos dará acceso directo a la lectura del proceso en memoria, en crudo. Por otro lado, el fichero maps almacena todas las direcciones de lo que hay cargado en memoria sobre el proceso y nos indica las direcciones base y el offset de las páginas. Vamos a revisar un poco el fichero maps:

Figura 5: Cat maps

En este fichero, se puede ver en primer lugar la dirección de memoria dónde comienza y después del guión la dirección dónde finaliza. Esto es un ejemplo, no es todo lo que tiene en memoria el proceso mousepad que hemos ejecutado. Se puede ver dónde comienza la zona de heap (más abajo veríamos la stack), alguna librería cargada, etcétera. Se pueden ver fácilmente los permisos que existen y si son elementos compartidos. 
Este fichero da mucho juego si quieres recorrer la memoria de un proceso buscando información en diferentes zonas de la memoria. Bien, ahora vamos a ver el fichero mem. Si hacemos un cat mem, esto no va a funcionar, ya que es algo especial (y muy cambiable). Si se quiere leer de este fichero, se debe utilizar la herramienta dd para poder hacer una copia de un flujo de bytes. Para ello, se debe ejecutar la siguiente instrucción:

dd if=/proc/PID/mem bs=BLOCK_SIZE skip=$((DIRECCION_BASE))

 count=$((DIRECCION_TOPE – DIRECCON_BASE))

Con esto, copiamos desde la dirección que se le diga (saltamos a esa posición en memoria) y leeremos lo que indique count, que será la resta entre el tope de la página y el inicio. Si probamos a ejecutarlo, esto devolverá un gran flujo de bytes, la mayoría binario que no se podrá imprimir (ni leer). Aquí es donde empezará a tener sentido utilizar un binario como strings para poder filtrar entre lo que es imprimible y lo que no.

Figura 7: Volcado e impresión de bytes

Si aplicamos a esta salida el comando strings, la cosa cambiará mucho, tal y como se puede ver en la imagen, ya que eliminamos lo que no podemos entender de primeras y dejaremos lo que se puede analizar (strings).

Figura 8:  Strings

Para automatizar un poco todo, ya que recorrer el fichero maps a mano puede ser eterno, debido a todas las librerías y zonas de memoria que hay que recorrer en un proceso, pensé en hacer un pequeño script en Bash a modo de prueba (se puede crear una herramienta fácilmente, aunque ya hay algunas escritas en Python bastante interesantes).
El pseudocódigo del script es el siguiente:

Mientras haya líneas que leer en el fichero maps del proceso PID
Hacer
Obtengo dirección base
Obtengo dirección tope
Ejecuto dd para leer de memoria saltando a dirección base y con un count de la resta de tope y base
Aplico filtro de strings
Aplico filtro de elementos que estoy buscando
Fin_Hacer

Para verlo codificado en Bash os dejo la imagen. Es una PoC, muy PoC, ya que ni el PID se le pasa como argumento al script, ni hay un control mínimo de argumentos, pero queda claro que se puede automatizar.

Figura 10:  PoC en Bash

Puede ser interesante detener un proceso antes de hacer la lectura (no se ha dicho, pero lógicamente podrás leer procesos sobre los que tienes privilegios) de la memoria. Para esto se puede ejecutar un kill -SIGSTOP PID, mientras que para volver a ponerlo en marcha hay que ejecutar kill -SIGCONT PID. Hay un detalle en el script y es que hay que ponerle el “0x” a las direcciones que se leen desde el fichero maps, ya que se operará con ellas y debemos convertirlas en hexadecimal. 
En la imagen, se puede ver un ejemplo a mano de la búsqueda de la palabra “user:”. Con el script automatizamos este proceso y logramos buscarlo en cada zona o, incluso, para todos los procesos sobre los que tenemos privilegio.

Figura 12:  Búsqueda a mano de "user"

Con esto, llegamos al final del artículo. Lo mejor es poder entender cómo funciona la memoria y cómo poder leer sobre ella en busca de información jugosa en un Ethical Hacking. ¿Sería sencillo incorporarlo a nuestra mochila de pentester? Sí y, además, tiene un valor añadido como herramienta de post-explotación.

No hay comentarios:

Publicar un comentario