By Luis Almaraz aka lehag
@lehag07
Descripción
Este reto fue presentado durante el CTF de Insomni'hack Quals 2020 donde participé junto con mi equipo Mayas. La única descripción que nos daba el reto es la siguiente: “Oh shit! Data have been stolen from my computer... I looked for malicious activity but found nothing suspicious. Could ya give me a hand and find the malware and how it's hiding?”. En esta entrada se describe la solución que propusimos para resolver el reto, a pesar de estar a poco de resolverlo durante del CTF.
Identificando el archivo memory_inshack.zip
Archivo | Tipo de archivo | Hash (MD5) |
---|---|---|
memory_inshack.zip | Zip archive data | 15bb69dbe9e5e1cb31b8f62b13da8628 |
2. Cuando descomprimimos el anterior archivo, él generó los siguientes dos archivos, ver Tabla 2.
Archivo | Tipo de archivo | Hash (MD5) |
---|---|---|
memory.vmem | Data | d41d8cd98f00b204e9800998ecf8427e |
Ubuntu_4.15.0-72-generic_profile.zip | Zip archive data | 2773ef2ec09e373e06e48a35a3fc3f0c |
El archivo memory.vmem representa el volcado de memoria, mientras que el archivo Ubuntu_4.15.0-72-generic_profile.zip se utiliza como perfil del Framework de Volatility para interpretar el primer archivo.
Triage
1. Una vez que el perfil de Volatility fue cargado en el directorio correspondiente [1], listamos los procesos activos contenidos en el volcado de memoria.
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$ volatility -f memory.vmem --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 linux_pstree
Volatility Foundation Volatility Framework 2.6
Name Pid Uid
systemd 1
.systemd-journal 326
[snipped]
...bash 1733 1000
....sudo 1750
.....meterpreter 1751
......sh 2964
[snipped]
.[jfsCommit] 2532
.[jfsSync] 2533
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$
Hubo un árbol de procesos que llamó nuestra atención. Fue la secuencia:
bash->sudo->meterpreter->sh.
Lo anterior, nos sugirió que el host comprometido tenía un backdoor activo llamado meterpreter [2], asociado con Metasploit, el cual ejecutó una shell en el sistema como usuario root. Meterpreter es un tipo de ataque de Metasploit asociado con un payload que provee de una shell interactiva que permite a un atacante ejecutar código remoto, es por ello, que decidimos indagar más sobre el proceso asociado con él.
2. Luego, listamos las conexiones activas presentes en el volcado de memoria relacionadas con el PID 1751.
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$ volatility -f memory.vmem --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 linux_netstat -p 1751
Volatility Foundation Volatility Framework 2.6
TCP 192.168.180.132 :51934 192.168.180.131 : 1337 ESTABLISHED meterpreter/1751
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$
Encontramos una conexión remota a un host con la IP 192.168.180.131 sobre el puerto 1337.
3. En ese momento, pensamos que sería una buena idea mirar los logs del volcado de memoria para entender mejor la ejecución del proceso malicioso.
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$ volatility -f memory.vmem --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 linux_dmesg Volatility Foundation Volatility Framework 2.6 [0.0] Linux version 4.15.0-72-generic (buildd@lcy01-amd64-026) (gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 (Ubuntu 4.15.0-72.81-generic 4.15.18) [snipped] [51534295268.51] show_signal_msg: 30 callbacks suppressed [51534297419.51] meterpreter[1743]: segfault at 0 ip 0000000000000000 sp 00007fff4ac3e110 error 14 in meterpreter[400000+1000] [snipped] [168682653963.168] rkit: loading out-of-tree module taints kernel. [168682697557.168] rkit: module verification failed: signature and/or required key missing - tainting kernel lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$
La información obtenida de los logs del volcado de memoria nos permitió entender que el sistema comprometido no sólo tenía un backdoor activo, sino también un módulo de kernel cargado, normalmente asociados con rootkits.
4. De nuevo, con la ayuda del Framework de Volatility, listamos los módulos de kernel cargados en el sistema comprometido.
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$ volatility -f memory.vmem --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 linux_check_modules
Volatility Foundation Volatility Framework 2.6
Module Address Core Address Init Address Module Name
------------------ ------------------ ------------------ ------------------------
0xffffffffc0943080 0xffffffffc0941000 0x0 rkit
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$
El rootkit en cuestión fue cargado en el sistema y su nombre era rkit.
5. Encontramos más información sobre el módulo de kernel al imprimir las cadenas embebidas en el volcado de memoria.
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$ strings memory.vmem | grep "rkit" rkit rkit [snipped] rkit.ko insmod /home/julien/Downloads/rkit.ko hide=rJ/1g5PA5amy176A64akjuq/jryOug== hide_pid=1751 Jan 16 06:02:47 168.682697] rkication failed: required key mid key missing - failed: signatur2697] rkit: modu6:02:47 ubuntu kut specifying a rkit +module:rkit +module:rkit rkit: loading out-of-tree module taints kernel. rkit: module verification failed: signature and/or required key missing - tainting kernel lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$
Alguna de la información que encontramos sobre el archivo llamado rkit, tenía los parámetros de su ejecución y el PID del proceso que escondía (1751), asociado con el backdoor.
Un aspecto a resaltar en la ejecución del rootkit era que el valor del parametro hide estaba codificado en base 64: rJ/1g5PA5amy176A64akjuq/jryOug==. Al intentar decodificarlo, no encontramos algún texto ASCII legible, por lo que en ese momento, el parámetro no nos decía mucho sobre su finalidad.
6. Enseguida, procedimos a recuperar el artefacto tomando como referencia su número de inodo 0xffffffffc0943080 del volcado de memoria, ver Tabla 3. El número de inodo anterior, fue obtenido al listar los módulos de kernel cargados en el sistema comprometido en el punto 4 de esta sección.
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$ volatility -f memory.vmem --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 linux_moddump -b 0xffffffffc0943080 -D ./rkit Volatility Foundation Volatility Framework 2.6 Wrote 4146551 bytes to rkit.0xffffffffc0943080.lkm lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$ cd rkit/ lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents/rkit$ mv rkit.0xffffffffc0943080.lkm rkit lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents/rkit$
En este caso, obtuvimos el mapa de memoria del rootkit en ejecución, pero también fue necesario recuperar el artefacto tal y como estaba en disco, ya que, de esta manera, conoceríamos la relación entre las direcciones de memoria del rootkit en ejecución con el nombre de las referencias a ciertas funciones y variables globales contenidas en el código del artefacto en disco.
7. Obtuvimos el archivo lógico asociado con el rootkit, ver Tabla 3.
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents/rkit$ volatility -f memory.vmem --profile=LinuxUbuntu_4_15_0-72-generic_profilex64 linux_find_file --inode=0xffff8a9dd42755e8 --outfile=./rkit/rkit.ko
[snipped]
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents/rkit$
Archivo | Tipo de archivo | Hash (MD5) |
---|---|---|
rkit | ELF 64-bit, not stripped | f5fc0d81f474954d36aba38ba8318b42 |
rkit.ko | ELF 64-bit, not stripped | 31ef522bb5f69a7b188d9aaad414e60b |
Aplicando ingeniería inversa al rootkit
Antes de comenzar esta sección, si no tienes experiencia en el funcionamiento de un rootkit, básicamente son programas cuyo propósito es ocultar la presencia de malware en un sistema comprometido y preservar su existencia privilegiada. Un rootkit ocultará archivos, procesos de malware, módulos cargados, claves de registro, cuentas de usuario o incluso registros del sistema [3]. El clásico ejemplo es cuando el rootkit instalado monitorea la ejecución del programa "ps" para que al listarse los procesos corriendo en el sistema, el proceso malicioso no se le muestre a la víctima y por consecuencia, permanezca escondido en el sistema. Normalmente son instalados en el sistema atacado como módulos de kernel.
1. Con el archivo lógico previamente recuperado, rkit.ko, procedimos a aplicar ingeniería inversa al código en ensamblador analizándolo con IDA Pro. Encontramos ciertas funciones que nos permitieron entender la lógica para obtener el flag, como se muestra en la Figura 1.
Figura 1. Funciones en el rootkit. |
2. La función init_module tenía el código observado en la Figura 2:
Figura 2. Código en la función init_module. |
3. Notamos que primero calcula el tamaño del argumento en base 64 (rJ/1g5PA5amy176A64akjuq/jryOug==). Luego, llama a una función renombrada aquí como decodeBase64 que decodifica la cadena en base 64, la cadena resultante es almacenada en una variable llamada ep. La función renombrada como xorOperation tenía la estructura de la Figura 3.
Figura 3. Estructura de la función xorOperation. |
Como se puede observar, la función carga un conjunto de valores de la variable llamada sk, sucede lo mismo con los valores de la variable pk.
4. Conocimos los valores anteriores porque se encontraban embebidos en las direcciones de memoria del mapa de proceso del rootkit obtenido también. Dichas direcciones de memoria fueron consultadas en el volcado de memoria proporcionado con el comando volshell del Framework de Volatility.
>>> db(0x0FFFFFFFFC09433E0, 62) <- valor de sk 0xffffffffc09433e0 e5 d1 a6 f8 c1 f0 d5 dd f9 e6 ca c6 db f4 f6 e1 ................ 0xffffffffc09433f0 da d4 e7 d9 fd c7 a5 fc c8 c4 e4 de e3 a2 f7 c5 ................ 0xffffffffc0943400 fe a3 ff d0 c3 e0 ab c2 a7 d8 d7 e2 df eb dc aa ................ 0xffffffffc0943410 a1 a0 d3 cb a4 f1 fa c0 fb f5 d6 f3 ea e8 .............. >>> db(0x0FFFFFFFFC0943000, 62) <- valor de pk 0xffffffffc0943000 77 43 34 6a 53 62 47 4f 6b 74 58 54 49 66 64 73 wC4jSbGOktXTIfds 0xffffffffc0943010 48 46 75 4b 6f 55 37 6e 5a 56 76 4c 71 30 65 57 HFuKoU7nZVvLq0eW 0xffffffffc0943020 6c 31 6d 42 51 72 39 50 35 4a 45 70 4d 79 4e 38 l1mBQr9P5JEpMyN8 0xffffffffc0943030 33 32 41 59 36 63 68 52 69 67 44 61 78 7a 32AY6chRigDaxz >>>
Luego, realiza una operación xor entre los valores de sk y los de pk. De acuerdo con el ciclo observado, la longitud de ambas cadenas era de 0x3E (62 en decimal) bytes.
5. En la Figura 4, podemos ver la implementación del Hooking de la función getdents de Linux. Hooking significa que la función original será modificada por el malware en memoria para que cada vez que dicha syscall se solicite, se ejecute el código malicioso del atacante en lugar del original.
En este caso, se puede ver cómo la dirección original del syscall se guarda en el registro rdx con la instrucción:
mov rdx, [rax+270h]
Inmediatamente después, se observa que la dirección original es modificada con la del código malicioso (etiquetado como "finalDecode" en la Figura):
mov qword ptr [rax+270h], offset finalDecode
Figura 4. Estructura de la nueva función getdents. |
Allí, encontramos la función que obtenía el flag, renombrada aquí como finalDecode y que tenía las líneas de código de la Figura 5, comprendiendo que el argumento inicial codificado en base 64 era utilizado para que una vez que la función decodeBase64 decodificara dicha cadena, entonces se tomara la nueva cadena y se procediera a hacer una operación XOR con ciertos valores en memoria (mostrados en el punto 4 de esta sección) y finalmente se obtuviera el flag.
Figura 5. Código relacionado con la generación del flag. |
Programando la solución
1. Al ya conocer los valores anteriores, procedimos a implementar nuestra solución.#!/usr/bin/env python3 ## Author: lehag ## Code: rkit.py import base64 def init_module(): sk = [0xE5, 0xD1, 0xA6, 0xF8, 0xC1, 0xF0, 0xD5, 0xDD, 0xF9, 0xE6, 0xCA, 0xC6, 0xDB, 0xF4, 0xF6, 0xE1, 0xDA, 0xD4, 0xE7, 0xD9, 0xFD, 0xC7, 0xA5, 0xFC, 0xC8, 0xC4, 0xE4, 0xDE, 0xE3, 0xA2, 0xF7, 0xC5, 0xFE, 0xA3, 0xFF, 0xD0, 0xC3, 0xE0, 0xAB, 0xC2, 0xA7, 0xD8, 0xD7, 0xE2, 0xDF, 0xEB, 0xDC, 0xAA, 0xA1, 0xA0, 0xD3, 0xCB, 0xA4, 0xF1, 0xFA, 0xC0, 0xFB, 0xF5, 0xD6, 0xF3, 0xEA, 0xE8] pk = b"wC4jSbGOktXTIfdsHFuKoU7nZVvLq0eWl1mBQr9P5JEpMyN832AY6chRigDaxz"; rbx = b"rJ/1g5PA5amy176A64akjuq/jryOug=="; ep = decodeBase64(rbx); ## xorOperation(sk, pk); getdentsHooking(ep, sk); def decodeBase64(rbx): return list(base64.b64decode(rbx)); def xorOperation(sk, pk): for i in range(0, 62): sk[i] = sk[i] ^ pk[i]; return sk; def getdentsHooking(ep, sk): finalDecode(ep, sk); def finalDecode(ep, sk): for i in range(len(ep)): ep[i] = chr(ep[i] ^ sk[i]); print("".join(ep)); init_module();
2. Finalmente, ejecutamos el código anterior y obtuvimos el flag.
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$ python3 rkit.py
INS{R00tK1tF0rRo0kies}
lehag@blackbox:~/Insomnihack/2020/Quals/RE/1. Getdents$
Flag
INS{R00tK1tF0rRo0kies}
Agradecimientos
A Tony Palma aka xbytemx, compañero de Mayas, por aportar ideas durante la solución del reto.Go Mayas!
Referencias
[1] https://github.com/volatilityfoundation/volatility/wiki/Linux#making-the-profile[2] https://www.offensive-security.com/metasploit-unleashed/about-meterpreter/
[3] https://www.enisa.europa.eu/topics/csirts-in-europe/glossary/rootkits
Comentarios
Publicar un comentario