lunes, 23 de febrero de 2009

Cosas que ver en /proc

El sistema de ficheros /proc de Linux no es un sistema de ficheros normal. En su lugar se trata de un sistema de ficheros virtual, de forma que todo su contenido no existe físicamente en el disco, sino que habita únicamente en la memoria RAM del sistema.

A lo largo de las siguientes líneas vamos a analizar su contenido centrándonos básicamente en los ficheros y directorios que puedan ser de utilidad en un proceso de respuesta ante incidentes.

El sistema operativo que he utilizado para todas las pruebas es una Fedora Core 10, la cual dispone de un kernel 2.6.27, pero los resultados no deberían variar considerablemente para otras distribuciones, al menos para las que ejecuten un kernel 2.6.

Breve introducción a /proc

Podemos observar el fichero /etc/fstab para observar la línea de montaje correspondiente a /proc:

proc   /proc proc defaults 0 0

Igualmente, y mediante el uso del comando mount, podemos ver como, efectivamente, el sistema /proc ha sido montado con las opciones anteriores:
/proc on /proc type proc (rw)

El sistema de ficheros /proc no está asociado a ningún dispositivo físico, dado que se trata de un sistema de ficheros virtual que se habilita/deshabilita durante la compilación del kernel de linux.

Podemos comprobar de forma sencilla la característica virtual de /proc si analizamos cualquiera de los ficheros que se encuentran en su interior, dado que todos tienen un tamaño igual a 0. Esto es debido a que no ocupan espacio en disco dado que solo existen en la memoria del kernel, la cual se almacena en la RAM del sistema. Por otra parte las fechas de última modificación se corresponderán con la fecha actual del sistema, lo que atestigua su carácter dinámico.

El sistema de ficheros /proc se diseñó originalmente para proporcionar información sobre los procesos ejecutándose en el equipo. En este momento también se utiliza para obtener información estadística y de configuración del kernel, de forma que determinados parámetros pueden modificarse "on the fly".

Ficheros interesantes

Vamos a analizar algunos ficheros cuyo contenido puede resultar esclarecedor. Para obtener su contenido podemos utilizar los comandos cat, less o more:

/proc/cmdline
Muestra los parámetros que se le han pasado al kernel como opciones durante el arranque del sistema.

/proc/cpuinfo
Muestra información sobre los diferentes procesadores de que dispone el sistema.

/proc/filesystems
Muestra los diferentes tipos de sistemas de ficheros soportados por el kernel. Si en la primera columna aparece nodev implica que no se requiere un dispositivo de bloque para que sea montado, o lo que es lo mismo, se trata de un sistema de ficheros virtual.

/proc/kallsyms
Se trata del reemplazo para el fichero ksyms del kernel 2.4. Muestra un listado con todos los símbolos presentes en el kernel, a diferencia de ksyms que únicamente mostraría los símbolos exportados. La segunda columna muestra el nombre de la función del kernel y la primera muestra la dirección en memoria que ocupa dicha función.

/proc/kcore
Mejor no intentes abrir este fichero dado que te llenará el terminal de carácteres extraños y tendrás que reiniciarlo para volver a la normalidad. A diferencia del resto de ficheros su tamaño no será igual a 0; en su lugar se corresponderá con el tamaño de la memoria RAM del sistema más 4 KB. Este fichero representa el contenido de la memoria física del sistema en formato core, el cual es adecuado para ser analizado con el debugger de Linux, gdb.

/proc/modules
Muestra un listado con todos los módulos cargados por el kernel.

/proc/mounts
Proporciona un listado con todos los "dispositivos" montados en el sistema de forma similar a la salida obtenida con el comando mount.

/proc/partitions
Muestra información sobre las particiones y dispositivos de bloque que las componen. El valor "major version" indica el número asociado al dispositivo que contiene dicha partición, el mismo que tiene asignado en el fichero /proc/devices. El valor "minor version" se corresponde con el número correspondiente a la partición dentro del dispositivo. Por último aparece el nombre asignado a la partición.

Información sobre los procesos en ejecución

Si realizamos un listado del directorio /proc nos encontraremos con muchos directorios cuyo nombre será un dígito o cifra numérica. Cada uno de estos directorios almacena información sobre los procesos en ejecución en el sistema y su nombre se corresponde con el PID asociado a cada uno de estos procesos; el dueño y grupo al que pertenecen estos directorios coinciden con los de la cuenta utilizada para lanzarlo.

Cada uno de los directorios asociados a procesos contienen diferentes ficheros de interés desde el punto de vista del investigador. Analicemos algunos de los más relevantes:

/proc/${PID}/cmdline
Almacena el comando y las opciones utilizadas para lanzar el proceso.

/proc/${PID}/cwd
En realidad no es un fichero con un contenido determinado, sino que se trata de un enlace simbólico al directorio de trabajo actual para el proceso.

/proc/${PID}/environ
Contiene una lista con las variables de entorno para el proceso. Se trata de un fichero con múltiples líneas que no terminan con el clásico carácter de nueva línea, por lo que si queremos analizar su contenido de forma que su salida sea fácilmente interpretable podemos utilizar un comando como el siguiente:
[neofito@ulises ~]$ cat /proc/${PID}/environ | xargs -0 -n 1

/proc/${PID}/exe
Al igual que ocurría con el fichero cwd es en realidad un enlace simbólico al ejecutable utilizado para lanzar el proceso.

/proc/${PID}/fd
Se trata de un directorio que contiene enlaces simbólicos para todos los descriptores de fichero abiertos por el proceso.

Recuperando ejecutables

Vamos a utilizar ahora el directorio /proc y su contenido para recuperar un binario que se encuentra en ejecución pero que ha sido eliminado del sistema de ficheros; ésta es una costumbre habitual para esconder puertas traseras o programas de control remoto.

Para nuestro ejemplo utilizaremos el programa netcat, cuyas fuentes podemos obtener en la siguiente dirección.

Una vez descargado, por ejemplo en /usr/local, procederemos a desempaquetarlo y realizar el proceso de compilación:
[root@ulises ~]# cd /usr/local/
[root@ulises local]# tar xvzf /home/javi/Descargas/netcat-0.7.1.tar.gz
[root@ulises netcat-0.7.1]# cd netcat-0.7.1/
[root@ulises netcat-0.7.1]# ./configure
[root@ulises netcat-0.7.1]# make

Como resultado de la compilación obtendremos el binario de netcat dentro del directorio de trabajo actual, concretamente dentro de src/. Hagamos una prueba y lancemos una shell bash esperando conexiones en el puerto 1234:
[root@ulises netcat-0.7.1]# cd src/
[root@ulises src]# ./netcat -l -p 1234 -e /bin/bash

Podemos comprobar que, efectivamente, se ha abierto una shell en modo escucha. Abramos para ello una nueva terminal y ejecutemos el siguiente comando:
[root@ulises ~]# netstat -anp | grep 1234
tcp 0 0 0.0.0.0:1234 0.0.0.0:* LISTEN 7144/netcat

También podemos comprobar que, efectivamente, se ha creado el directorio correspondiente para el proceso netcat dentro de /proc:
[root@ulises ~]# ls -ld /proc/7144
dr-xr-xr-x 7 root root 0 feb 22 14:34 /proc/7144

Cerremos ahora la terminal desde la que lanzamos el comando netcat, el cual seguirá a la escucha, y procedamos a eliminar el binario desde la otra terminal que tenemos abierta:
[root@ulises ~]# cd /usr/local/netcat-0.7.1/src/
[root@ulises src]# ll
total 392
-rw-r--r-- 1 100 users 30890 ene 3 2004 core.c
-rw-r--r-- 1 root root 30972 feb 22 14:22 core.o
-rw-r--r-- 1 100 users 5619 dic 10 2003 flagset.c
-rw-r--r-- 1 root root 9296 feb 22 14:22 flagset.o
-rw-r--r-- 1 100 users 2208 ene 18 2003 intl.h
-rw-r--r-- 1 root root 11668 feb 22 14:22 Makefile
-rw-r--r-- 1 100 users 1427 ago 28 2003 Makefile.am
-rw-r--r-- 1 100 users 11934 ene 11 2004 Makefile.in
-rw-r--r-- 1 100 users 13321 ene 3 2004 misc.c
-rw-r--r-- 1 100 users 3069 mar 6 2003 misc.h
-rw-r--r-- 1 root root 16296 feb 22 14:22 misc.o
-rwxr-xr-x 1 root root 87888 feb 22 14:22 netcat
-rw-r--r-- 1 100 users 21028 ago 28 2003 netcat.c
-rw-r--r-- 1 100 users 6423 ene 3 2004 netcat.h
-rw-r--r-- 1 root root 26784 feb 22 14:22 netcat.o
-rw-r--r-- 1 100 users 20848 ene 3 2004 network.c
-rw-r--r-- 1 root root 22136 feb 22 14:22 network.o
-rw-r--r-- 1 100 users 3663 ene 3 2004 proto.h
-rw-r--r-- 1 100 users 7555 feb 28 2003 telnet.c
-rw-r--r-- 1 root root 6992 feb 22 14:22 telnet.o
-rw-r--r-- 1 100 users 9843 oct 27 2003 udphelper.c
-rw-r--r-- 1 root root 6760 feb 22 14:22 udphelper.o
[root@ulises src]# rm -f netcat
[root@ulises src]# ll
total 300
-rw-r--r-- 1 100 users 30890 ene 3 2004 core.c
-rw-r--r-- 1 root root 30972 feb 22 14:22 core.o
-rw-r--r-- 1 100 users 5619 dic 10 2003 flagset.c
-rw-r--r-- 1 root root 9296 feb 22 14:22 flagset.o
-rw-r--r-- 1 100 users 2208 ene 18 2003 intl.h
-rw-r--r-- 1 root root 11668 feb 22 14:22 Makefile
-rw-r--r-- 1 100 users 1427 ago 28 2003 Makefile.am
-rw-r--r-- 1 100 users 11934 ene 11 2004 Makefile.in
-rw-r--r-- 1 100 users 13321 ene 3 2004 misc.c
-rw-r--r-- 1 100 users 3069 mar 6 2003 misc.h
-rw-r--r-- 1 root root 16296 feb 22 14:22 misc.o
-rw-r--r-- 1 100 users 21028 ago 28 2003 netcat.c
-rw-r--r-- 1 100 users 6423 ene 3 2004 netcat.h
-rw-r--r-- 1 root root 26784 feb 22 14:22 netcat.o
-rw-r--r-- 1 100 users 20848 ene 3 2004 network.c
-rw-r--r-- 1 root root 22136 feb 22 14:22 network.o
-rw-r--r-- 1 100 users 3663 ene 3 2004 proto.h
-rw-r--r-- 1 100 users 7555 feb 28 2003 telnet.c
-rw-r--r-- 1 root root 6992 feb 22 14:22 telnet.o
-rw-r--r-- 1 100 users 9843 oct 27 2003 udphelper.c
-rw-r--r-- 1 root root 6760 feb 22 14:22 udphelper.o

Comprobemos nuevamente que el programa sigue en ejecución, así como el path desde el que lo lanzamos:
[root@ulises ~]# netstat -anp | grep 1234
tcp 0 0 0.0.0.0:1234 0.0.0.0:* LISTEN 7144/netcat
[root@ulises src]# ps aux | grep 7144
root 7144 0.0 0.1 3988 704 ? S 14:28 0:00 ./netcat -l -p 1234 -e /bin/bash

Vamos a intentar ahora recuperar el binario, utilizando para ello las características de /proc:
[root@ulises src]# cat /proc/7144/exe > netcat_recuperado
[root@ulises src]# ll
total 392
-rw-r--r-- 1 100 users 30890 ene 3 2004 core.c
-rw-r--r-- 1 root root 30972 feb 22 14:22 core.o
-rw-r--r-- 1 100 users 5619 dic 10 2003 flagset.c
-rw-r--r-- 1 root root 9296 feb 22 14:22 flagset.o
-rw-r--r-- 1 100 users 2208 ene 18 2003 intl.h
-rw-r--r-- 1 root root 11668 feb 22 14:22 Makefile
-rw-r--r-- 1 100 users 1427 ago 28 2003 Makefile.am
-rw-r--r-- 1 100 users 11934 ene 11 2004 Makefile.in
-rw-r--r-- 1 100 users 13321 ene 3 2004 misc.c
-rw-r--r-- 1 100 users 3069 mar 6 2003 misc.h
-rw-r--r-- 1 root root 16296 feb 22 14:22 misc.o
-rw-r--r-- 1 100 users 21028 ago 28 2003 netcat.c
-rw-r--r-- 1 100 users 6423 ene 3 2004 netcat.h
-rw-r--r-- 1 root root 26784 feb 22 14:22 netcat.o
-rw-r--r-- 1 root root 87888 feb 22 14:41 netcat_recuperado
-rw-r--r-- 1 100 users 20848 ene 3 2004 network.c
-rw-r--r-- 1 root root 22136 feb 22 14:22 network.o
-rw-r--r-- 1 100 users 3663 ene 3 2004 proto.h
-rw-r--r-- 1 100 users 7555 feb 28 2003 telnet.c
-rw-r--r-- 1 root root 6992 feb 22 14:22 telnet.o
-rw-r--r-- 1 100 users 9843 oct 27 2003 udphelper.c
-rw-r--r-- 1 root root 6760 feb 22 14:22 udphelper.o

Y ahora que hemos recuperado el binario desde la memoria podemos finalizar su ejecución:
[root@ulises src]# kill -s KILL 7144

Si comparamos ahora el tamaño para el binario recuperado con el del fichero original comprobaremos que es exactamente el mismo, e incluso podemos probar a ejecutarlo para constatar que el programa netcat recuperado está en perfectas condiciones.

Referencias

UNIX and Linux Forensic Analysis DVD Toolkit
Chris Pogue, Cory Altheide y Todd Haverkos
Syngress Publishing, Inc.

Red Hat Linux 7.2: The Official Red Hat Linux Reference Guide
Chapter 4. The /proc Filesystem


Entendiendo /proc

The /proc filesystem documentation

Y esto ha sido todo de momento, en breve más, y mejor, espero.

2 comentarios:

Unknown dijo...

Un artículo muy interesante, para que luego digas. Como verás, un rato que tengo te lo dedico :-)

Un solo pero, sabes que esto es más fuerte que yo; cito:

'por lo que si queremos analizar su contenido de forma que su salida sea fácilmente interpretable podemos utilizar un comando como el siguiente:

[neofito@ulises ~]$ cat /proc/${PID}/environ | xargs -0 -n 1 '

Horroroso!!! :D

Eso es un flagrante caso de uso inútil de cat!!! Xargs es suficiente con una redirección de bash:

xargs -0 -n 1 < /proc/${PID}/environ

Ahora en serio, muy buen artículo

neofito dijo...

Sabes que Linux no es mi fuerte, pero para eso estas tú, para enseñarme :-D

Saludos y gracias por la atención