Aviso ético y legal: los comandos, rutas y técnicas descritos en este módulo se explican con fines exclusivamente educativos y de defensa. Aplícalos únicamente en sistemas sobre los que tengas autorización explícita y por escrito (tu propio laboratorio, un entorno corporativo donde actúes con mandato formal de respuesta a incidentes, o una plataforma de práctica legal). Acceder, analizar o extraer artefactos de un sistema Linux ajeno sin autorización constituye un delito conforme a los artículos 197 y 264 del Código Penal español, además de vulnerar de forma grave la cadena de custodia y la ética profesional DFIR. Todas las IP, hashes, nombres de usuario y rutas de ejemplo de este módulo son ficticios o pertenecen a rangos reservados para documentación (RFC 5737, RFC 3849) salvo que se indique lo contrario.
Qué aprenderás
- Dónde vive cada log crítico en un Linux comprometido según la familia de distribución (Debian/Ubuntu vs. RHEL/CentOS/Fedora) y cómo leerlos con
journalctl,last,lastbylastlog. - Por qué
~/.bash_historyno lleva marca de tiempo por defecto, cómo activarla retroactivamente conHISTTIMEFORMATy las diferencias con~/.zsh_history. - Cómo auditar usuarios y escalada de privilegios revisando
/etc/passwd,/etc/shadow,/etc/sudoersy el rastro desudoen los logs de autenticación. - Los doce mecanismos de persistencia más habituales en Linux —cron, systemd, rc.local, shell rc files,
ld.so.preload, claves SSH, módulos del kernel y binarios SUID/SGID anómalos— y el comando exacto para detectar cada uno. - Cómo usar
/proc/PID/para inspeccionar procesos vivos y recuperar de memoria un binario malicioso que se ha borrado del disco tras ejecutarse. - Los fundamentos de
auditdyausearchpara reconstruir qué proceso hizo qué, cuándo y con qué identidad. - Cómo construir una timeline forense unificada con log2timeline/plaso y analizarla con psort o Timeline Explorer.
- Por qué no debes confiar en los binarios del propio host comprometido, y cómo trabajar con herramientas estáticas de confianza.
1. Por qué el forense en Linux exige un modelo mental distinto al de Windows
Si vienes de forense en Windows, el primer choque es la ausencia de un «Visor de eventos» centralizado con IDs numerados y estandarizados. En Linux, la telemetría está fragmentada entre ficheros de texto plano (tradición syslog), el journal binario de systemd (journalctl), bases de datos binarias de sesión (wtmp, btmp, lastlog) y, si está desplegado, el subsistema de auditoría del kernel (auditd). No hay un único «Event ID 4688» que te diga «se creó un proceso»; tienes que correlacionar varias fuentes independientes, cada una con su propio formato.
La segunda diferencia es filosófica: en Linux, casi todo es un fichero, y casi toda persistencia se reduce a «¿qué se ejecuta automáticamente y desde dónde?». Esto simplifica el análisis una vez interiorizas el patrón, porque la superficie de persistencia —aunque amplia— es enumerable y verificable con comandos estándar. El objetivo de este módulo es que memorices esa lista de ubicaciones y el comando exacto para auditarlas, de forma que un triaje inicial de persistencia te lleve minutos, no horas.
2. Logs clave y sus rutas exactas
2.1 Autenticación: auth.log vs. secure
El log de autenticación es la primera parada en cualquier triaje de compromiso, porque registra logins SSH, uso de sudo, cambios de sesión (su) y fallos de PAM. Su ruta depende de la familia de distribución:
- Debian, Ubuntu y derivados:
/var/log/auth.log(rotado comoauth.log.1,auth.log.2.gz, etc.). - RHEL, CentOS, Fedora, Rocky, AlmaLinux:
/var/log/secure.
# Ver los últimos accesos SSH exitosos (Debian/Ubuntu)
grep "Accepted" /var/log/auth.log
# Ver intentos fallidos de autenticación SSH
grep "Failed password" /var/log/auth.log | tail -50
# Mismo triaje en RHEL/CentOS
grep -E "Accepted|Failed password" /var/log/secure
# Contar IPs de origen de fuerza bruta SSH, ordenadas por volumen
grep "Failed password" /var/log/auth.log | grep -oE "[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}" | sort | uniq -c | sort -rn | head -20
# Rastro de uso de sudo: quién ejecutó qué, como quién
grep "sudo:" /var/log/auth.log | grep "COMMAND="
# Cambios de sesión con su/su -
grep -E "session opened for user root" /var/log/auth.log
Un patrón de compromiso clásico que debes buscar activamente: un aluvión de Failed password desde una IP concreta seguido, en cuestión de minutos u horas, de un único Accepted password o Accepted publickey desde esa misma IP. Eso es fuerza bruta exitosa. Anótalo en tu timeline con la marca de tiempo exacta: es tu candidato a «hora de acceso inicial».
2.2 syslog y messages: el log general del sistema
El log general —no solo de autenticación— también sigue la misma división por familia de distribución:
- Debian/Ubuntu:
/var/log/syslog. - RHEL/CentOS/Fedora:
/var/log/messages.
Aquí aparecen eventos de kernel, arranque de servicios, cron ejecutándose, mensajes de systemd y errores de aplicaciones que loguean vía syslog(). Es tu segunda parada tras auth.log/secure, especialmente para correlacionar «¿qué proceso arrancó justo cuando entró el atacante?».
2.3 journalctl: el journal binario de systemd
En cualquier distribución moderna con systemd (lo cual, en 2026, es prácticamente universal salvo Alpine/musl y algunos embebidos), el journal de systemd es la fuente más rica y estructurada, porque indexa por unidad, por PID, por rango temporal exacto y por prioridad, y sobrevive aunque rsyslog no esté instalado o mal configurado.
# Todo el journal desde un momento exacto
journalctl --since "2026-06-28 00:00:00" --until "2026-06-30 23:59:59"
# Solo los logs de una unidad concreta (ej: el servicio SSH)
journalctl -u sshd --since "-24h"
# Logs de un PID concreto (útil para seguir un proceso sospechoso ya identificado)
journalctl _PID=13842
# Solo mensajes de prioridad error o superior, últimas 2 horas
journalctl -p err --since "-2h"
# Seguimiento en vivo (equivalente a tail -f) durante una respuesta activa
journalctl -f
# Exportar el journal completo en formato JSON para procesarlo/timelinearlo
journalctl -o json --since "2026-06-01" > /mnt/evidencia/journal-export.json
# Ver arranques del sistema (útil para detectar reinicios inesperados que oculten rastro)
journalctl --list-boots
Detalle operativo importante: el journal puede estar configurado como volátil (solo en /run/log/journal, se pierde al reiniciar) o persistente (en /var/log/journal, sobrevive reinicios). Comprueba esto pronto con journalctl --disk-usage y comprobando si existe /var/log/journal/: si el atacante reinició el sistema y el journal era volátil, has perdido esa fuente y dependes de syslog/auth.log y de auditd si estaba activo.
2.4 wtmp, btmp y lastlog: sesiones en formato binario
Estos tres ficheros no son legibles con cat —son binarios estructurados (formato utmp)— y se leen con herramientas dedicadas:
| Fichero | Ruta habitual | Contenido | Comando de lectura |
|---|---|---|---|
wtmp |
/var/log/wtmp |
Historial de logins/logouts exitosos y reinicios del sistema | last -f /var/log/wtmp |
btmp |
/var/log/btmp |
Intentos de login fallidos | lastb -f /var/log/btmp |
lastlog |
/var/log/lastlog |
Última vez que cada usuario del sistema inició sesión (indexado por UID) | lastlog |
# Historial completo de logins/logouts, incluyendo reinicios
last -f /var/log/wtmp
# Solo los accesos de un usuario concreto
last -f /var/log/wtmp www-data
# Intentos fallidos de login (btmp requiere permisos root para leerse)
sudo lastb -f /var/log/btmp
# Última sesión de cada cuenta del sistema (detecta cuentas "dormidas" usadas de repente)
lastlog
# Filtrar lastlog para ver solo cuentas con acceso reciente, ignorando "Never logged in"
lastlog | grep -v "Never logged in"
Un IOC de manual: una cuenta de servicio que en lastlog nunca había iniciado sesión interactiva (por ejemplo www-data, backup o una cuenta de aplicación con shell /bin/bash en /etc/passwd en lugar de /usr/sbin/nologin) y que de repente muestra una entrada reciente en last -f /var/log/wtmp. Eso es, casi con total seguridad, una cuenta comprometida siendo usada para movimiento lateral o persistencia.
3. Historial de shell: bash_history y zsh_history
El historial de comandos es oro forense, pero tiene una limitación crítica que todo analista de Linux debe conocer de memoria: ~/.bash_history no guarda marca de tiempo por defecto. Solo contiene la lista de comandos en orden de ejecución, sin fecha ni hora, salvo que la variable HISTTIMEFORMAT estuviera activada antes de que se ejecutaran esos comandos.
# Ver el historial de un usuario (requiere permisos de lectura sobre su home)
cat /home/usuario/.bash_history
# Si HISTTIMEFORMAT ya estaba activo, verás timestamps al usar el comando "history"
# dentro de una sesión interactiva de ese usuario:
export HISTTIMEFORMAT="%F %T "
history
# Activar timestamps de cara al futuro (no retroactivo) en el .bashrc del usuario o global
echo 'export HISTTIMEFORMAT="%F %T "' >> /etc/profile.d/histtime.sh
# Buscar comandos sospechosos típicos de post-explotación en TODOS los historiales del sistema
grep -riE "wget|curl|nc -e|/dev/tcp|base64 -d|chmod +x|python.*socket" /home/*/.bash_history /root/.bash_history 2>/dev/null
Puntos que debes verificar activamente en cualquier triaje:
- ¿El fichero existe pero está vacío o truncado? Un atacante competente suele borrar su historial (
history -corm ~/.bash_history) o, más sutilmente, exportarHISTFILE=/dev/nullounset HISTFILEal inicio de su sesión para que nada se registre desde ese momento. Un.bash_historyanormalmente pequeño o inexistente en una cuenta que sí tiene actividad reciente enwtmpes en sí mismo un indicador de manipulación antiforense. - Comprueba el enlace simbólico. Algunas técnicas antiforenses enlazan
~/.bash_historya/dev/nullconln -sf /dev/null ~/.bash_history, de forma que nada se escribe nunca. Verifícalo conls -la ~/.bash_history. - Metadatos del propio fichero. Aunque el contenido no tenga timestamps internos, el
mtimedel fichero.bash_historyte dice cuándo se escribió por última vez, lo cual acota una ventana temporal aunque sea imprecisa:stat /home/usuario/.bash_history.
zsh se comporta de forma distinta y, para el forense, más favorable: ~/.zsh_history incluye marca de tiempo Unix por entrada de forma nativa si la opción EXTENDED_HISTORY está activada (habitual en configuraciones con Oh My Zsh), con el formato : 1719580800:0;comando ejecutado. Si detectas que el atacante o el usuario legítimo usa zsh en lugar de bash, prioriza ese fichero: la información temporal que te falta en bash puede estar ahí.
# Leer zsh_history con marcas de tiempo extendidas (formato: EPOCH:duración;comando)
cat ~/.zsh_history
# Convertir el epoch de una entrada concreta a fecha legible
date -d @1719580800
4. Usuarios y escalada de privilegios
Tres ficheros forman el núcleo de la auditoría de cuentas y privilegios en cualquier sistema Linux:
# /etc/passwd: listado de cuentas, UID, GID, home, shell asignado
cat /etc/passwd
# Buscar cuentas con UID 0 (root) — solo debería existir "root"
awk -F: '$3 == 0 {print $1}' /etc/passwd
# Buscar cuentas de servicio con shell interactivo (posible indicador de backdoor)
awk -F: '$7 ~ /(bash|sh)$/ {print $1, $7}' /etc/passwd | grep -vE "^root|^usuario_legitimo"
# /etc/shadow: hashes de contraseña y política de expiración (requiere root)
sudo cat /etc/shadow
# Detectar cuentas sin contraseña (campo vacío tras el primer ":") — riesgo crítico
sudo awk -F: '($2 == "") {print $1}' /etc/shadow
# /etc/sudoers y /etc/sudoers.d/: quién puede escalar a root y con qué restricciones
sudo cat /etc/sudoers
sudo ls -la /etc/sudoers.d/
sudo cat /etc/sudoers.d/*
# Verificar reglas NOPASSWD (sudo sin contraseña) — vector habitual de escalada
sudo grep -r "NOPASSWD" /etc/sudoers /etc/sudoers.d/ 2>/dev/null
# Rastro de todos los comandos ejecutados con sudo, según auth.log/secure
grep "sudo:.*COMMAND=" /var/log/auth.log | awk -F'COMMAND=' '{print $2}' | sort | uniq -c | sort -rn
Correlaciona siempre /etc/passwd con su mtime (stat /etc/passwd) y con el journal/auth.log de esa misma ventana temporal: la creación de una cuenta nueva o la adición de un usuario a un grupo con privilegios (sudo, wheel, docker —este último es en la práctica equivalente a root, porque un contenedor con el socket de Docker montado permite escapar al host—) suele dejar rastro tanto en el fichero como en el log de auditoría del comando que lo modificó (useradd, usermod, gpasswd).
# Miembros del grupo docker (equivalente a root si tienen acceso al daemon)
getent group docker
# Miembros de sudo/wheel según distribución
getent group sudo
getent group wheel
5. Persistencia en Linux: doce mecanismos y cómo detectarlos
Esta es la sección central del módulo. Un atacante que ha obtenido acceso inicial casi siempre intenta garantizar que sobrevive a un reinicio o a la eliminación de su shell activa. La superficie de persistencia en Linux es amplia pero finita y enumerable; la tabla siguiente cubre los mecanismos que debes revisar de forma sistemática en cada triaje, en el orden aproximado de frecuencia con la que se observan en incidentes reales.
| Mecanismo | Ubicación | Cómo detectarlo |
|---|---|---|
| Cron del sistema | /etc/crontab, /etc/cron.d/, /etc/cron.hourly|daily|weekly|monthly/ |
cat /etc/crontab; ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ |
| Cron de usuario | /var/spool/cron/crontabs/<usuario> (Debian) o /var/spool/cron/<usuario> (RHEL) |
for u in $(cut -f1 -d: /etc/passwd); do crontab -l -u $u 2>/dev/null && echo "== $u =="; done |
| systemd services | /etc/systemd/system/, /usr/lib/systemd/system/, ~/.config/systemd/user/ |
systemctl list-units --type=service --state=running y revisar unidades no reconocidas con systemctl cat <unidad> |
| systemd timers | Mismo árbol que los services, ficheros .timer |
systemctl list-timers --all |
| rc.local (legacy) | /etc/rc.local |
cat /etc/rc.local y comprobar si el servicio rc-local.service está habilitado |
| Shell rc files | ~/.bashrc, ~/.profile, ~/.bash_profile, /etc/profile.d/*.sh |
grep -E "curl|wget|base64|nc |/dev/tcp" ~/.bashrc ~/.profile /etc/profile.d/*.sh 2>/dev/null |
| ld.so.preload (inyección de librerías) | /etc/ld.so.preload |
cat /etc/ld.so.preload 2>/dev/null — este fichero debería estar vacío o no existir en un sistema limpio |
| Claves SSH autorizadas | ~/.ssh/authorized_keys, /etc/ssh/sshd_config (AuthorizedKeysFile) |
find / -name authorized_keys -exec ls -la {} ; -exec cat {} ; 2>/dev/null |
| Módulos del kernel / rootkits LKM | /lib/modules/$(uname -r)/, salida de lsmod |
lsmod comparado contra baseline; chkrootkit/rkhunter (con reservas, ver sección 8) |
| Binarios SUID/SGID anómalos | Todo el filesystem | find / -perm -4000 -type f 2>/dev/null (SUID) y find / -perm -2000 -type f 2>/dev/null (SGID) |
| Servicios inetd/xinetd | /etc/inetd.conf, /etc/xinetd.d/ |
cat /etc/inetd.conf 2>/dev/null; ls -la /etc/xinetd.d/ 2>/dev/null |
| Tareas «at» (ejecución diferida única) | /var/spool/at/ |
atq y ls -la /var/spool/at/ |
| Web shells en el docroot | Directorio raíz del servidor web (p. ej. /var/www/html/) |
find /var/www -mtime -7 -name "*.php" -o -mtime -7 -name "*.jsp" 2>/dev/null |
# Auditoría rápida de cron a nivel de sistema
cat /etc/crontab
ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/
cat /etc/cron.d/*
# Auditoría de cron por usuario (recorre TODAS las cuentas del passwd, no solo la actual)
for u in $(cut -f1 -d: /etc/passwd); do
crontab -l -u "$u" 2>/dev/null && echo "== crontab de $u =="
done
# Listado de servicios y timers de systemd activos, y sus definiciones
systemctl list-units --type=service --state=running --no-pager
systemctl list-timers --all --no-pager
# Inspeccionar el contenido exacto de una unidad sospechosa (ExecStart es la clave)
systemctl cat nombre-servicio-sospechoso.service
# Buscar servicios habilitados que NO forman parte del paquete original de la distro
systemctl list-unit-files --type=service --state=enabled | grep -v "@"
# ld.so.preload: inyección de librerías compartidas en TODOS los procesos del sistema
cat /etc/ld.so.preload 2>/dev/null
ls -la /etc/ld.so.preload 2>/dev/null
# Claves SSH autorizadas en cualquier home del sistema (incluye /root)
find / -xdev -name "authorized_keys" -exec ls -la {} ; -exec echo "---" ; -exec cat {} ; 2>/dev/null
# Módulos del kernel cargados, para comparar contra una baseline conocida
lsmod
# Búsqueda de binarios SUID/SGID en TODO el filesystem, redirigiendo errores de permisos
find / -perm -4000 -type f 2>/dev/null
find / -perm -2000 -type f 2>/dev/null
# Comparar el listado SUID actual contra un baseline guardado en tu fase de preparación
diff <(find / -perm -4000 -type f 2>/dev/null | sort) /mnt/baseline/suid-baseline.txt
# Tareas "at" pendientes (ejecución diferida, a menudo pasada por alto)
atq
ls -la /var/spool/at/ 2>/dev/null
Un matiz importante sobre la tabla anterior: en un incidente real, un atacante mínimamente competente no usa un único mecanismo, sino persistencia en capas —por ejemplo, un cron job discreto que cada hora verifica si su clave SSH sigue en authorized_keys y la reinserta si el defensor la ha borrado—. Por eso, cuando encuentres un primer mecanismo de persistencia, no cierres el caso: sigue recorriendo la tabla completa. Es habitual encontrar dos o tres mecanismos redundantes en el mismo host.
6. Análisis de /proc: procesos vivos y binarios borrados en ejecución
El sistema de ficheros virtual /proc es, posiblemente, la herramienta forense más infravalorada por analistas que empiezan en Linux. Cada proceso en ejecución tiene un directorio /proc/<PID>/ con metadatos en tiempo real que no existen en ningún log: qué binario ejecuta exactamente, con qué línea de comandos completa, qué ficheros tiene abiertos, qué mapa de memoria usa y —crucialmente— si el binario que está ejecutando ha sido borrado del disco tras arrancar el proceso, una técnica antiforense extremadamente común en malware Linux.
# Listar todos los PID activos con su línea de comandos completa
for pid in /proc/[0-9]*; do
echo "== $pid =="
cat "$pid/cmdline" 2>/dev/null | tr ' ' ' '
echo
done
# Ver a qué binario apunta realmente el proceso (enlace simbólico exe)
ls -la /proc/1234/exe
# Detectar binarios borrados pero AÚN EN EJECUCIÓN: el enlace exe mostrará "(deleted)"
ls -la /proc/*/exe 2>/dev/null | grep deleted
# Recuperar del disco (vía memoria) el binario borrado de un proceso concreto
# El kernel mantiene el inodo vivo mientras el proceso lo tenga abierto, así que /proc/PID/exe
# sigue siendo un fichero leíble aunque "ls" del directorio original ya no lo muestre
cp /proc/1234/exe /mnt/evidencia/binario-recuperado-pid1234
# Ver la línea de comandos completa (separada por NUL, cmdline la concatena)
cat /proc/1234/cmdline | tr ' ' 'n'
# Variables de entorno con las que arrancó el proceso (puede revelar C2, tokens, paths)
cat /proc/1234/environ | tr ' ' 'n'
# Directorio de trabajo actual del proceso
ls -la /proc/1234/cwd
# Ficheros abiertos por el proceso — sockets, pipes, ficheros de log propios, etc.
ls -la /proc/1234/fd/
# Mapa de memoria del proceso: regiones cargadas, librerías vinculadas, permisos rwx
cat /proc/1234/maps
# Volcar la memoria completa del proceso para análisis posterior offline (ej. con Volatility/strings)
cat /proc/1234/mem > /mnt/evidencia/pid1234.mem 2>/dev/null
# Estado y árbol de parentesco del proceso (PID padre, UID real/efectivo, estado)
cat /proc/1234/status | grep -E "Name|PPid|Uid|Gid|State"
# Buscar en TODO /proc procesos cuyo binario ya no existe en el filesystem, en un solo comando
for exe in /proc/[0-9]*/exe; do
pid=$(echo "$exe" | cut -d/ -f3)
target=$(readlink "$exe" 2>/dev/null)
if [[ "$target" == *"(deleted)"* ]]; then
echo "PID $pid ejecuta binario BORRADO: $target"
cat "/proc/$pid/cmdline" 2>/dev/null | tr ' ' ' '; echo
fi
done
La técnica de «ejecutar y luego borrar el binario del disco» es efectiva contra un analista que solo revisa el filesystem con find o un antivirus que escanea disco, porque el fichero literalmente ya no está ahí para ninguna herramienta que no consulte /proc. El kernel de Linux, sin embargo, mantiene el inodo del fichero vivo mientras exista al menos un proceso con un descriptor de fichero abierto sobre él —propiedad fundamental de cómo funciona unlink() en Unix—, y por eso /proc/<PID>/exe sigue apuntando a contenido recuperable mientras el proceso siga vivo. En el momento en que termina, el inodo se libera de verdad y el binario desaparece para siempre: si detectas un caso así, tu prioridad es volcar /proc/PID/exe a evidencia antes de matar el proceso o de que el sistema se reinicie.
7. auditd y ausearch
El subsistema de auditoría del kernel Linux (auditd) es, cuando está desplegado y correctamente configurado, la fuente más granular de todas: permite registrar llamadas al sistema concretas, accesos a ficheros específicos, ejecución de binarios y cambios de identidad, con precisión de PID, UID real/efectivo y línea de comandos. Su gran limitación es que, como todo lo demás en este módulo, debe estar activo antes del incidente para servir de algo —no es retroactivo—.
# Verificar si auditd está corriendo
systemctl status auditd
# Ver las reglas de auditoría activas
auditctl -l
# Reglas típicas de interés forense: vigilar ejecución de binarios sensibles
auditctl -w /usr/bin/sudo -p x -k sudo_exec
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/shadow -p wa -k shadow_changes
auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config_changes
# Buscar eventos por "key" (etiqueta) definida en las reglas anteriores
ausearch -k passwd_changes
# Buscar eventos de ejecución de un binario concreto (execve) en una ventana temporal
ausearch -m EXECVE --start today
# Buscar todos los eventos asociados a un UID concreto
ausearch -ui 1001
# Buscar eventos de autenticación fallida capturados directamente por el kernel
ausearch -m USER_AUTH -sv no
# Generar un informe resumido y legible a partir del log crudo de auditd
aureport --summary
# Informe específico de ejecución de comandos
aureport -x --summary
# El log crudo, si prefieres inspeccionarlo directamente (formato clave=valor por línea)
less /var/log/audit/audit.log
Un flujo de trabajo realista: si tu organización despliega auditd con el conjunto de reglas recomendado por el CIS Benchmark de Linux (que incluye vigilancia de /etc/passwd, /etc/shadow, /etc/sudoers, montajes, cambios de hora del sistema y ejecución de utilidades de escalada), un incidente que en un sistema sin auditd te llevaría reconstruir por inferencia a partir de wtmp y bash_history se convierte en una secuencia de eventos ausearch con PID, UID, línea de comandos y resultado (éxito/fallo) exactos. La inversión en desplegar auditd correctamente en fase de preparación es, sistemáticamente, una de las que más acorta el tiempo de investigación en la fase de respuesta.
8. Construcción de una timeline unificada con log2timeline/plaso
Cuando el número de fuentes a correlacionar crece —journal, auth.log, wtmp, mtime de ficheros del filesystem, historial de bash, logs de aplicación—, hacerlo a mano con grep deja de escalar. Plaso (Plaso Langar Ad Ósýnileika, «log2timeline de próxima generación») es el framework estándar de la industria para generar una super-timeline: un único cuerpo de eventos ordenado cronológicamente que fusiona decenas de tipos de artefactos distintos en un formato común.
# Procesar una imagen de disco completa (o un directorio con una copia montada de la evidencia)
# y generar un "storage file" .plaso — este es el paso que tarda más tiempo
log2timeline.py /mnt/evidencia/salida.plaso /mnt/evidencia/imagen-disco.dd
# Procesar solo un directorio concreto (por ejemplo, si ya extrajiste /var/log y los homes)
log2timeline.py /mnt/evidencia/salida.plaso /mnt/evidencia/artefactos-extraidos/
# Ver qué parsers detectó y usó plaso sobre la fuente (útil para validar cobertura)
log2timeline.py --parsers list
# Limitar explícitamente los parsers a los relevantes para Linux (más rápido, menos ruido)
log2timeline.py --parsers "linux,syslog,bash_history,selinux,systemd_journal" /mnt/evidencia/salida.plaso /mnt/evidencia/artefactos-extraidos/
# Convertir el storage file a una timeline en texto plano ordenada cronológicamente
psort.py -o dynamic -w /mnt/evidencia/timeline.csv /mnt/evidencia/salida.plaso
# Filtrar la timeline resultante a una ventana temporal concreta del incidente
psort.py -o dynamic -w /mnt/evidencia/timeline-ventana.csv "date > '2026-06-28 00:00:00' AND date < '2026-06-30 23:59:59'" /mnt/evidencia/salida.plaso
# Exportar en formato compatible con Timeline Explorer / análisis en hoja de cálculo
psort.py -o l2tcsv -w /mnt/evidencia/timeline-l2t.csv /mnt/evidencia/salida.plaso
Una vez generado el CSV, herramientas como Timeline Explorer (de Eric Zimmerman; aunque nació orientado a Windows, funciona perfectamente sobre CSV de origen Linux) te permiten filtrar por columna, resaltar eventos de interés y buscar texto libre sobre cientos de miles de filas con fluidez. El valor real de la super-timeline no es solo «todo en un sitio», sino detectar eventos correlacionados en el tiempo que no lo estaban en ninguna fuente individual: por ejemplo, ver en la misma ventana de 90 segundos un Accepted publickey en auth.log, la creación de un fichero en /etc/cron.d/ según su mtime, y una entrada del journal reiniciando el servicio cron —secuencia que, mirando cada log por separado, podrías no conectar nunca—.
9. Laboratorio: hallar la persistencia en una VM Linux comprometida
Este laboratorio asume que dispones de una máquina virtual de práctica deliberadamente comprometida (por ejemplo, un escenario tipo «boot2root» descargado de una plataforma legal como VulnHub o construido por ti mismo en un laboratorio aislado, nunca en un sistema de producción ni de terceros). El objetivo es aplicar el flujo completo de este módulo de principio a fin.
- Snapshot antes de tocar nada. Si trabajas sobre la VM en caliente (no sobre una imagen forense estática), toma una snapshot del hipervisor antes de cualquier interacción, para poder volver al estado exacto si cometes un error.
- Triaje de sesiones. Ejecuta
last -f /var/log/wtmp,sudo lastb -f /var/log/btmpylastlog | grep -v "Never logged in". Identifica cualquier cuenta con actividad que no encaje con el uso esperado del sistema. - Revisión de auth.log/secure. Busca el patrón de fuerza bruta seguido de éxito descrito en la sección 2.1. Anota la IP de origen y la hora exacta del
Accepted. - Auditoría de cuentas. Revisa
/etc/passwdbuscando UID 0 duplicados o cuentas de servicio con shell interactivo. Revisa/etc/sudoersy/etc/sudoers.d/buscando reglasNOPASSWDañadidas fuera de la configuración por defecto de la distro. - Barrido completo de persistencia. Recorre, en orden, cada fila de la tabla de la sección 5: cron de sistema y de usuario, unidades systemd,
rc.local, shell rc files,/etc/ld.so.preload,authorized_keysen todos los homes,lsmod, y el barrido completo de SUID/SGID confind / -perm -4000 -type f 2>/dev/null. Documenta cada hallazgo con su ruta exacta y sumtime/ctime. - Procesos vivos. Lista todos los PID con
ps auxfy cruza contra/proc/*/exebuscando la cadena(deleted). Si encuentras un proceso con binario borrado, vuelca/proc/PID/exea un fichero de evidencia antes de continuar. - Reconstrucción con auditd (si estaba activo). Ejecuta
ausearch -m EXECVE --start todayyaureport --summarypara ver si el subsistema de auditoría capturó ejecución de comandos que confirme o amplíe tus hallazgos anteriores. - Timeline final. Con los hallazgos de los pasos 2 a 7, construye a mano (o con
log2timeline.pysi dispones de una copia de los artefactos) una timeline en orden cronológico: acceso inicial → creación de cuenta/clave/cron → primera ejecución del mecanismo de persistencia → acciones posteriores observadas. - Informe. Redacta un resumen de una página: vector de acceso inicial, cuenta(s) comprometida(s), mecanismo(s) de persistencia hallados con su ruta exacta, y recomendación de erradicación para cada uno.
10. Errores comunes
- Confiar en las herramientas del propio host comprometido. Si el atacante ha desplegado un rootkit LKM, puede haber interceptado las llamadas al sistema que usan
ps,ls,netstato inclusofindpara ocultar sus propios procesos, ficheros y conexiones. Ejecutar tus comandos de triaje con los binarios instalados en el sistema comprometido es, en presencia de un rootkit competente, forense de baja confianza. La práctica correcta es usar binarios estáticos de confianza traídos desde fuera (por ejemplo, herramientasbusyboxestático montadas desde un USB/ISO de confianza) o, mejor aún, analizar una imagen forense del disco montada en solo lectura desde un sistema de análisis limpio, nunca con análisis interactivo sostenido sobre el host vivo comprometido. - Ignorar mtime/atime/ctime, o confundirlos entre sí.
mtime(modification time) cambia cuando se modifica el contenido del fichero;ctime(change time, no «creation time» pese al nombre) cambia cuando se modifican los metadatos del inodo (permisos, propietario, o el propio contenido);atime(access time) cambia al leer el fichero, pero muchos sistemas modernos montan connoatimeorelatimepor rendimiento, lo que lo hace poco fiable. Es un error común asumir quectimees «cuándo se creó» el fichero: Linux, a diferencia de NTFS, no tiene un campo estándar de fecha de creación en la mayoría de sistemas de ficheros (ext4 guardacrtimeinternamente, perolsno lo muestra; se necesitadebugfs -R 'stat <fichero>' /dev/sdaXostatx()). Verifica siempre los tres valores constat <fichero>y razona con precisión qué evento explica cada uno. - Detenerse en el primer mecanismo de persistencia encontrado. Como se explicó en la sección 5, la persistencia en capas es habitual. Encontrar una clave SSH añadida no significa que no haya también un cron job o un servicio systemd de respaldo.
- No comprobar si el journal de systemd es volátil. Si asumes que
journalctlte dará semanas de histórico y en realidad solo estaba configurado en/run/log/journal(volátil), un reinicio —accidental o provocado por el atacante como técnica antiforense— te habrá dejado sin esa fuente sin que lo supieras hasta que ya era tarde. - Olvidar los timers de systemd al auditar solo cron. Un analista que solo revisa
/etc/crontaby los directorioscron.*puede pasar por alto por completo unsystemd timer, que en distribuciones modernas es cada vez más el mecanismo preferido —tanto por administradores legítimos como por atacantes— para tareas programadas. - No fijar la evidencia volátil de un proceso con binario borrado antes de intervenir. Si matas el proceso, reinicias el servicio o el sistema, o simplemente esperas demasiado, el inodo del binario borrado se libera y esa evidencia desaparece de forma irrecuperable.
11. Ejercicio
Se te entrega acceso a una VM de laboratorio con un Linux Ubuntu Server comprometido (entorno aislado, sin conexión a redes de producción). Los indicios iniciales, según el equipo que reportó el incidente, son: «la carga de CPU sube de forma anómala cada hora en punto, y un desarrollador reporta que su clave SSH personal ya no funciona pero sigue pudiendo entrar con una contraseña que no recuerda haber configurado». Realiza el siguiente análisis y documenta cada paso con el comando exacto usado y su salida relevante:
- Determina la hora aproximada del acceso inicial usando
auth.log/securey correlaciónala conwtmp. - Identifica el mecanismo responsable del pico de CPU horario (pista: revisa tanto cron como
systemctl list-timers --all). - Explica, con evidencia de
/etc/ssh/sshd_configy de/etc/passwd//etc/shadow, por qué la clave SSH del desarrollador dejó de funcionar mientras una contraseña desconocida sí funciona (pista: revisa siPasswordAuthenticationfue reactivado y si el hash de la cuenta en/etc/shadowtiene unmtimereciente). - Recorre la tabla completa de la sección 5 y documenta TODOS los mecanismos de persistencia presentes en la VM (el laboratorio contiene más de uno de forma deliberada).
- Localiza al menos un proceso vivo cuyo binario haya sido borrado del disco tras su ejecución, y recupéralo vía
/proc/PID/exe. - Entrega una timeline final en orden cronológico con los hallazgos de los cinco puntos anteriores.
Preguntas frecuentes
¿Qué diferencia hay entre /var/log/auth.log y /var/log/secure, y por qué solo existe uno de los dos en cada sistema?
Ambos cumplen exactamente la misma función —registrar autenticación, uso de sudo y cambios de sesión vía PAM—, pero pertenecen a dos linajes distintos de configuración de rsyslog/syslog-ng. auth.log es la convención de Debian y sus derivados (Ubuntu, Linux Mint, Kali); secure es la convención de Red Hat y sus derivados (CentOS, Fedora, Rocky Linux, AlmaLinux). Nunca vas a encontrar ambos activos con contenido real en el mismo sistema salvo que alguien haya reconfigurado manualmente el syslog; identifica primero la distribución con cat /etc/os-release para saber cuál buscar directamente, en lugar de perder tiempo comprobando los dos.
¿Por qué mi bash_history no tiene marcas de tiempo aunque active HISTTIMEFORMAT ahora mismo?
Porque HISTTIMEFORMAT solo afecta a cómo se muestra el historial en memoria de la sesión activa (comando history) a partir del momento en que la activas, y a cómo se escriben las entradas nuevas si además tienes HISTFILE configurado para persistir con timestamp. No reescribe retroactivamente las entradas que ya estaban en ~/.bash_history desde antes: esas quedan para siempre sin marca de tiempo, salvo que la variable ya estuviera activa en el momento exacto en que se ejecutaron. Es una limitación estructural de bash, no un fallo de configuración que puedas «arreglar» a posteriori sobre datos ya escritos.
¿Es fiable ejecutar rkhunter o chkrootkit directamente en el sistema que sospecho comprometido?
Con reservas importantes. Ambas herramientas son útiles como primera señal de alarma, pero si el compromiso incluye un rootkit a nivel de kernel (LKM) que intercepta llamadas al sistema, el propio rkhunter/chkrootkit ejecutándose en ese sistema puede recibir información falseada por el rootkit y no detectar nada, dándote una falsa sensación de limpieza. Úsalas como triaje rápido inicial, nunca como veredicto final: la validación de confianza pasa por analizar una imagen del disco montada en modo solo lectura desde un sistema de análisis limpio, o por comparar hashes de binarios del sistema contra una baseline conocida y fiable (por ejemplo, los paquetes originales del gestor de paquetes de la distro, verificados con debsums en Debian/Ubuntu o rpm -Va en RHEL/CentOS).
¿Qué debo priorizar si tengo tiempo limitado: revisar auditd o construir la timeline completa con plaso?
Depende de si auditd estaba desplegado antes del incidente. Si lo estaba, ausearch/aureport te da respuestas más rápidas y con mayor precisión (PID, UID exacto, línea de comandos, resultado) que una timeline genérica, así que revísalo primero. Si auditd no estaba activo —el caso más común en la práctica, porque muchas organizaciones no lo despliegan por defecto—, tendrás que depender de la correlación manual de auth.log, wtmp, journal y mtimes del filesystem; en ese escenario, construir la super-timeline con plaso cuanto antes es lo que te permite ver el panorama completo sin perderte fuente por fuente. En un incidente real con recursos limitados, el orden recomendado es: triaje manual rápido de las fuentes más ricas primero (sección 2 a 7 de este módulo) para acotar la ventana temporal del compromiso, y reservar el procesado completo con plaso —que puede tardar horas sobre una imagen grande— para la fase de análisis en profundidad una vez tengas esa ventana acotada.
¿Por qué encontrar una clave en authorized_keys no basta para dar por cerrada la investigación de persistencia?
Porque, como se explica en la sección 5, un atacante que ha invertido esfuerzo en comprometer el sistema rara vez confía en un único mecanismo de persistencia. Es una práctica extendida en incidentes reales encontrar una clave SSH añadida junto con, por ejemplo, un cron job que verifica periódicamente que esa clave sigue presente y la reinserta si un defensor la elimina sin tratar la causa raíz, además de una cuenta de respaldo con UID 0 duplicado por si la cuenta original es deshabilitada. Cerrar la investigación tras el primer hallazgo es uno de los motivos más frecuentes por los que un incidente «resuelto» reaparece semanas después: el defensor eliminó el síntoma visible pero dejó intactos los mecanismos redundantes.
«Plaso, or plaso log2timeline, is a Python-based backend engine used by tools such as log2timeline for automatic creation of a super timeline. The tool is designed to extract timestamps from various files found on a typical computer system(s) and aggregate them into a single timeline.»
— Plaso (log2timeline) — Official Documentation, plaso.readthedocs.io
