domingo, 22 de marzo de 2009

Análisis de un caso ¿real?

Todos los personajes, lugares, acciones y conversaciones narradas a continuación no son reales, existen o han tenido lugar en otro sitio que no sea la imaginación del que suscribe estas líneas, el cual no se siente responsable de las opiniones, reacciones o ideas vertidas, pues, a fín de cuentas, suceden únicamente en su ficción, ¿o tal vez no?

Tengo un mal presentimiento

No puedo explicar porqué, ni siquiera yo lo entiendo, pero esta mañana me ha parecido notar algo raro en la máquina virtual que utilizo para las pruebas de diseño web de las que, a veces, saco algún beneficio. Es increible la gente que quiere ver su negocio en internet, pero más lo es aún la que prefiere tenerlo por muy poco dinero. Cada vez me alegro más del curso ese de "CCT" que hice el año pasado.

Menos mal que está el vecino del quinto, ese chico un poco raro al que le gustan mucho los ordenadores y algo que llama análisis forense de sistemas, ¡jaja, ni que fuera del CSI! Ha bastado cruzar unas palabras con él en el rellano y ahí lo tengo, con una línea de comandos en la pantalla, aunque creo que lo único que quiere es vacilarme. Bueno, mientras no me cueste nada que haga lo que quiera.

¿Ya ha terminado? Se lleva algo en el pendrive y me sugiere que no utilice la máquina virtual hasta que no me diga algo. Bueno, mejor, así me tomo el día libre que mañana tengo que volver a la oficina.

Al final de la escalera

Venga, dos pisos más y ya estaré en casa. Tengo que ser masoca, anda que las ganas que tengo de analizar el dump de la memoria del sistema virtual del tío este. Parece que el día empieza a pintar bién, quien iba a imaginarlo. Hubiera sido más rápido suspender el Windows virtual y copiar el fichero vmem. A fín de cuentas dicen que tiene un formato similar a un volcado raw, pero bueno, así he podido probar mdd. Ha resultado tan sencillo como descargarlo, abrir un cmd y ejecutar:

C:\>mdd_1.3.exe -v -o mdd.forensics.dd > caso.01.01.log

Tan sólo le ha llevado 38 segundos generar el volcado de memoria en formato raw, sin una sola operación de mapeo defectuosa y calculando además el hash md5 para el fichero. El siguiente turno para win32dd, que también le tengo ganas.

Voy a arrancar Atreyu, con una Ubuntu Linux y volatility totalmente tuneado. Seguro que con Fujur (un Windows XP SP3) también me hubiera servido, pero así juego con Linux que lo tengo un poco olvidado.

Vale, ya tengo la imagen en el directorio /usr/local/volatility, voy a calcular el hash md5 para comprobar que el fichero no esté corrupto:
$ md5sum -b mdd.forensics.dd 

cbab2054a53e3998930c680176148420 *mdd.forensics.dd

Si comparo el hash anterior con el que aparece en la última línea del fichero de resultado de mdd deberían coincidir:
$ tail -n 1 caso.01.01.log

MD5 is: cbab2054a53e3998930c680176148420

Ok, coinciden, todo marcha bién.

Con las manos en la masa

Lo primero un poco de información general. Parecía un Windows XP Profesional, pero mejor lo confirmo:
$  python volatility ident -f mdd.forensics.dd 

Image Name: mdd.forensics.dd
Image Type: Service Pack 2
VM Type: pae
DTB: 0xa9a000
Datetime: Thu Mar 12 10:43:37 2009

Lo que imaginaba, ni siquiera tiene instalado el último Service Pack, además de que no tenía antivirus. En fín, allá cada uno con lo suyo.

Voy a ver ahora los procesos que se estaban ejecutando en la máquina, a ver si encuentro algo raro. Pero como la salida va a ser un poco larga mejor la redirijo a un fichero y así podré analizarla con tranquilidad:
$ python volatility pslist -f mdd.forensics.dd > caso.01.02.log

Que raro, parecen todos procesos legítimos de Windows. A lo mejor solo es cosa del tio este ... Vamos a ver ahora los sockets abiertos, quizás por ahí:
$ python volatility sockets -f mdd.forensics.dd

$

No aparece ninguna conexión abierta, mejor consulto todas las conexiones, independientemente de su estado. Como antes, redirigiendo la salida:
$ python volatility sockscan -f mdd.forensics.dd > caso.01.03.log

Hmmm, esto ya es otra cosa. Que raro, juraría que ese proceso con PID 804 no aparecía en el resultado de ejecutar pslist, aunque el puerto en el que escucha parece el de "Escritorio remoto":
PID    Port   Proto  Create Time                Offset 

------ ------ ------ -------------------------- ----------
...
804 3389 6 Thu Mar 12 10:40:15 2009 0x024e7e98
...
804 3389 6 Thu Mar 12 10:40:15 2009 0x122a2e98
...
804 3389 6 Thu Mar 12 10:40:15 2009 0x132fce98
...

Voy a probar otra cosa. Si no me equivoco el comando psscan es mucho mas lento, pero debería mostrar todos los objetos EPROCESS que encuentre en el volcado:
$ python volatility psscan -f mdd.forensics.dd > caso.01.04.log

Ahí está el proceso oculto, además parece que no es la primera vez que ha sido ejecutado. Por la fecha de finalización del otro proceso con el mismo nombre y PID 460 debe ser la segunda. El que lo hizo no debía tener muy claro su uso y corrigió su ejecución tras un primer intento. Menos mal que se me ha ocurrido guardar la salida del comando anterior para consultarla en otro momento, sin duda se me habría escapado ese detalle.

El primer paso lógico que se me ocurre sería analizar ese ejecutable. Si no me equivoco volatility incluía un comando que permitía volcar un ejecutable cargado en la memoria. Voy a consultar la ayuda:
$ python volatility procdump --help

Usage: procdump [options] (see --help)
 
Options:
-h, --help show this help message and exit
-f FILENAME, --file=FILENAME
(required) XP SP2 Image file
-b BASE, --base=BASE (optional, otherwise best guess is made) Physical
offset (in hex) of directory table base
-t TYPE, --type=TYPE (optional, default="auto") Identify the image type
(pae, nopae, auto)
-o OFFSET, --offset=OFFSET
EPROCESS Offset (in hex) in physcial address space
-p PID, --pid=PID Dump the process with this Pid
-m MODE, --mode=MODE strategy to use when saving executable. Use "disk" to
save using disk-based section sizes, "mem" for memory-
based sections. (default: "mem")
-u, --unsafe do not perform sanity checks on sections when dumping

Menuda pedazo herramienta se ha currado esta gente, así dá gusto. Bueno, creo que tendré que utilizar el offset obtenido de la salida de psscan, porque dado que el PID está oculto no debería funcionar. Bueno, mejor lo pruebo y así salgo de dudas:
$ python volatility procdump -p 804 -f mdd.forensics.dd 

Error process [804] not found

Lo que me imaginaba. Pués nada, utilizamos el offset y listo:
$ python volatility procdump -o 0x021fa020 -f mdd.forensics.dd

Dumping hide_n, pid: 804 output: executable.804.exe
Memory Not Accessible: Virtual Address: 0x40a000 File Offset: 0xa000 Size: 0x1000

Ahora sí, ahí lo tengo, en el directorio /usr/local/volatility. Además, por el resultado del comando parece que únicamente había una página de memoria que no era accesible (0x1000 = 4096 bytes). No me extraña, lo más seguro es que haya sido paginada por el sistema para liberar espacio en la memoria.

Y las palabras, palabras son

Bueno, vamos a ver que tiene dentro el proceso oculto; seguro que analizar las cadenas de texto que contiene nos dará más pistas sobre su origen y funcionalidad:
$ strings -t x -n 3 executable.804.exe > caso.01.05.log

Agregando el parámetro "-t x" cada cadena encontrada irá precedida por el offset o posición, en formato hexadecimal, que ocupa dentro del fichero analizado y el segundo parámetro, "-n 3", encontrará todas las que tengan al menos tres letras. Abriré el fichero de resultados a ver que encuentro:
...

cba4 Cmd line:
cbb0 port numbers can be individual or ranges: m-n [inclusive]
cbec -u UDP mode
cbfa -v verbose [use twice to be more verbose]
cc26 -w secs timeout for connects and final net reads
cc59 -z zero-I/O mode [used for scanning]
cc80 -t answer TELNET negotiation
cca0 -g gateway source-routing hop point[s], up to 8
ccd1 -G num source-routing pointer: 4, 8, 12, ...
cd00 -h this cruft
cd10 -i secs delay interval for lines sent, ports scanned
cd47 -l listen mode, for inbound connects
cd6e -L listen harder, re-listen on socket close
cd9c -n numeric-only IP addresses, no DNS
cdc3 -o file hex dump of traffic
cde1 -p port local port number
cdfd -r randomize local and remote ports
ce23 -s addr local source address
ce44 -e prog inbound program to exec [dangerous!!]
ce74 -d detach from console, stealth mode
ce9c [v1.10 NT]
cea7 connect to somewhere: nc [-options] hostname port[s] [ports] ...
cee9 listen for inbound: nc -l -p port [options] [hostname] [port]
cf27 options:
f7d8 PST
f818 PDT
fd60 C:\Documents and Settings\javier\Escritorio\hide_nc.exe
...

Bueno, bueno, bueno, desde luego estas cadenas casi no dejan lugar a dudas, tiene toda la pinta de ser la versión de netcat portada por la gente de @stake a sistemas Windows NT. Además ahora parece cobrar sentido el puerto en el que estaba escuchando el proceso. Si mal no recuerdo:
nc.exe -d -L -p 3389 -e cmd.exe

Ejecutándolo así consigo que se ejecute en modo daemon, así podremos cerrar la ventana de comandos sin que finalice su ejecución y así también evitaremos ver un proceso padre (PPID) asociado a un cmd.exe. Las siguientes opciones hacen que el programa quede a la escucha, "-L", en el puerto 3389, "-p 3389", y que aquel que se conecte reciba como resultado una "shell" en el sistema, "-e cmd.exe". Simple pero efectivo.

A pesar de estar casi convencido mejor me aseguro un poco más. Está claro que si calculo el hash md5 para el proceso volcado y lo comparo con el hash del programita en cuestión no van a coincidir, al menos 4kb han sido rellenados con ceros para suplir la página que no se encontraba en la memoria. Pero para estos casos existe ssdeep, y el concepto de fuzzy hashing.

Si no recuerdo mal, lo que hacía el programita en cuestión era calcular el hash para los distintos fragmentos que componen el objetivo de análisis. Si comparamos los resultados con otro fichero distinto y los hashes para los diferentes fragmentos coinciden para determinadas secuencias, podremos determinar la probabilidad de que sean similares a pesar de encontrar discrepancias en el resultado final. Gran idea la de Jesse Kornblum. Ahora la práctica, pero primero mejor lo instalo:
$ apt-get install ssdeep

Ahora calcularé el hash para la versión que he descargado del binario "completo" y guardaré el resultado en un fichero:
$ ssdeep -b nc.exe > caso.01.06.log

Vale, ya lo tengo, ahora calcularé el hash para el binario que he volcado desde el dump de la memoria y lo compararé con el anterior, a ver con que probabilidad son el mismo:
$ ssdeep -bm caso.01.06.log executable.804.exe 

executable.804.exe matches nc.exe (94)

Blanco y en botella ...

La relación causa-efecto

Recapitulemos: tenemos un proceso oculto el cual se corresponde con la versión de netcat para Windows y que se ha ejecutado de forma que acepta conexiones en el puerto 3389. No está vinculado a ninguna consola de comandos y además, haciendo que escuche en ese puerto, podríamos pensar que se trata del servicio "Escritorio remoto" legítimo. Indudablemente el "intruso" pretendía mantenerlo lo mas escondido posible, y ¿cual es la forma más simple de ocultar un proceso en Windows? ¡Ya lo tengo, dkom!

Si se trata de dkom existirá otro programa utilizado para ocultarlo, y para que éste último funcione tendrá que acceder al modo kernel, ¿y cual es la forma de acceder al modo kernel desde un programa ejecutándose en modo usuario? Pues un driver. ¡Si es que a veces hasta me doy miedo!

Empecemos pués; el primer paso será obtener un listado de los módulos que existen en el volcado de memoria. Dado que el proceso de netcat estaba oculto nada nos dice que el propio driver utilizado no se haya ocultado a sí mismo, así que utilizaré el comando modscan, un poco más lento pero mucho más seguro:
$ python volatility modscan -f mdd.forensics.dd > caso.01.07.log

Ahora compararé los módulos encontrados con los existentes en un sistema Windows XP SP2 recién instalado. Por lógica los que no coincidan deberían ser los analizados, aunque esto no es estrictamente necesario, pero por algo tengo que empezar.

Vale, me ha llevado bastante rato pero creo que ya lo tengo. El único módulo que se sale de lo normal es dkomdriver, un nombre bastante obvio me temo. Voy a volcarlo en un fichero y así podré analizarlo mejor:
$ python volatility moddump -o 0x00f8d02000 -f mdd.forensics.dd 

Dumping (unknown module name) @f8d02000

Se ha volcado correctamente con el nombre driver.f8d02000.sys, así que vamos a ver si las cadenas de texto que contiene pueden ser de utilidad. Utilizaré el comando strings tal y como ya hice antes con el ejecutable para el proceso oculto, pero en esta ocasión utilizaré el flag "-a" para que el análisis sea más completo:
strings -a -t d -n 3 driver.f8d02000.sys > caso.01.08.log

Voy a ver el fichero obtenido como resultado a ver si encuentro algo:
1152 Llamada a dkomCreate

1196 Llamada a dkomWrite
1238 Llamada a dkomRead
1280 Llamada a dkomClose
1322 Llamada a dkomControl
1346 Recibido codigo de control no definido
1386 Recibido codigo de control IOCTL_DRV_HIDE
1430 Ocultando proceso con PID %d en la direccion %08lX
1482 No se ha obtenido la direccion del EPROCESS para el PID

¡Bien! Parece que he tenido suerte y el creador del driver no tenía malas intenciones (un proof of concept quizás). Si hubiera sido un poco mas malo no hubiera utilizado cadenas tan obvias. Ahora ya está más o menos claro:
  • Primero renombró el ejecutable nc.exe a hide_nc.exe, seguramente para facilitar su posterior ocultacion.

  • A eso de las 10:39:13 del 12 de marzo hace una prueba de funcionamiento de netcat, pero seguramente se olvidó de utilizar la opcion "-d" con lo que al cerrar la consola adiós a su puerta trasera en el sistema.

  • Segundo intento a las 10:40:15, y parece que esta vez el resultado es satisfactorio.

  • El siguiente paso ocultar el proceso, y para eso se aprovechó de la tecnica dkom mediante el driver dkomdriver.

Desde luego no es un análisis muy profesional, pero aprender seguro que he aprendido. Por la tarde bajaré a contarle lo que he encontrado al vecino, pero ahora toca ver Padre de familia :-D

NOTA: El siguiente fichero zip contiene la mayor parte de ficheros de registro o ejecutables obtenidos o analizados durante el desarrollo de este artículo. Si al descargarlo aparece un mensaje del antivirus será por contener el binario de netcat.

13 comentarios:

Ariel M. Liguori de Gottig dijo...

Si te digo que te has pasado Neo, me quedo corto! Excelentisíma nota! Gracias por tu aportes!

neofito dijo...

¡Muchas gracias Ariel! Si al menos una persona lo ha considerado interesante creo que ha valido la pena.

Saludos

GigA ~~ dijo...

Que sean 2 personas Neo!

Lobosoft dijo...

Como se suele decir, no hay dos sin tres. Me quito el sombrero: una excelente entrada.

Descubro con agrado tu blog. Un cordial saludo.

neofito dijo...

Gracias a ambos por los comentarios, y bienvenido Lobosoft a este, mi pequeño rincon :-)

Saludos

bad dijo...

ya son 4 :D , yo como siempre siguiendo tu blog

saludos

hmontoliu dijo...

neofito, muy interesante y sobre todo muy ameno!!!

Fujur y Atreyu, eh? que original ;-)

neofito dijo...

;) Me alegro te haya gustado, y gracias por la ayuda con el comando strings.

Saludos

gnomius dijo...

Una lectura muy amena e interesante tema, sí señor. Me temo que tienes a otro que se está aficionando a tu blog. ;)

Juanjo dijo...

En fin, qué decir que no sepas ya...Ha sido alucinante, realmente me ha gustado y he aprendido leyéndolo, a ver si lo llevo a la práctica que tengo un mensaje de error cada vez que inicio Windows y quiero ver si aplicando estos conceptos u otros parecidos soy capaz de resolverlo.

¡Saludos!

FeCr_88 dijo...

Buenisimo, me inspiras para hacer un articulo parecido al que hiciste, no creo que tenga el mismo nivel. Por la trama y eso, pero servira para afrontar el grandioso tema del Analisis Forense de Sistemas

e-go dijo...

Simplemente excelente neo... bien explicado y aún disponibilizas el zip con los archivos, gracias!

ChechoHack dijo...

joder ! compañero te has jalado tremendo post ! =) estare atento a tu blog ! =P me estoy metiendo en el area de informatica forense y tu blog parece tener buena info ! sigue asi ! ;)