Bash para Incident Response en Linux: Scripts de Triaje y Recolección
Scripts Bash para respuesta a incidentes en Linux: recolección de información del sistema, análisis de procesos, conexiones de red, mecanismos de persistencia, empaquetado de logs y cadena de custodia con hashing. Triaje automatizado para analistas SOC.
Por qué Bash para incident response
Cuando un servidor Linux muestra comportamiento anómalo, el analista SOC necesita respuestas rápidas. ¿Qué procesos están corriendo? ¿Hay conexiones de red sospechosas? ¿Alguien instaló persistencia? Las herramientas forenses comerciales son potentes, pero requieren instalación, licencias y tiempo de configuración. Bash, en cambio, está ahí. Siempre.
Un buen script de triaje Bash hace tres cosas: recolecta datos volátiles antes de que desaparezcan, organiza la salida de forma estructurada y genera hashes para cadena de custodia. En este artículo construiremos cinco scripts especializados y uno que los combina todos en un triaje automatizado completo.
Orden de volatilidad
Antes de ejecutar cualquier script, recuerda el orden de volatilidad (RFC 3227). Los datos más efímeros se recolectan primero:
- Registros de CPU y caché
- Memoria RAM (procesos, conexiones activas)
- Estado de red (tablas de routing, conexiones)
- Procesos en ejecución
- Sistema de archivos (timestamps, archivos temporales)
- Disco (logs, configuración, datos persistentes)
Nuestros scripts siguen este orden de forma deliberada.
Preparación del entorno
Antes de empezar, crea un directorio de salida con un nombre que incluya el hostname y la fecha. Toda la evidencia irá ahí:
#!/bin/bash
# prepare_output.sh — Crear directorio de evidencia
HOSTNAME_SHORT=$(hostname -s)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
OUTPUT_DIR="/tmp/ir_${HOSTNAME_SHORT}_${TIMESTAMP}"
mkdir -p "$OUTPUT_DIR"
echo "[*] Directorio de evidencia: $OUTPUT_DIR"
Si es posible, monta un USB o recurso de red para el directorio de salida en lugar de escribir en el disco local del sistema comprometido.
Script 1: Recolección de información del sistema
El primer script captura el contexto básico: quién es esta máquina, cuánto tiempo lleva encendida, quién está conectado y cuál es su configuración de red. Estos datos son críticos para contextualizar todo lo que venga después.
#!/bin/bash
# collect_system_info.sh — Información básica del sistema
OUT="$1/01_system_info.txt"
echo "========== SYSTEM INFO ==========" > "$OUT"
echo "[Timestamp] $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> "$OUT"
echo "" >> "$OUT"
echo "--- Hostname ---" >> "$OUT"
hostname -f 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Uptime ---" >> "$OUT"
uptime >> "$OUT"
echo "" >> "$OUT"
echo "--- Kernel / OS ---" >> "$OUT"
uname -a >> "$OUT"
cat /etc/os-release 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Usuarios conectados ---" >> "$OUT"
w >> "$OUT"
echo "" >> "$OUT"
echo "--- Últimos logins ---" >> "$OUT"
last -n 25 >> "$OUT"
echo "" >> "$OUT"
echo "--- Usuarios con UID 0 (root) ---" >> "$OUT"
awk -F: '$3 == 0 {print $1}' /etc/passwd >> "$OUT"
echo "" >> "$OUT"
echo "--- Interfaces de red ---" >> "$OUT"
ip addr show >> "$OUT"
echo "" >> "$OUT"
echo "--- Tabla de routing ---" >> "$OUT"
ip route show >> "$OUT"
echo "" >> "$OUT"
echo "--- DNS configurado ---" >> "$OUT"
cat /etc/resolv.conf >> "$OUT"
echo "" >> "$OUT"
echo "--- Fecha de instalacion (approx) ---" >> "$OUT"
stat / 2>/dev/null | grep -i birth >> "$OUT"
ls -lct /etc/ | tail -1 >> "$OUT"
echo "[+] System info guardado en $OUT"
Lo que buscamos aquí: usuarios con UID 0 que no sean root (posible backdoor), sesiones SSH activas de orígenes desconocidos, uptime inusualmente bajo (puede indicar reinicio para cargar un rootkit) y configuración DNS alterada (redirección a servidor malicioso).
Script 2: Análisis de procesos
Los procesos en ejecución son la evidencia más volátil después de la memoria. Un atacante puede tener un reverse shell, un cryptominer o un agente C2 corriendo ahora mismo.
#!/bin/bash
# collect_processes.sh — Análisis de procesos en ejecución
OUT="$1/02_processes.txt"
echo "========== PROCESS ANALYSIS ==========" > "$OUT"
echo "[Timestamp] $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> "$OUT"
echo "" >> "$OUT"
echo "--- Todos los procesos (detalle) ---" >> "$OUT"
ps auxwwf >> "$OUT"
echo "" >> "$OUT"
echo "--- Procesos sin terminal (posible backdoor) ---" >> "$OUT"
ps aux | awk '$7 == "?" {print}' >> "$OUT"
echo "" >> "$OUT"
echo "--- Procesos con paths sospechosos ---" >> "$OUT"
ls -la /proc/*/exe 2>/dev/null | grep -i deleted >> "$OUT"
echo "" >> "$OUT"
echo "--- Binarios ejecutandose desde /tmp /dev/shm /var/tmp ---" >> "$OUT"
ls -la /proc/*/exe 2>/dev/null | grep -E '/(tmp|dev/shm|var/tmp)/' >> "$OUT"
echo "" >> "$OUT"
echo "--- Linea de comando de todos los procesos ---" >> "$OUT"
for pid in /proc/[0-9]*; do
PID_NUM=$(basename "$pid")
CMD=$(cat "$pid/cmdline" 2>/dev/null | tr '\0' ' ')
EXE=$(readlink "$pid/exe" 2>/dev/null)
if [ -n "$CMD" ]; then
echo "PID $PID_NUM | EXE: $EXE | CMD: $CMD" >> "$OUT"
fi
done
echo "" >> "$OUT"
echo "--- Environment variables de procesos sospechosos ---" >> "$OUT"
for pid in /proc/[0-9]*; do
PID_NUM=$(basename "$pid")
EXE=$(readlink "$pid/exe" 2>/dev/null)
if echo "$EXE" | grep -qE '/(tmp|dev/shm|var/tmp)/'; then
echo "=== PID $PID_NUM ($EXE) ===" >> "$OUT"
cat "$pid/environ" 2>/dev/null | tr '\0' '\n' >> "$OUT"
echo "" >> "$OUT"
fi
done
echo "--- Archivos abiertos (top 20 procesos por fd count) ---" >> "$OUT"
for pid in /proc/[0-9]*; do
PID_NUM=$(basename "$pid")
FD_COUNT=$(ls "$pid/fd" 2>/dev/null | wc -l)
echo "$FD_COUNT $PID_NUM"
done | sort -rn | head -20 | while read count pid; do
echo "PID $pid: $count file descriptors" >> "$OUT"
ls -la "/proc/$pid/fd" 2>/dev/null | head -10 >> "$OUT"
echo "" >> "$OUT"
done
echo "[+] Process analysis guardado en $OUT"
Indicadores clave: procesos cuyo ejecutable ha sido eliminado del disco (aparece como "deleted" en /proc/PID/exe), binarios ejecutándose desde /tmp, /dev/shm o /var/tmp (ubicaciones típicas de malware), y procesos con un número inusualmente alto de file descriptors abiertos.
Script 3: Conexiones de red
Las conexiones de red activas revelan comunicaciones C2, exfiltración de datos y movimiento lateral. Este script captura el estado completo de la red.
#!/bin/bash
# collect_network.sh — Conexiones de red y reglas de firewall
OUT="$1/03_network.txt"
echo "========== NETWORK ANALYSIS ==========" > "$OUT"
echo "[Timestamp] $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> "$OUT"
echo "" >> "$OUT"
echo "--- Conexiones activas (ss) ---" >> "$OUT"
ss -tulnpa >> "$OUT"
echo "" >> "$OUT"
echo "--- Conexiones ESTABLISHED con proceso ---" >> "$OUT"
ss -tnpa state established >> "$OUT"
echo "" >> "$OUT"
echo "--- Puertos en escucha ---" >> "$OUT"
ss -tlnp >> "$OUT"
echo "" >> "$OUT"
echo "--- Conexiones netstat (fallback) ---" >> "$OUT"
netstat -tulnpa 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Conexiones a IPs externas (no RFC1918) ---" >> "$OUT"
ss -tnpa state established | \
awk '{print $5}' | \
grep -vE '^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.|::1|0\.0\.0\.0|\*)' | \
sort -u >> "$OUT"
echo "" >> "$OUT"
echo "--- Cache ARP ---" >> "$OUT"
ip neigh show >> "$OUT"
echo "" >> "$OUT"
echo "--- Reglas iptables ---" >> "$OUT"
iptables -L -n -v 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Reglas nftables ---" >> "$OUT"
nft list ruleset 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Conexiones por IP destino (top 20) ---" >> "$OUT"
ss -tn state established | \
awk '{print $5}' | \
cut -d: -f1 | \
sort | uniq -c | sort -rn | head -20 >> "$OUT"
echo "[+] Network analysis guardado en $OUT"
Lo que buscamos: conexiones ESTABLISHED a IPs no reconocidas (posible C2), puertos en escucha que no deberían estar ahí (reverse shells, proxies SOCKS), reglas de firewall modificadas (el atacante abriendo puertos) y un alto número de conexiones a la misma IP destino (beaconing o exfiltración).
El filtrado de IPs RFC1918 permite centrarse en las conexiones externas, que son las más relevantes para identificar comunicaciones C2. Los atacantes a veces usan puertos altos o puertos que imitan servicios legítimos (443, 8080, 53).
Script 4: Verificación de persistencia
La persistencia es el mecanismo que permite al atacante sobrevivir reinicios. En Linux, hay docenas de vectores de persistencia. Este script verifica los más comunes.
#!/bin/bash
# collect_persistence.sh — Mecanismos de persistencia
OUT="$1/04_persistence.txt"
echo "========== PERSISTENCE CHECK ==========" > "$OUT"
echo "[Timestamp] $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> "$OUT"
echo "" >> "$OUT"
echo "--- Crontabs de todos los usuarios ---" >> "$OUT"
for user in $(cut -f1 -d: /etc/passwd); do
CRON=$(crontab -l -u "$user" 2>/dev/null)
if [ -n "$CRON" ]; then
echo "=== Crontab de $user ===" >> "$OUT"
echo "$CRON" >> "$OUT"
echo "" >> "$OUT"
fi
done
echo "--- Cron directories ---" >> "$OUT"
for dir in /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly; do
if [ -d "$dir" ]; then
echo "=== $dir ===" >> "$OUT"
ls -la "$dir" >> "$OUT"
echo "" >> "$OUT"
fi
done
echo "--- /etc/crontab ---" >> "$OUT"
cat /etc/crontab 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Systemd timers activos ---" >> "$OUT"
systemctl list-timers --all 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Servicios systemd habilitados ---" >> "$OUT"
systemctl list-unit-files --type=service --state=enabled 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Servicios systemd recientes (creados ultimos 7 dias) ---" >> "$OUT"
find /etc/systemd/system /usr/lib/systemd/system \
-name '*.service' -mtime -7 2>/dev/null -exec ls -la {} \; >> "$OUT"
echo "" >> "$OUT"
echo "--- rc.local ---" >> "$OUT"
cat /etc/rc.local 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- init.d scripts ---" >> "$OUT"
ls -la /etc/init.d/ 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- SSH authorized_keys (todos los usuarios) ---" >> "$OUT"
find / -name authorized_keys -type f 2>/dev/null | while read keyfile; do
echo "=== $keyfile ===" >> "$OUT"
ls -la "$keyfile" >> "$OUT"
cat "$keyfile" >> "$OUT"
echo "" >> "$OUT"
done
echo "--- SSH config (per-user) ---" >> "$OUT"
find /home -name config -path '*/.ssh/*' 2>/dev/null | while read conf; do
echo "=== $conf ===" >> "$OUT"
cat "$conf" >> "$OUT"
echo "" >> "$OUT"
done
echo "--- Bashrc / profile hooks ---" >> "$OUT"
for f in /etc/profile /etc/bash.bashrc /etc/environment; do
if [ -f "$f" ]; then
echo "=== $f ===" >> "$OUT"
cat "$f" >> "$OUT"
echo "" >> "$OUT"
fi
done
find /home -maxdepth 2 \( -name .bashrc -o -name .profile -o -name .bash_profile \) \
2>/dev/null | while read rc; do
echo "=== $rc ===" >> "$OUT"
grep -n 'curl\|wget\|python\|perl\|nc\|ncat\|base64\|eval' "$rc" 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
done
echo "--- LD_PRELOAD y ld.so.preload ---" >> "$OUT"
echo "LD_PRELOAD=$LD_PRELOAD" >> "$OUT"
cat /etc/ld.so.preload 2>/dev/null >> "$OUT"
echo "" >> "$OUT"
echo "--- Modulos kernel cargados ---" >> "$OUT"
lsmod >> "$OUT"
echo "" >> "$OUT"
echo "--- Modulos kernel recientes (ultimos 7 dias) ---" >> "$OUT"
find /lib/modules -name '*.ko' -mtime -7 2>/dev/null -exec ls -la {} \; >> "$OUT"
echo "[+] Persistence check guardado en $OUT"
Vectores de persistencia en Linux que este script cubre:
- Cron jobs: el clásico. El atacante programa la ejecución periódica de su backdoor. Revisamos los crontabs de todos los usuarios y los directorios de cron del sistema.
- Servicios systemd: un servicio malicioso con
Restart=alwaysgarantiza la persistencia. Los servicios creados en los últimos 7 días son especialmente sospechosos. - SSH authorized_keys: el atacante añade su clave pública para acceso sin contraseña. Verificamos las claves de todos los usuarios.
- Bashrc/profile hooks: comandos maliciosos que se ejecutan cada vez que un usuario abre una terminal. Buscamos patrones como curl, wget, base64 o eval.
- LD_PRELOAD: carga de bibliotecas maliciosas antes que las legítimas. Un rootkit userland clásico.
- Módulos kernel: los rootkits kernel-level se cargan como módulos .ko.
Script 5: Recolección y empaquetado de logs
Los logs son la narrativa del incidente. Este script recopila los logs relevantes y los empaqueta para análisis offline.
#!/bin/bash
# collect_logs.sh — Recoleccion y empaquetado de logs
OUT_DIR="$1/05_logs"
mkdir -p "$OUT_DIR"
echo "========== LOG COLLECTION =========="
echo "[Timestamp] $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
# Logs de autenticacion
echo "[*] Recolectando logs de autenticacion..."
cp /var/log/auth.log* "$OUT_DIR/" 2>/dev/null
cp /var/log/secure* "$OUT_DIR/" 2>/dev/null
# Logs del sistema
echo "[*] Recolectando logs del sistema..."
cp /var/log/syslog* "$OUT_DIR/" 2>/dev/null
cp /var/log/messages* "$OUT_DIR/" 2>/dev/null
cp /var/log/kern.log* "$OUT_DIR/" 2>/dev/null
cp /var/log/dmesg* "$OUT_DIR/" 2>/dev/null
# Logs de servicios web
echo "[*] Recolectando logs web..."
cp /var/log/apache2/access.log* "$OUT_DIR/" 2>/dev/null
cp /var/log/apache2/error.log* "$OUT_DIR/" 2>/dev/null
cp /var/log/nginx/access.log* "$OUT_DIR/" 2>/dev/null
cp /var/log/nginx/error.log* "$OUT_DIR/" 2>/dev/null
# Logs SSH
echo "[*] Recolectando logs SSH..."
journalctl -u sshd --no-pager -n 5000 > "$OUT_DIR/sshd_journal.log" 2>/dev/null
# Intentos fallidos de login
echo "[*] Recolectando intentos fallidos..."
lastb -n 100 > "$OUT_DIR/lastb_failed_logins.txt" 2>/dev/null
# Journalctl ultimas 24h
echo "[*] Extrayendo journal de las ultimas 24h..."
journalctl --since "24 hours ago" --no-pager > "$OUT_DIR/journal_24h.log" 2>/dev/null
# Archivos modificados en las ultimas 48h (excluyendo /proc /sys /dev)
echo "[*] Buscando archivos modificados recientemente..."
find / -mtime -2 -type f \
-not -path '/proc/*' \
-not -path '/sys/*' \
-not -path '/dev/*' \
-not -path '/run/*' \
2>/dev/null | sort > "$OUT_DIR/files_modified_48h.txt"
# Archivos SUID/SGID (posible escalacion)
echo "[*] Buscando archivos SUID/SGID..."
find / -type f \( -perm -4000 -o -perm -2000 \) \
-not -path '/proc/*' \
2>/dev/null -exec ls -la {} \; > "$OUT_DIR/suid_sgid_files.txt"
# Archivos en directorios temporales
echo "[*] Listando archivos en directorios temporales..."
ls -laR /tmp /var/tmp /dev/shm 2>/dev/null > "$OUT_DIR/temp_dirs_listing.txt"
echo "[+] Logs recolectados en $OUT_DIR/"
Los archivos modificados en las últimas 48 horas ayudan a establecer la timeline del incidente. Los archivos SUID/SGID son un vector clásico de escalación de privilegios: si el atacante creó un binario SUID root, tiene acceso root permanente.
Script de triaje automatizado
Este script combina todos los anteriores en un flujo secuencial con timing y resumen final.
#!/bin/bash
# ir_triage.sh — Triaje automatizado de incident response
# Uso: sudo bash ir_triage.sh
# Requiere: root o sudo
set -euo pipefail
if [ "$(id -u)" -ne 0 ]; then
echo "[ERROR] Este script requiere privilegios root."
echo "Uso: sudo bash ir_triage.sh"
exit 1
fi
HOSTNAME_SHORT=$(hostname -s)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
OUTPUT_DIR="/tmp/ir_${HOSTNAME_SHORT}_${TIMESTAMP}"
mkdir -p "$OUTPUT_DIR"
echo "=============================================="
echo " INCIDENT RESPONSE TRIAGE"
echo " Host: $(hostname -f)"
echo " Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
echo " Output: $OUTPUT_DIR"
echo "=============================================="
echo ""
TOTAL_START=$(date +%s)
# Fase 1: System Info
echo "[1/5] Recolectando informacion del sistema..."
START=$(date +%s)
bash collect_system_info.sh "$OUTPUT_DIR"
END=$(date +%s)
echo " Completado en $((END - START)) segundos"
# Fase 2: Procesos
echo "[2/5] Analizando procesos..."
START=$(date +%s)
bash collect_processes.sh "$OUTPUT_DIR"
END=$(date +%s)
echo " Completado en $((END - START)) segundos"
# Fase 3: Red
echo "[3/5] Analizando conexiones de red..."
START=$(date +%s)
bash collect_network.sh "$OUTPUT_DIR"
END=$(date +%s)
echo " Completado en $((END - START)) segundos"
# Fase 4: Persistencia
echo "[4/5] Verificando mecanismos de persistencia..."
START=$(date +%s)
bash collect_persistence.sh "$OUTPUT_DIR"
END=$(date +%s)
echo " Completado en $((END - START)) segundos"
# Fase 5: Logs
echo "[5/5] Recolectando logs..."
START=$(date +%s)
bash collect_logs.sh "$OUTPUT_DIR"
END=$(date +%s)
echo " Completado en $((END - START)) segundos"
TOTAL_END=$(date +%s)
echo ""
echo "[*] Triaje completado en $((TOTAL_END - TOTAL_START)) segundos"
echo ""
# Generar manifiesto
echo "--- Generando manifiesto ---"
MANIFEST="$OUTPUT_DIR/MANIFEST.txt"
echo "IR Triage Report" > "$MANIFEST"
echo "Host: $(hostname -f)" >> "$MANIFEST"
echo "Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> "$MANIFEST"
echo "Analyst: $(whoami)" >> "$MANIFEST"
echo "Duration: $((TOTAL_END - TOTAL_START)) seconds" >> "$MANIFEST"
echo "" >> "$MANIFEST"
echo "Files collected:" >> "$MANIFEST"
find "$OUTPUT_DIR" -type f | sort >> "$MANIFEST"
echo "[+] Manifiesto generado: $MANIFEST"
El script incluye timing para cada fase, un manifiesto con todos los archivos recolectados y se ejecuta como root (necesario para acceder a /proc de otros usuarios y a logs del sistema).
Cadena de custodia con hashing
La cadena de custodia es lo que convierte una recolección informal en evidencia potencialmente utilizable. Cada archivo recolectado debe tener un hash que demuestre que no ha sido alterado.
#!/bin/bash
# chain_of_custody.sh — Hashing y empaquetado con cadena de custodia
# Ejecutar DESPUES de ir_triage.sh
OUTPUT_DIR="$1"
if [ -z "$OUTPUT_DIR" ] || [ ! -d "$OUTPUT_DIR" ]; then
echo "[ERROR] Especifica el directorio de evidencia"
echo "Uso: bash chain_of_custody.sh /tmp/ir_hostname_timestamp"
exit 1
fi
HASH_FILE="$OUTPUT_DIR/SHA256SUMS.txt"
COC_FILE="$OUTPUT_DIR/CHAIN_OF_CUSTODY.txt"
# Generar hashes SHA256 de todos los archivos
echo "[*] Generando hashes SHA256..."
find "$OUTPUT_DIR" -type f \
-not -name 'SHA256SUMS.txt' \
-not -name 'CHAIN_OF_CUSTODY.txt' \
-exec sha256sum {} \; | sort > "$HASH_FILE"
echo "[+] Hashes guardados en $HASH_FILE"
# Generar documento de cadena de custodia
echo "CHAIN OF CUSTODY DOCUMENT" > "$COC_FILE"
echo "=========================" >> "$COC_FILE"
echo "" >> "$COC_FILE"
echo "Case ID: IR-$(date +%Y%m%d)-$(hostname -s)" >> "$COC_FILE"
echo "Collection Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> "$COC_FILE"
echo "Collected By: $(whoami)@$(hostname -f)" >> "$COC_FILE"
echo "Collection Method: Automated Bash triage script v1.0" >> "$COC_FILE"
echo "System IP: $(hostname -I | awk '{print $1}')" >> "$COC_FILE"
echo "" >> "$COC_FILE"
echo "Evidence Items:" >> "$COC_FILE"
echo "---------------" >> "$COC_FILE"
FILE_COUNT=$(find "$OUTPUT_DIR" -type f | wc -l)
TOTAL_SIZE=$(du -sh "$OUTPUT_DIR" | awk '{print $1}')
echo "Total files: $FILE_COUNT" >> "$COC_FILE"
echo "Total size: $TOTAL_SIZE" >> "$COC_FILE"
echo "" >> "$COC_FILE"
echo "Integrity Verification:" >> "$COC_FILE"
echo " sha256sum -c SHA256SUMS.txt" >> "$COC_FILE"
echo "" >> "$COC_FILE"
echo "HASH OF THIS COLLECTION:" >> "$COC_FILE"
# Empaquetar todo
ARCHIVE="${OUTPUT_DIR}.tar.gz"
tar czf "$ARCHIVE" -C "$(dirname $OUTPUT_DIR)" "$(basename $OUTPUT_DIR)"
# Hash del archivo comprimido
ARCHIVE_HASH=$(sha256sum "$ARCHIVE" | awk '{print $1}')
echo " Archive: $(basename $ARCHIVE)" >> "$COC_FILE"
echo " SHA256: $ARCHIVE_HASH" >> "$COC_FILE"
echo "[+] Cadena de custodia: $COC_FILE"
echo "[+] Archivo comprimido: $ARCHIVE"
echo "[+] SHA256 del archivo: $ARCHIVE_HASH"
echo ""
echo "Guarda este hash en un canal independiente (email, ticket, foto):"
echo "$ARCHIVE_HASH"
El hash SHA256 del archivo comprimido final debe registrarse en un sistema independiente (ticket de ITSM, email, foto con el móvil) para demostrar que la evidencia no ha sido modificada después de la recolección.
Verificación posterior
Quien reciba la evidencia puede verificar la integridad:
# Verificar integridad del archivo comprimido
echo "HASH_ESPERADO archivo.tar.gz" | sha256sum -c
# Descomprimir
tar xzf ir_hostname_timestamp.tar.gz
# Verificar integridad de cada archivo individual
cd ir_hostname_timestamp/
sha256sum -c SHA256SUMS.txt
Si algún hash no coincide, la evidencia ha sido alterada y no es fiable.
Consideraciones prácticas
Ejecución remota
Si necesitas ejecutar el triaje en múltiples servidores, puedes usar SSH con un script wrapper:
#!/bin/bash
# remote_triage.sh — Ejecutar triaje en servidor remoto
TARGET="$1"
SCRIPTS_DIR="./ir_scripts"
if [ -z "$TARGET" ]; then
echo "Uso: bash remote_triage.sh usuario@servidor"
exit 1
fi
echo "[*] Copiando scripts a $TARGET..."
scp -r "$SCRIPTS_DIR" "$TARGET:/tmp/ir_scripts/"
echo "[*] Ejecutando triaje..."
ssh -t "$TARGET" "sudo bash /tmp/ir_scripts/ir_triage.sh"
echo "[*] Recuperando evidencia..."
REMOTE_DIR=$(ssh "$TARGET" "ls -td /tmp/ir_* | head -1")
scp -r "$TARGET:$REMOTE_DIR.tar.gz" ./evidence/
echo "[+] Evidencia descargada en ./evidence/"
Limitaciones conocidas
Estos scripts no cubren:
- Memoria RAM: para volcado de memoria, usa herramientas como LiME (Linux Memory Extractor). Bash no puede acceder directamente a la memoria física.
- Rootkits kernel: si hay un rootkit kernel activo, los comandos del sistema pueden devolver información falsificada. Herramientas como rkhunter o chkrootkit ayudan, pero no son infalibles desde un sistema comprometido.
- Análisis forense de disco: para análisis de sectores, recuperación de archivos borrados o análisis de timeline del sistema de archivos, necesitas herramientas como Sleuth Kit / Autopsy sobre una imagen forense.
- Contenedores Docker: los procesos dentro de contenedores tienen su propio namespace. Usa
docker psydocker inspectpara complementar el triaje.
Personalización para tu entorno
Adapta los scripts a tu infraestructura:
- Añade la recolección de logs de tu SIEM agent (Wazuh, OSSEC, Elastic Agent)
- Incluye verificación de integridad de binarios críticos (
rpm -Vaen Red Hat,debsumsen Debian) - Agrega recolección de logs de aplicaciones específicas (bases de datos, servidores de aplicaciones)
- Integra el envío automático del paquete de evidencia a un servidor central
Recursos
- RFC 3227 (Guidelines for Evidence Collection): el estándar de referencia para recolección de evidencia digital. Define el orden de volatilidad y las buenas prácticas de preservación.
- SANS DFIR Linux Cheat Sheet: referencia rápida de comandos forenses para Linux, incluyendo los que usamos en estos scripts y muchos más.
- Linux Forensics (Philip Polstra, 2015): libro dedicado exclusivamente a forense en Linux con Bash y Python. Cubre adquisición, análisis de sistema de archivos y memoria.
- The Sleuth Kit / Autopsy: herramienta open source para análisis forense de disco. Complementa el triaje en vivo que hacemos con Bash.
- LiME (Linux Memory Extractor): módulo kernel para volcado de memoria RAM en Linux. El siguiente paso después del triaje Bash para análisis de memoria.
- MITRE ATT&CK (Persistence Tactic): referencia de todas las técnicas de persistencia conocidas en Linux (T1053, T1543, T1546, T1556).
Preguntas frecuentes
Libros recomendados
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.