Persistencia en Linux: cron, systemd, SSH keys, rootkits, LD_PRELOAD
· 9 min de lectura
Qué mecanismos usa un atacante para sobrevivir en Linux: cron, systemd timers, servicios, claves SSH, rootkits LKM, LD_PRELOAD, PAM backdoors, archivos init. Cómo se detectan y se limpian.
La persistencia en Linux comparte filosofía con la de Windows (asegurar que el acceso sobrevive a reinicios, cambios de credenciales y limpieza) pero utiliza mecanismos muy distintos: cron, systemd, servicios tradicionales, claves SSH, archivos de inicialización del shell, rootkits a nivel de kernel, preloads de biblioteca y backdoors de PAM. Todas estas técnicas aparecen en los informes forenses de incidentes en servidores Linux y cualquier auditoría de hardening las revisa antes de dar por limpio un equipo.
Esta lección cubre el catálogo completo organizado por nivel de privilegio y por sigilo. Es el complemento de la lección anterior sobre Windows; juntas cubren el 95 % de lo que ves en respuesta a incidentes reales.
Practica solo en equipos propios o laboratorios autorizados. Dejar backdoors en servidores ajenos es intrusión y delito; en pentests legítimos solo se instalan con autorización explícita y se limpian al terminar.
El mapa#

Catalogada por nivel de privilegio típico requerido para instalarse:
| Mecanismo | Disparador | Nivel |
|---|---|---|
| Cron jobs | Tiempo | Usuario / root |
| Systemd user units | Logon + tiempo | Usuario |
| Systemd system units | Boot + tiempo | Root |
| SSH authorized_keys | Conexión SSH | Usuario / root |
| Shell init files (.bashrc, .profile) | Logon | Usuario |
| /etc/profile.d/*.sh | Logon cualquier usuario | Root |
| LD_PRELOAD | Ejecución de binarios | Root |
| PAM backdoor | Autenticación | Root |
| Kernel module (LKM rootkit) | Boot | Root |
| XDG autostart | Login gráfico | Usuario |
| init.d / rc.local | Boot | Root |
| APT/Yum post-install hooks | Actualizaciones | Root |
Cron jobs#
Clásico. Un cron corre periódicamente comandos por el usuario que lo instaló:
# Como usuario normal
crontab -e
# Añadir:
* * * * * /tmp/.cache/evil.sh
# Como root, cron del sistema
echo "* * * * * root /usr/local/bin/evil.sh" > /etc/cron.d/system-check
Ubicaciones a revisar:
/etc/crontab/etc/cron.d//etc/cron.{hourly,daily,weekly,monthly}//var/spool/cron/crontabs/<user>
Systemd#
El sistema moderno de servicios. Un atacante con root crea un servicio:
# /etc/systemd/system/system-update.service
[Unit]
Description=System Update
After=network.target
[Service]
ExecStart=/usr/local/bin/evil.sh
Restart=always
[Install]
WantedBy=multi-user.target
Luego systemctl daemon-reload && systemctl enable --now system-update. Sobrevive reboots.
Usuario normal puede usar systemd user units en ~/.config/systemd/user/:
[Unit]
Description=User Update
[Service]
ExecStart=/home/maria/.local/bin/evil.sh
Restart=always
[Install]
WantedBy=default.target
Systemd timers#
Equivalente moderno a cron:
# system-update.timer
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
Más potente que cron (puede disparar por eventos, dependencias, etc.) y muchas defensas tradicionales de Linux solo vigilan cron, no systemd timers.
SSH authorized_keys#
El estándar silencioso: añadir una clave pública tuya a ~/.ssh/authorized_keys del usuario comprometido, o directamente a /root/.ssh/authorized_keys:
echo "ssh-rsa AAAAB3Nza... attacker" >> /root/.ssh/authorized_keys
Sobrevive a cambios de password del usuario (las keys no dependen del password). La forma correcta de detectar es hashear y comparar con una baseline de claves autorizadas.
SSH Match User y configuraciones avanzadas#
En /etc/ssh/sshd_config se pueden añadir configuraciones que solo aplican a usuarios o IPs específicas:
Match User root
AuthenticationMethods publickey
AuthorizedKeysFile /etc/ssh/root_keys
Si un atacante controla ese archivo, no es visible en el authorized_keys del usuario; está en el archivo de configuración.
Archivos de inicialización del shell#
Cada vez que un usuario inicia una shell, Bash/Zsh leen archivos de configuración:
~/.bashrc,~/.bash_profile,~/.profile(usuario)./etc/bash.bashrc,/etc/profile(sistema)./etc/profile.d/*.sh(fragmentos del sistema).
Añadir una línea que ejecute tu backdoor:
echo '/usr/local/bin/evil.sh 2>/dev/null &' >> ~/.bashrc
Cada shell que abra ese usuario ejecuta el backdoor en background. Sobrevive casi todo. Detección: diff contra baseline, o auditoría manual.
LD_PRELOAD#
Variable de entorno que fuerza a cargar una librería compartida antes que cualquier otra cosa. Si estableces LD_PRELOAD=/tmp/evil.so globalmente o para procesos específicos, puedes:
- Interceptar llamadas a funciones de libc (
open,write,exec). - Ejecutar código arbitrario cuando cualquier proceso arranca.
- Ocultar archivos y procesos a ciertas herramientas.
# En /etc/ld.so.preload
echo "/tmp/.evil.so" >> /etc/ld.so.preload
A partir de ese momento, cualquier binario que use libc carga primero evil.so. Rootkit en user-space muy efectivo.
PAM backdoor#
PAM (Pluggable Authentication Modules) gestiona la autenticación en Linux. Modificar un módulo PAM o añadir uno malicioso permite aceptar una contraseña maestra además de la legítima:
// En /lib/security/pam_unix.so modificado
if (strcmp(password, "backdoor") == 0) return PAM_SUCCESS;
Un atacante con root recompila pam_unix.so con esa lógica. Resultado: cualquier usuario del sistema puede entrar con la password backdoor, además de su propia password. La password original sigue funcionando: el usuario no se da cuenta.
Detección: hash de los módulos PAM contra baseline.
Kernel module (LKM rootkit)#
Los más sigilosos: cargan código en el kernel que oculta procesos, archivos, conexiones de red y módulos a las herramientas estándar. Los más famosos:
- Diamorphine (educativo, open source): oculta procesos, archivos y da root a cualquier usuario con una signal.
- Reptile (más sofisticado): también oculta tráfico de red.
Para instalar requieren compilar contra el kernel exacto del objetivo. Una vez cargados, son extremadamente difíciles de detectar con herramientas desde el propio sistema (ya están secuestradas); se detectan con análisis forense desde fuera (memoria con Volatility, comparación con snapshot).
XDG autostart (login gráfico)#
En sistemas con entorno gráfico:
# ~/.config/autostart/evil.desktop
[Desktop Entry]
Type=Application
Exec=/home/maria/.local/bin/evil.sh
Hidden=false
X-GNOME-Autostart-enabled=true
Cada vez que el usuario inicia sesión gráfica, se ejecuta el script.
Detección#

Herramientas:
- chkrootkit y rkhunter: scanner de rootkits y backdoors conocidos.
- AIDE / Tripwire: file integrity monitoring. Comparan hashes con baseline.
- auditd: audit rules sobre ejecuciones, modificaciones de archivos sensibles.
- Wazuh: SIEM con reglas para patrones de persistencia.
- unhide: compara
/procconpspara detectar procesos ocultos. - Linpeas / LinEnum: aunque son para enumeración offensive, revisan todas las ubicaciones de persistencia; útiles también para defensa.
Checklist manual rápido tras RCE sospechosa:
# Cron de todos
for u in $(cut -d: -f1 /etc/passwd); do echo "=== $u ==="; crontab -l -u "$u" 2>/dev/null; done
# Systemd units
systemctl list-unit-files --state=enabled
# SSH keys
find /home /root -name "authorized_keys" -exec cat {} +
# Archivos init del shell
for f in /etc/profile /etc/bash.bashrc /etc/profile.d/*.sh; do md5sum "$f"; done
for u in $(cut -d: -f6 /etc/passwd | sort -u); do
for f in .bashrc .bash_profile .profile; do
[ -f "$u/$f" ] && md5sum "$u/$f"
done
done
# LD preload
cat /etc/ld.so.preload 2>/dev/null
echo "$LD_PRELOAD"
# PAM modificado
for f in /lib/security/*.so; do rpm -V $(rpm -qf "$f" 2>/dev/null) 2>/dev/null; done
# Módulos de kernel
lsmod | awk '{print $1}' | sort > /tmp/mods
diff /tmp/mods <baseline>
# Procesos ocultos
unhide proc
Mitigación#
- AIDE / Tripwire con baseline revisada tras cambios legítimos.
- auditd con reglas sobre
authorized_keys,/etc/systemd/system/*,/etc/pam.d/*,/etc/ld.so.preload. - Kernel con lockdown (
CONFIG_SECURITY_LOCKDOWN_LSM) para dificultar LKM rootkits. - SELinux / AppArmor con policies que restrinjan qué puede ejecutar cada servicio.
- Immutable config:
chattr +ien archivos críticos (/etc/ld.so.preload,/etc/sudoers). - SSH keys centralizadas con revocation (HashiCorp Vault, Smallstep, Teleport).
- Logs centralizados y protegidos: syslog hacia SIEM externo que no se puede borrar desde el servidor.
Walkthrough: respuesta a un incidente#
Supón que detectas un servidor con tráfico saliente anómalo hacia una IP externa. El flujo de respuesta canónico:
Paso 1 — aislar sin apagar:
# Cortar red excepto la IP del responder
iptables -A OUTPUT -j DROP
iptables -I OUTPUT -d <responder-ip> -j ACCEPT
Apagar perdería memoria volátil (procesos, conexiones). Aislar permite investigar.
Paso 2 — snapshot y copias:
# Memoria con AVML o LiME (si tienes kernel symbols)
avml -o mem.lime
# Disco relevante
tar czf /mnt/responder/etc.tar.gz /etc
tar czf /mnt/responder/home.tar.gz /home /root
# Logs
tar czf /mnt/responder/var-log.tar.gz /var/log
Paso 3 — identificar el proceso malicioso:
# Conexiones activas
ss -tunlp | grep <ip-sospechosa>
# Padre del proceso
ps -ef --forest
Paso 4 — identificar persistencia:
Con el PID del proceso malicioso, saber cómo arrancó:
# Padre (ya has visto ps --forest)
ls -la /proc/<pid>/exe
cat /proc/<pid>/cmdline
cat /proc/<pid>/environ
# ¿Lo lanzó cron? ¿systemd? ¿rc.local?
systemctl status --pid <pid>
Paso 5 — buscar mecanismos de persistencia:
Barrer con el checklist manual visto arriba. Documentar cada hallazgo.
Paso 6 — eliminar y bastionar:
Una vez identificada toda la persistencia, eliminarla, y aplicar las mitigaciones. Reiniciar y verificar que no vuelve.
En servidores críticos, muchos equipos prefieren reemplazar la máquina entera (snapshot limpio, reimagen) en lugar de confiar en la limpieza. Es más caro en tiempo pero más seguro.
Errores comunes#
- Confiar en que
psylsson fiables tras compromiso (rootkits los secuestran). - No tener baseline de archivos críticos.
- SSH con
PermitRootLogin yesy keys sin rotación. /etc/sudoers.d/no vigilado: un archivo nuevo ahí da sudo fácil.- Instalaciones con
curl | shque añaden persistencia como efecto colateral.
Preguntas frecuentes#
¿Qué persistencia es más difícil de detectar en Linux?#
Los LKM rootkits bien escritos son casi invisibles desde el propio sistema. Después, LD_PRELOAD con hooking selectivo. Las clásicas (cron, authorized_keys) caen en cualquier auditoría medianamente seria.
¿Reinstalar el OS garantiza limpieza?#
Sí si es reinstall completo con wipe. Si preservas /home o /etc, puedes arrastrar la persistencia. Los backups también pueden estar comprometidos; hay que verificarlos antes de restaurar.
¿SELinux/AppArmor bloquean persistencias?#
Mitigan muchas (ejecuciones no permitidas, cargas de módulos no firmados). No bloquean todas; cron legítimo con script malicioso sigue funcionando. Son capa adicional, no bala de plata.
¿En contenedores Linux la persistencia es distinta?#
Sí: los contenedores son efímeros por diseño. Persistencia en contenedor muere cuando el pod se reinicia. Para persistir en Kubernetes se persiste en imágenes (push al registry), webhooks admission, Kubernetes resources (Jobs recurrentes, DaemonSets) o se escapa al host como viste en Docker escape.
¿Dónde practico?#
El laboratorio titan-linux de whoami-labs incluye un servidor con varios mecanismos de persistencia instalados intencionadamente y un ejercicio de respuesta: identificar los 8 mecanismos usados, limpiarlos y verificar que el equipo queda limpio.
¿Qué pasa con containers Linux?#
Los contenedores son efímeros por diseño: una persistencia instalada dentro del contenedor muere cuando el pod se reinicia. Para persistir en Kubernetes, un atacante persiste en imágenes (push al registry controlado), webhooks admission, Jobs recurrentes, DaemonSets, o escapa al host (lección docker-escape). En entornos serverless (Lambda, Cloud Run, Azure Functions), la persistencia va en el código de la función, en variables de entorno o en capas Lambda que se reusan.
¿Y en sistemas embedded o IoT?#
En dispositivos embedded (routers baratos, cámaras IP, controladores industriales) la persistencia suele vivir en el firmware (NVRAM, flash). Modificar /etc no sirve porque muchos tienen rootfs read-only que se sobrescribe desde flash en cada boot. Hay que modificar el firmware persistente directamente. Técnicas: flash dumping, firmware reflashing, ataques a bootloader. Por eso un router comprometido rara vez se limpia reiniciando: la persistencia está en un chip que el reinicio no toca.
¿Un servidor sin GUI es más fácil de limpiar?#
En general sí: menos superficie (sin XDG autostart, sin shells gráficas ejecutándose). Pero los mecanismos core (cron, systemd, SSH, LD_PRELOAD, PAM, LKM) siguen presentes. Un servidor headless comprometido por un APT serio sigue requiriendo el trabajo completo de forense.
Resumen#
- Persistencia Linux usa cron, systemd, servicios, SSH keys, shell init, LD_PRELOAD, PAM, LKM rootkits, XDG autostart.
- Sigilo creciente: user autoruns > system autoruns > user-space rootkit > LKM.
- Herramientas: chkrootkit, rkhunter, AIDE, auditd, Wazuh, unhide.
- Mitigación: baseline + FIM + kernel lockdown + SELinux/AppArmor + logs externos.
Al final, dominar el catálogo de persistencias Linux es tanto arte ofensivo como defensivo: saber dónde esconder y dónde mirar son dos caras de la misma habilidad.