AvanzadorootkitsLinuxLKMSuterusuAdore-NGVFS

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.

MalwareIntel Research··12 min lectura
Serie: Rootkits y Bootkits — Parte 18

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:

  1. Kernels 2.6.x dejaron de exportar el simbolo. Ya no se podia obtener la direccion con un simple extern.
  2. Tecnicas de busqueda en memoria eran fragiles. Escanear /boot/System.map o recorrer memoria buscando patrones era dependiente de la version exacta del kernel.
  3. 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

CapacidadImplementacion
Ocultar procesosHook readdir en /proc, filtrar PIDs en filldir
Ocultar ficherosHook readdir en /, filtrar por nombre
Ocultar el modulolist_del de la lista de modulos del kernel
Escalada de privilegiosInterfaz via fichero especial en /proc
Control remotoNo 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

CapacidadAdore-NG (2004)Suterusu (2013)
VFS hookingreaddir/filldiriterate/iterate_shared
Ocultar procesosSiSi
Ocultar ficherosSiSi
Ocultar moduloSiSi
Ocultar conexiones redNoSi (netfilter + /proc/net/tcp)
Backdoor de redNoSi (port knocking + reverse shell)
Escalada de privilegiosSiSi
Kernels soportados2.2 - 3.103.0 - 3.x
MantenimientoAbandonado (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

  1. 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.
  2. Compatible con kernel hardening. CONFIG_X86_X32_ABI, restricciones de kprobes y proteccion de la syscall table no afectan al VFS hooking.
  3. Menor superficie de deteccion en la syscall table. Herramientas que verifican la integridad de sys_call_table no detectan hooks VFS.
  4. 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

  1. Solo afecta enumeracion basada en filesystem. Si una herramienta accede directamente a APIs del kernel (por ejemplo, recorriendo la lista de task_struct desde un modulo del kernel), el hook VFS no la intercepta.
  2. 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.
  3. Cambios de API del kernel. La interfaz VFS cambio de readdir a iterate a iterate_shared, rompiendo compatibilidad. La syscall table es mas estable.
  4. No intercepta otras syscalls. VFS hooking cubre getdents/getdents64, pero no kill, stat, open u 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:

PeriodoRootkitTecnica principalInnovacion
1997HeroinSyscall table hookingPrimer LKM rootkit documentado
1999KnarkSyscall table hookingOcultacion de ficheros + procesos + red
1999AdoreSyscall table hookingInterfaz de control via /proc
2004Adore-NGVFS hookingPrimera alternativa a syscall table
2008-2012VariosDKOM directoManipulacion de listas enlazadas del kernel
2013SuterusuVFS hooking + netfilterVFS modernizado + red
2013DiamorphineSyscall table hookingMinimalismo, compatibilidad 2.6-6.x
2015ReptileVFS + syscall hibridoBackdoor cifrado + persistencia avanzada
2020+VarioseBPF rootkitsHooks 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

HerramientaCapacidad contra VFS hooksNotas
rkhunterParcial. Detecta variantes conocidas por firmaNo verifica punteros VFS directamente
chkrootkitParcial. Comparacion de procesos visible vs realEfectivo si el rootkit no cubre todos los caminos
VolatilityAlta. Analisis forense de memoria con perfil del kernelPuede comparar punteros VFS contra simbolos conocidos
Tracee (eBPF)Alta. Monitoriza a nivel de tracepointDetecta discrepancias entre lo que el kernel procesa y lo que retorna
LKRGAlta. Verifica integridad de estructuras del kernel en runtimeDetecta 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

TecnicaIDUso en Adore-NG / Suterusu
RootkitT1014Categoria principal. LKM que oculta artefactos via VFS hooking
Kernel Modules and ExtensionsT1547.006Se cargan como modulos del kernel con insmod
HookingT1574.013Reemplazan punteros en file_operations (VFS) en lugar de syscall table
Hide ArtifactsT1564Ocultan procesos, ficheros y el propio modulo
Network Service Discovery (evasion)T1046Suterusu oculta conexiones de red via netfilter hooks
Traffic SignalingT1205Suterusu usa port knocking para activar backdoor
Access Token ManipulationT1134Escalada de privilegios via modificacion de credenciales del kernel

Contramedidas recomendadas

  1. Restringir carga de modulos del kernel. kernel.modules_disabled=1 en sysctl tras el boot. Module signing con CONFIG_MODULE_SIG_FORCE para aceptar solo modulos firmados.
  2. Desplegar LKRG. Linux Kernel Runtime Guard verifica la integridad de estructuras del kernel (incluyendo file_operations) en tiempo real.
  3. Monitorizar con eBPF. Tracee y Falco detectan discrepancias entre lo que el kernel procesa internamente y lo que las herramientas de usuario reciben.
  4. Verificar integridad de file_operations. Scripts periodicos que comparen los punteros de file_operations de /proc, / y /sys contra los valores esperados del kernel en ejecucion.
  5. 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.
  6. Auditar conexiones de red. Comparar la salida de ss (userspace) con los contadores de netfilter y la informacion de /proc/net/tcp leida 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

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.