Investigación forense en Linux
Del primer archivo sospechoso a un informe respaldado por evidencias
Cuando aparece un proceso extraño, un archivo ejecutable en /tmp o una conexión hacia una dirección desconocida, la reacción más habitual es eliminarlo y reiniciar el servidor.
Eso puede resolver el síntoma, pero también puede destruir las evidencias que permitirían responder las preguntas importantes:
- ¿Qué se ejecutó realmente?
- ¿Con qué usuario y permisos?
- ¿Quién lo lanzó?
- ¿Cómo volvió a iniciarse?
- ¿Qué archivos modificó?
- ¿Se comunicó con otro sistema?
- ¿El incidente afectó únicamente a ese servidor?
Una investigación forense operativa en Linux no consiste en ejecutar un escáner y aceptar su resultado. Consiste en relacionar hechos independientes hasta construir una explicación reproducible del incidente.
Este procedimiento está pensado para administradores y equipos técnicos que necesitan investigar un servidor Linux en funcionamiento. No sustituye una adquisición forense completa ni el asesoramiento legal que puede ser necesario en un caso judicial. NIST distingue entre las técnicas de forense aplicadas a la respuesta operativa y una investigación formal con requisitos legales específicos (NIST SP 800-86).
El principio central: primero preservar, después modificar
Cada orden ejecutada cambia, aunque sea mínimamente, el estado del sistema. Leer un archivo puede actualizar su fecha de acceso; matar un proceso elimina información de /proc; reiniciar borra los procesos y conexiones actuales; instalar herramientas puede sobrescribir archivos y registros.
Antes de contener o borrar:
- Registra el número del caso, el hostname, la fecha y la hora en UTC.
- Define quién está autorizado a intervenir.
- Abre un registro de todas las acciones realizadas.
- Guarda las salidas fuera del árbol que se está investigando, preferiblemente en un medio o servidor de evidencias.
- Calcula el hash de los archivos y recopilaciones que vayas a conservar.
NIST recomienda conservar la integridad y la procedencia de los datos recopilados, registrar las acciones realizadas y estimar el alcance buscando indicadores de compromiso y persistencia en otros activos, no solo en el equipo inicialmente detectado (NIST SP 800-61r3).
Crear el registro de la sesión
En una respuesta operativa, una sesión registrada es mejor que una secuencia reconstruida posteriormente desde el historial del shell:
CASE="/ruta/segura/evidencia/caso-2026-001"
mkdir -p "$CASE"
chmod 700 "$CASE"
script -a -f "$CASE/sesion-$(date -u +%Y%m%dT%H%M%SZ).log"
Dentro de esa sesión, registra el contexto:
date -u
hostname --fqdn
uname -a
id
who
uptime
El directorio CASE es un ejemplo. En un incidente real debe estar en un destino protegido, con espacio suficiente y controles de acceso. No conviene guardar la única copia de la evidencia en el mismo disco que puede estar comprometido.
Fase 1: capturar lo que desaparecerá primero
La información volátil debe recogerse antes de apagar, reiniciar o detener procesos. Empieza por procesos, conexiones, sesiones y configuración de red:
ps -eo user,pid,ppid,stat,lstart,etime,cmd --sort=pid
pstree -aps <PID_SOSPECHOSO>
ss -H -tunap
lsof -nP -i
who
w
last -Fi
ip addr show
ip route show
No todos los comandos estarán instalados en todas las distribuciones. Si una herramienta falta, anótalo; no instales paquetes automáticamente en el sistema investigado sin valorar el impacto. Cuando sea posible, utiliza un conjunto de herramientas confiable desde un medio externo.
La interfaz /proc expone información del kernel sobre cada proceso: línea de comandos, proceso padre, directorio actual, ejecutable, descriptores abiertos, entorno y mapas de memoria. La documentación oficial de /proc describe, entre otras entradas, /proc/PID/exe, /proc/PID/cmdline, /proc/PID/cwd, /proc/PID/fd y /proc/PID/maps.
Para un PID concreto:
PID="<PID_SOSPECHOSO>"
ps -o pid,ppid,user,group,stat,lstart,etime,args -p "$PID"
readlink -v "/proc/$PID/exe"
readlink -v "/proc/$PID/cwd"
tr '\0' ' ' < "/proc/$PID/cmdline"; echo
tr '\0' '\n' < "/proc/$PID/environ" 2>/dev/null
cat "/proc/$PID/status"
ls -l "/proc/$PID/fd" 2>/dev/null
lsof -nP -p "$PID" 2>/dev/null
Un resultado como /ruta/programa (deleted) en /proc/PID/exe o en lsof es relevante: el nombre del archivo ya no está en el directorio, pero el proceso todavía puede estar utilizando el ejecutable abierto. No significa por sí solo que sea malware, pero sí que hay que conservar la información del proceso antes de terminarlo.
¿Cómo saber si un proceso es sospechoso?
No existe un único comando que clasifique un proceso como malicioso. Se considera sospechoso cuando varias señales no encajan con la operación normal del servidor. Por ejemplo:
- Se ejecuta desde
/tmp,/var/tmp,/dev/shmo un directorio oculto. - El ejecutable aparece como
(deleted). - El nombre intenta imitar un proceso legítimo, pero su ruta o su propietario no coinciden.
- No pertenece a ningún paquete conocido y nadie puede explicar su origen.
- Fue iniciado por un
bash,curl,wget,python,crono unidadsystemdinesperada. - Mantiene conexiones externas que el servicio no necesita.
- Consume CPU o memoria sin una razón operativa.
- Vuelve a aparecer después de detenerlo.
Cada señal, por separado, puede tener una explicación legítima. La sospecha aumenta cuando el proceso, su archivo, su proceso padre, su persistencia y su conexión de red forman una misma historia.
Fase 2: identificar el archivo sin ejecutarlo
El archivo sospechoso debe tratarse como evidencia, no como un programa que se puede probar “para ver qué hace”. Recoge metadatos, contenido y controles de acceso sin ejecutarlo:
SUSPECT="/ruta/al/archivo"
stat "$SUSPECT"
file "$SUSPECT"
ls -la --full-time "$SUSPECT"
sha256sum "$SUSPECT"
getfacl -p "$SUSPECT" 2>/dev/null
getcap "$SUSPECT" 2>/dev/null
lsattr "$SUSPECT" 2>/dev/null
Después relaciona el archivo con el sistema de paquetes, cuando corresponda:
# Debian/Ubuntu
dpkg -S "$SUSPECT" 2>/dev/null
dpkg-query -S "$SUSPECT" 2>/dev/null
# RHEL/Fedora/Rocky/AlmaLinux
rpm -qf "$SUSPECT" 2>/dev/null
rpm -V <paquete> 2>/dev/null
Estas comprobaciones ayudan a distinguir un archivo perteneciente a un paquete legítimo de uno creado fuera del mecanismo normal de instalación. No prueban que un paquete sea seguro ni que un archivo no empaquetado sea malicioso: son piezas de contexto.
Conserva una copia de análisis sin alterar el original. Para este procedimiento práctico, crear un archivo tar es suficiente para guardar una copia de trabajo:
tar -cf "$CASE/archivo-sospechoso.tar" -- "$SUSPECT"
sha256sum "$SUSPECT" > "$CASE/hashes.sha256"
sha256sum "$CASE/archivo-sospechoso.tar" >> "$CASE/hashes.sha256"
Las opciones avanzadas --xattrs, --acls y --selinux pueden añadirse cuando se necesite conservar atributos extendidos, ACL o etiquetas de seguridad. No son necesarias para entender el flujo inicial.
sha256sum calcula una huella digital del contenido. Si el archivo cambia, normalmente cambiará también su huella. Sirve para demostrar que la copia conservada coincide con el archivo capturado y para detectar modificaciones posteriores. No identifica por sí solo si un archivo es malware ni lo compara automáticamente con una base de datos de amenazas.
No cambies permisos, propietarios ni atributos antes de completar esta captura. En particular, chmod 000 no detiene un proceso que ya tiene el archivo abierto y puede destruir información útil para explicar cómo se ejecutó.
Fase 3: seguir la relación archivo → proceso → usuario → proceso padre
Un archivo aislado rara vez explica el incidente. La pregunta es quién lo ejecutó y desde dónde.
Construye la cadena de ejecución:
pstree -aps "$PID"
ps -o pid,ppid,user,lstart,args -p "$PID"
PPID="$(ps -o ppid= -p "$PID" | tr -d ' ')"
ps -o pid,ppid,user,lstart,args -p "$PPID"
Repite el recorrido hasta llegar a un servicio, una tarea programada, un shell, una sesión SSH, un contenedor o el proceso de inicio. Un proceso lanzado por cron, systemd, sshd, un servidor web o un intérprete como bash, sh, python o perl conduce a líneas de investigación diferentes.
Comprueba también:
readlink -v "/proc/$PID/root"
cat "/proc/$PID/loginuid" 2>/dev/null
cat "/proc/$PID/cgroup" 2>/dev/null
cat "/proc/$PID/maps" 2>/dev/null
El loginuid, cuando está disponible, puede ayudar a relacionar una acción con la sesión que la originó. El cgroup puede revelar que el proceso pertenece a un contenedor o a una unidad de servicio. Ningún campo debe interpretarse de forma aislada.
Fase 4: buscar persistencia
Eliminar el ejecutable no resuelve el incidente si existe un mecanismo que lo vuelve a crear o ejecutar. Revisa, como mínimo, estas fuentes.
Systemd
systemctl list-unit-files --type=service --type=timer --all
systemctl list-units --type=service --type=timer --all
systemctl list-timers --all
find /etc/systemd/system /run/systemd/system /usr/lib/systemd/system \
-type f -ls 2>/dev/null
find recorre esas carpetas y muestra los archivos de configuración de systemd; -type f limita el resultado a archivos y -ls muestra sus permisos, propietario, tamaño y fecha. 2>/dev/null oculta avisos de permisos. Inspecciona cualquier unidad no reconocida, especialmente si apunta a /tmp, /var/tmp, /dev/shm, un directorio oculto o un intérprete con una orden codificada. Las unidades de servicio y los temporizadores son archivos de configuración administrados por systemd; los temporizadores pueden activar servicios en momentos definidos (systemd.unit, systemd.timer).
Cron y tareas diferidas
for user in $(awk -F: '{print $1}' /etc/passwd); do
crontab -u "$user" -l 2>/dev/null | sed "s/^/$user: /"
done
find /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly \
/etc/cron.monthly /var/spool/cron /var/spool/cron/crontabs \
-type f -ls 2>/dev/null
atq 2>/dev/null
atq lista las tareas de una sola ejecución que están programadas mediante at. Si no devuelve nada, no hay tareas pendientes o el sistema no tiene ese comando instalado. Es distinto de cron, que se utiliza normalmente para tareas recurrentes. Revisa el contenido y también propietario, permisos y fechas. Una entrada legítima puede coexistir con una orden añadida al final de un archivo existente.
Otros puntos frecuentes
grep -R -nE '(/tmp|/var/tmp|/dev/shm|curl|wget|bash -c|python|perl|nc |socat)' \
/etc/profile /etc/profile.d /etc/bash.bashrc /etc/rc.local \
/etc/ssh /etc/pam.d /etc/ld.so.preload 2>/dev/null
find /root /home -maxdepth 3 -type f \( -name authorized_keys -o -name '.bashrc' \
-o -name '.profile' -o -name '.ssh/config' \) -ls 2>/dev/null
También hay que revisar contenedores, hooks de despliegue, servicios de proveedores, sudoers, módulos cargados y mecanismos propios de la aplicación. La lista anterior es un punto de partida, no una garantía de cobertura total.
Fase 5: comprobar red y posible infraestructura de mando
Una conexión externa puede aportar la relación entre proceso, usuario, puerto local y destino remoto:
ss -H -tunap
lsof -nP -iTCP -sTCP:ESTABLISHED
Para una conexión concreta, anota:
- IP y puerto de origen y destino.
- PID y proceso asociado.
- Usuario efectivo.
- Hora de primera y última observación.
- Dominio resuelto, si existe.
- Registros del firewall, proxy, DNS o balanceador.
Una IP desconocida no demuestra por sí misma que exista un compromiso. La conclusión gana fuerza cuando la conexión coincide con un proceso no esperado, un archivo fuera del paquete, una tarea de persistencia o una secuencia temporal coherente.
La contención de red debe coordinarse: cortar una conexión puede impedir que el atacante continúe, pero también puede provocar cambios en el comportamiento del malware o perder una evidencia volátil. Antes de bloquear, registra las conexiones y las reglas actuales. Cuando sea posible, aplica el bloqueo en un punto controlado de la red y conserva los logs.
Fase 6: utilizar los logs con una pregunta concreta
Los logs no deben revisarse al azar. Formula primero una hipótesis:
- ¿Qué usuario creó el archivo?
- ¿Qué proceso lo ejecutó?
- ¿Cuándo se modificó la persistencia?
- ¿Hubo un acceso SSH exitoso o fallido?
- ¿Qué ocurrió justo antes de la conexión externa?
En sistemas con systemd:
journalctl --utc --since "2026-08-25 00:00:00" --until "2026-08-26 00:00:00"
journalctl --utc -u <servicio> --since "2026-08-25 00:00:00"
journalctl --utc _PID="$PID"
Revisa además las rutas habituales, que varían entre distribuciones:
grep -Ei 'sshd|sudo|cron|authentication|session|failed|accepted' \
/var/log/auth.log /var/log/secure /var/log/messages /var/log/syslog \
2>/dev/null
El historial de Bash es una pista, no una grabación completa de la actividad. Puede estar deshabilitado, truncado, manipulado o no corresponder al proceso investigado. No debe ser la única base de una conclusión.
Auditd: registrar lo que ocurra desde este momento
Si auditd ya estaba instalado y tenía reglas adecuadas, puede aportar usuario, PID, proceso padre, llamada y objeto afectado. ausearch consulta los registros y auditctl controla las reglas; en sistemas basados en Red Hat, los registros suelen encontrarse en /var/log/audit/audit.log (documentación de auditoría de Red Hat).
Comprueba el estado antes de modificarlo:
auditctl -s 2>/dev/null
auditctl -l 2>/dev/null
ausearch -ts today -i 2>/dev/null | tail -n 200
Si necesitas vigilar un archivo o directorio después de preservar su estado, añade una regla temporal con una clave identificable:
auditctl -w /ruta/sospechosa -p wa -k caso_2026_001 2>/dev/null
ausearch -k caso_2026_001 -i 2>/dev/null
La regla no reconstruye eventos anteriores a su activación. Además, las distribuciones gestionan la persistencia de reglas de forma distinta; una regla temporal no debe convertirse accidentalmente en un cambio permanente sin documentarlo y revisarlo.
Fase 7: contener, eliminar y verificar
Cuando ya se haya conservado la evidencia, decide la respuesta según el riesgo y la continuidad del servicio:
- Aislar o bloquear la comunicación maliciosa.
- Detener el proceso, si continúa activo.
- Deshabilitar el mecanismo de persistencia.
- Rotar credenciales, claves y tokens que pudieron quedar expuestos.
- Corregir la vulnerabilidad o el vector de acceso.
- Eliminar o poner en cuarentena los artefactos, conservando su hash y ubicación original.
- Repetir las comprobaciones después de reiniciar los servicios.
No confundas “el archivo ya no aparece” con “el sistema está limpio”. Verifica que:
pgrep -af '<indicador>'
ss -H -tunap
find /tmp /var/tmp /dev/shm -xdev -type f -perm /111 -ls 2>/dev/null
for p in /proc/[0-9]*; do
exe=$(readlink "$p/exe" 2>/dev/null || true)
case "$exe" in
*'(deleted)'*) echo "$p -> $exe" ;;
esac
done
Después de una ventana de observación, repite la revisión de persistencia y las conexiones. Si un archivo vuelve a aparecer, la pregunta deja de ser “¿cómo lo borro?” y pasa a ser “¿qué proceso lo está recreando y desde qué mecanismo?”.
Si existe evidencia de que el atacante obtuvo privilegios de root, la opción más confiable para recuperar la confianza suele ser reconstruir el sistema desde una imagen y paquetes confiables, restaurar datos validados y rotar todos los secretos. La ausencia de indicadores conocidos en un sistema comprometido no equivale a una certificación de integridad.
Cómo convertir los hallazgos en un informe
Un informe técnico útil debe permitir que otra persona reproduzca el razonamiento. Una estructura práctica es:
1. Resumen ejecutivo
Qué se observó, qué se confirmó, qué impacto se identificó y cuál es la recomendación inmediata.
2. Alcance y limitaciones
Hostname, IP, sistema operativo, periodo analizado, cuentas y servicios revisados, herramientas disponibles y datos que no pudieron obtenerse.
3. Línea temporal
| Hora UTC | Evidencia | Interpretación | Confianza |
|---|---|---|---|
| 2026-08-25 10:14 | Proceso ejecutándose con usuario X | El archivo estaba activo | Alta |
| 2026-08-25 10:15 | /proc/PID/exe apunta a temporal |
El proceso se originó en un temporal | Alta |
| 2026-08-25 10:16 | Conexión TCP al destino Y | Existía comunicación saliente | Alta |
| 2026-08-25 10:20 | Unidad systemd modificada | Posible persistencia | Media, pendiente de correlación |
4. Evidencias
Para cada archivo, log o captura: identificador, ruta, fecha de adquisición, responsable, tamaño, hash SHA-256 y ubicación de conservación.
5. Hallazgos y razonamiento
Separa claramente:
- Confirmado: observado directamente y respaldado por una evidencia concreta.
- Consistente con: hipótesis que encaja con varias evidencias, pero no está demostrada.
- No observado: no apareció durante el periodo y alcance examinados.
- No determinable: falta evidencia suficiente para concluir.
6. Acciones y recomendaciones
Qué se hizo, qué queda pendiente, qué credenciales deben rotarse, qué vulnerabilidad debe corregirse y si conviene reconstruir el servidor.
Conclusión
La investigación forense en Linux no termina cuando desaparece el archivo sospechoso. Termina cuando se puede explicar, con evidencias, la cadena completa:
indicio → archivo → proceso → usuario → proceso padre → persistencia → conexión → contención → validación
Si alguno de esos enlaces falta, la conclusión debe reflejar esa limitación. Un informe profesional no necesita afirmar más de lo que los datos permiten; necesita demostrar por qué cada afirmación es válida.
