Playbooks de Respuesta a Incidentes como Código: Diseño e Implementación
Diseño e implementación de playbooks de respuesta a incidentes como código: estructura NIST SP 800-61, templates YAML para ransomware, phishing, malware y acceso no autorizado, control de versiones, testing de playbooks y matriz de niveles de automatización.
Por qué los playbooks deben ser código
La mayoría de SOCs tienen playbooks en documentos Word, wikis Confluence o PDFs que nadie lee. Cuando llega un incidente real, el analista abre el documento, busca el paso relevante, improvisa lo que no entiende y cierra el ticket. El playbook no se actualiza hasta la próxima auditoría.
Los playbooks como código cambian este paradigma:
- Versionados: cada cambio tiene un commit con autor, fecha y motivo. Puedes ver cómo evolucionó el playbook y hacer rollback si una actualización introduce problemas.
- Ejecutables: un motor de automatización interpreta el playbook y ejecuta los pasos que pueden automatizarse. El analista solo interviene donde se requiere criterio humano.
- Testeables: puedes simular un incidente en un entorno de pruebas y verificar que el playbook funciona antes de necesitarlo en producción.
- Medibles: cada ejecución genera métricas (tiempo por paso, pasos automatizados vs manuales, resultado).
Estructura de un playbook: NIST SP 800-61
NIST SP 800-61 (Computer Security Incident Handling Guide) define cuatro fases de respuesta a incidentes. Nuestros playbooks siguen esta estructura:
- Preparación: herramientas, accesos y configuraciones necesarias antes del incidente
- Detección y Análisis: cómo se detecta el incidente y qué triaje inicial se realiza
- Contención, Erradicación y Recuperación: acciones para limitar el daño, eliminar la amenaza y restaurar operaciones
- Actividad Post-Incidente: lecciones aprendidas y mejoras
Formato YAML del playbook
# playbook_schema.yaml — Estructura base de un playbook
---
metadata:
id: PB-XXX-NNN
name: "Nombre descriptivo del playbook"
version: "1.0.0"
author: "Equipo SOC"
created: "2026-01-15"
updated: "2026-06-01"
severity_range: [medium, critical]
mitre_techniques:
- T1566 # Phishing
- T1486 # Data Encrypted for Impact
nist_phase: "all"
tags:
- ransomware
- incident-response
sla:
triage: 15m
containment: 1h
eradication: 24h
recovery: 48h
trigger:
type: alert # alert | manual | scheduled
source: SIEM
conditions:
- field: alert.category
operator: equals
value: "malware"
- field: alert.severity
operator: gte
value: "high"
phases:
- name: triage
automation_level: full # full | semi | manual
steps: []
- name: containment
automation_level: semi
steps: []
- name: eradication
automation_level: manual
steps: []
- name: recovery
automation_level: semi
steps: []
- name: post_mortem
automation_level: manual
steps: []
Estructura de un paso (step)
steps:
- id: step_001
name: "Recopilar información del host afectado"
description: "Obtener hostname, IP, usuario, SO y estado del EDR"
automation_level: full
timeout: 60s
tool: edr_api
action: get_host_info
parameters:
host_id: "{{ trigger.host_id }}"
output_variable: host_info
on_success: step_002
on_failure: step_001_fallback
on_timeout: step_001_fallback
- id: step_001_fallback
name: "Recopilar info del host (manual)"
automation_level: manual
instructions: >
Accede al EDR y busca el host manualmente.
Documenta: hostname, IP, usuario, SO, ultimo check-in.
notify:
channel: slack
target: "#soc-incidents"
message: >
Fallo en recopilacion automatica del host
{{ trigger.host_id }}. Requiere accion manual.
checkpoint: true
output_variable: host_info
on_complete: step_002
Playbook 1: Ransomware
El playbook más crítico de cualquier SOC. La velocidad de contención determina el impacto.
---
metadata:
id: PB-IR-001
name: "Respuesta a incidente de ransomware"
version: "2.1.0"
severity_range: [high, critical]
mitre_techniques:
- T1486 # Data Encrypted for Impact
- T1490 # Inhibit System Recovery
- T1489 # Service Stop
- T1071 # Application Layer Protocol (C2)
sla:
triage: 15m
containment: 30m
eradication: 24h
recovery: 72h
trigger:
type: alert
source: EDR
conditions:
- field: alert.category
operator: in
value: ["ransomware", "encryption", "ransom_note"]
phases:
- name: triage
automation_level: full
steps:
- id: t_001
name: "Confirmar alerta de ransomware"
automation_level: full
tool: edr_api
action: get_alert_details
parameters:
alert_id: "{{ trigger.alert_id }}"
output_variable: alert_details
- id: t_002
name: "Identificar host y usuario afectado"
automation_level: full
tool: edr_api
action: get_host_info
parameters:
host_id: "{{ alert_details.host_id }}"
output_variable: host_info
- id: t_003
name: "Verificar extensiones de archivos cifrados"
automation_level: full
tool: edr_api
action: search_files
parameters:
host_id: "{{ alert_details.host_id }}"
pattern: "*.encrypted|*.locked|*.crypt|*DECRYPT*"
output_variable: encrypted_files
- id: t_004
name: "Buscar nota de rescate"
automation_level: full
tool: edr_api
action: search_files
parameters:
host_id: "{{ alert_details.host_id }}"
pattern: "*README*ransom*|*DECRYPT*|*RECOVER*"
output_variable: ransom_note
- id: t_005
name: "Notificar al equipo"
automation_level: full
tool: slack
action: send_message
parameters:
channel: "#soc-critical"
message: >
RANSOMWARE DETECTADO
Host: {{ host_info.hostname }}
({{ host_info.ip }})
Usuario: {{ host_info.user }}
Archivos cifrados: {{ encrypted_files.count }}
Familia: {{ alert_details.malware_family }}
- name: containment
automation_level: semi
steps:
- id: c_001
name: "Aislar host de la red"
automation_level: full
tool: edr_api
action: isolate_host
parameters:
host_id: "{{ alert_details.host_id }}"
reason: "Ransomware containment - PB-IR-001"
critical: true
- id: c_002
name: "Buscar movimiento lateral"
automation_level: full
tool: siem_api
action: search
parameters:
query: >
src_ip="{{ host_info.ip }}"
AND (dest_port=445 OR dest_port=135
OR dest_port=3389 OR dest_port=5985)
timerange: "-2h"
output_variable: lateral_movement
- id: c_003
name: "Aislar hosts contactados"
automation_level: semi
checkpoint: true
instructions: >
Revisar los {{ lateral_movement.count }} hosts
contactados. Confirmar aislamiento de los que
muestren signos de compromiso.
tool: edr_api
action: isolate_hosts
parameters:
host_ids: "{{ lateral_movement.dest_hosts }}"
- id: c_004
name: "Bloquear IOCs en firewall"
automation_level: full
tool: firewall_api
action: block_iocs
parameters:
ips: "{{ alert_details.c2_ips }}"
domains: "{{ alert_details.c2_domains }}"
duration: "permanent"
- id: c_005
name: "Deshabilitar cuenta comprometida"
automation_level: semi
checkpoint: true
instructions: >
Confirmar deshabilitacion de la cuenta
{{ host_info.user }} en Active Directory.
Verificar que no es una cuenta de servicio critica.
tool: ad_api
action: disable_account
parameters:
username: "{{ host_info.user }}"
- name: eradication
automation_level: manual
steps:
- id: e_001
name: "Identificar familia de ransomware"
automation_level: semi
tool: malware_analysis
action: identify_family
parameters:
ransom_note: "{{ ransom_note.content }}"
file_extension: "{{ encrypted_files.extension }}"
hashes: "{{ alert_details.file_hashes }}"
- id: e_002
name: "Buscar decryptor disponible"
automation_level: full
tool: http_request
action: check_nomoreransom
parameters:
family: "{{ e_001.family_name }}"
output_variable: decryptor_available
- id: e_003
name: "Recopilar evidencia forense"
automation_level: manual
instructions: >
1. Crear imagen forense del disco del host
2. Exportar logs del EDR (ultimas 48h)
3. Capturar memoria RAM si el host sigue encendido
4. Documentar timeline de la infeccion
5. Preservar la nota de rescate
- id: e_004
name: "Limpiar host o restaurar desde imagen"
automation_level: manual
instructions: >
Opcion A: Si el decryptor existe, aplicarlo.
Opcion B: Restaurar desde backup limpio.
Opcion C: Reimagen completa del sistema.
En todos los casos, verificar que la amenaza
esta eliminada antes de reconectar a la red.
- name: recovery
automation_level: semi
steps:
- id: r_001
name: "Restaurar datos desde backup"
automation_level: manual
instructions: >
Verificar integridad del backup antes de restaurar.
Confirmar que el backup es anterior a la infeccion.
Restaurar datos en el sistema limpio.
- id: r_002
name: "Reconectar host a la red"
automation_level: semi
checkpoint: true
tool: edr_api
action: unisolate_host
parameters:
host_id: "{{ alert_details.host_id }}"
- id: r_003
name: "Restablecer credenciales"
automation_level: semi
tool: ad_api
action: reset_password
parameters:
username: "{{ host_info.user }}"
instructions: >
Comunicar al usuario el cambio de contrasena.
Verificar que no hay sesiones activas con
credenciales antiguas.
- id: r_004
name: "Monitoreo intensivo 72h"
automation_level: full
tool: siem_api
action: create_watchlist
parameters:
hosts: ["{{ host_info.hostname }}"]
duration: "72h"
alert_threshold: "low"
- name: post_mortem
automation_level: manual
steps:
- id: pm_001
name: "Documentar timeline completa"
instructions: >
Crear documento con:
- Fecha y hora de cada fase
- Vector de entrada inicial
- Sistemas afectados
- Datos comprometidos
- Acciones de contencion tomadas
- Tiempo de respuesta por fase
- id: pm_002
name: "Lecciones aprendidas"
instructions: >
Reunion post-mortem con el equipo (max 48h despues).
Documentar: que funciono, que no, que cambiar
en el playbook para la proxima vez.
- id: pm_003
name: "Actualizar playbook"
instructions: >
Aplicar las lecciones aprendidas:
commit en Git con los cambios al playbook.
Tag con nueva version.
Playbook 2: Phishing confirmado
---
metadata:
id: PB-IR-002
name: "Respuesta a phishing confirmado"
version: "1.3.0"
severity_range: [medium, high]
mitre_techniques:
- T1566.001 # Spearphishing Attachment
- T1566.002 # Spearphishing Link
- T1204.001 # User Execution: Malicious Link
sla:
triage: 10m
containment: 30m
eradication: 4h
phases:
- name: triage
automation_level: full
steps:
- id: t_001
name: "Analizar email reportado"
tool: email_analyzer
action: full_analysis
parameters:
email_id: "{{ trigger.email_id }}"
output_variable: email_analysis
- id: t_002
name: "Verificar si el usuario hizo clic"
tool: proxy_logs
action: search
parameters:
user: "{{ trigger.reporter }}"
urls: "{{ email_analysis.urls }}"
timerange: "-24h"
output_variable: click_evidence
- id: t_003
name: "Verificar si otros usuarios recibieron el mismo email"
tool: email_gateway
action: search_similar
parameters:
sender: "{{ email_analysis.sender }}"
subject: "{{ email_analysis.subject }}"
timerange: "-48h"
output_variable: affected_users
- name: containment
automation_level: full
steps:
- id: c_001
name: "Bloquear sender en email gateway"
tool: email_gateway
action: block_sender
parameters:
sender: "{{ email_analysis.sender }}"
- id: c_002
name: "Eliminar email de todos los buzones"
tool: email_gateway
action: purge_message
parameters:
message_id: "{{ email_analysis.message_id }}"
- id: c_003
name: "Bloquear URLs en proxy"
tool: proxy_api
action: block_urls
parameters:
urls: "{{ email_analysis.malicious_urls }}"
- id: c_004
name: "Si usuario hizo clic, resetear credenciales"
automation_level: semi
condition: "{{ click_evidence.clicked == true }}"
checkpoint: true
tool: ad_api
action: reset_password
parameters:
username: "{{ trigger.reporter }}"
Playbook 3: Detección de malware
---
metadata:
id: PB-IR-003
name: "Respuesta a deteccion de malware en endpoint"
version: "1.2.0"
severity_range: [medium, high]
mitre_techniques:
- T1059 # Command and Scripting Interpreter
- T1055 # Process Injection
- T1547 # Boot or Logon Autostart Execution
phases:
- name: triage
automation_level: full
steps:
- id: t_001
name: "Obtener detalles de la deteccion"
tool: edr_api
action: get_detection
parameters:
detection_id: "{{ trigger.detection_id }}"
output_variable: detection
- id: t_002
name: "Clasificar la amenaza"
tool: threat_intel
action: lookup_hash
parameters:
sha256: "{{ detection.file_hash }}"
output_variable: threat_info
- id: t_003
name: "Verificar si es un falso positivo conocido"
tool: whitelist
action: check
parameters:
hash: "{{ detection.file_hash }}"
path: "{{ detection.file_path }}"
signer: "{{ detection.signer }}"
output_variable: fp_check
- name: containment
automation_level: semi
steps:
- id: c_001
name: "Cuarentenar el archivo"
condition: "{{ fp_check.is_false_positive == false }}"
tool: edr_api
action: quarantine_file
parameters:
host_id: "{{ detection.host_id }}"
file_path: "{{ detection.file_path }}"
- id: c_002
name: "Matar proceso malicioso"
condition: "{{ detection.process_running == true }}"
tool: edr_api
action: kill_process
parameters:
host_id: "{{ detection.host_id }}"
pid: "{{ detection.process_id }}"
Playbook 4: Acceso no autorizado
---
metadata:
id: PB-IR-004
name: "Respuesta a acceso no autorizado"
version: "1.1.0"
severity_range: [high, critical]
mitre_techniques:
- T1078 # Valid Accounts
- T1110 # Brute Force
- T1021 # Remote Services
phases:
- name: triage
automation_level: full
steps:
- id: t_001
name: "Recopilar eventos de autenticacion"
tool: siem_api
action: search
parameters:
query: >
user="{{ trigger.username }}"
AND (EventCode=4624 OR EventCode=4625
OR EventCode=4648 OR EventCode=4672)
timerange: "-24h"
output_variable: auth_events
- id: t_002
name: "Analizar patrones de acceso"
tool: ueba
action: analyze_user
parameters:
username: "{{ trigger.username }}"
timerange: "-7d"
output_variable: behavior_analysis
- id: t_003
name: "Geolocalizacion de IPs de acceso"
tool: enrichment
action: geolocate_ips
parameters:
ips: "{{ auth_events.source_ips }}"
output_variable: geo_data
- name: containment
automation_level: semi
steps:
- id: c_001
name: "Forzar cierre de sesiones activas"
checkpoint: true
tool: ad_api
action: revoke_sessions
parameters:
username: "{{ trigger.username }}"
- id: c_002
name: "Bloquear IPs sospechosas"
tool: firewall_api
action: block_ips
parameters:
ips: "{{ behavior_analysis.anomalous_ips }}"
Control de versiones
Los playbooks como código se gestionan en Git como cualquier otro código:
playbooks/
PB-IR-001-ransomware.yaml
PB-IR-002-phishing.yaml
PB-IR-003-malware.yaml
PB-IR-004-unauthorized-access.yaml
CHANGELOG.md
tests/
test_pb_ir_001.py
test_pb_ir_002.py
fixtures/
sample_ransomware_alert.json
sample_phishing_email.json
Cada cambio sigue un proceso:
- Branch con nombre descriptivo:
feat/pb-001-add-lateral-check - Modificar el playbook
- Actualizar la version en metadata (semver)
- Ejecutar tests
- Pull request con revisión del equipo
- Merge a main
- Tag con la nueva versión
Testing de playbooks
Testear un playbook no es testear código: es simular un incidente y verificar que el playbook responde correctamente.
"""
test_playbook.py — Tests de playbooks de respuesta
"""
import yaml
import pytest
def load_playbook(path: str) -> dict:
with open(path) as f:
return yaml.safe_load(f)
class TestRansomwarePlaybook:
"""Tests del playbook PB-IR-001."""
@pytest.fixture
def playbook(self):
return load_playbook("playbooks/PB-IR-001-ransomware.yaml")
def test_has_all_phases(self, playbook):
phase_names = [p["name"] for p in playbook["phases"]]
required = [
"triage", "containment",
"eradication", "recovery", "post_mortem",
]
for phase in required:
assert phase in phase_names, (
f"Missing phase: {phase}"
)
def test_containment_isolates_host(self, playbook):
containment = next(
p for p in playbook["phases"]
if p["name"] == "containment"
)
actions = [
s.get("action") for s in containment["steps"]
]
assert "isolate_host" in actions, (
"Containment must isolate the affected host"
)
def test_sla_defined(self, playbook):
sla = playbook["metadata"]["sla"]
assert "triage" in sla
assert "containment" in sla
def test_no_step_without_timeout_or_checkpoint(
self, playbook
):
for phase in playbook["phases"]:
for step in phase["steps"]:
if step.get("automation_level") == "full":
assert "timeout" in step or "on_timeout" in step, (
f"Step {step['id']} needs timeout"
)
def test_critical_steps_have_fallback(self, playbook):
for phase in playbook["phases"]:
for step in phase["steps"]:
if step.get("critical"):
assert "on_failure" in step, (
f"Critical step {step['id']} "
f"needs failure handler"
)
Matriz de niveles de automatización
No todo se puede (ni se debe) automatizar. La matriz define qué nivel de automatización aplica a cada tipo de acción:
| Acción | Full | Semi | Manual | Justificación |
|---|---|---|---|---|
| Recopilar contexto (host, usuario, logs) | X | Sin riesgo, siempre beneficioso | ||
| Enriquecer IOCs (reputación, TI) | X | Sin riesgo, ahorra tiempo | ||
| Notificar al equipo (Slack, email) | X | Sin riesgo | ||
| Aislar host de la red | X | Impacto en disponibilidad, requiere confirmación | ||
| Bloquear IP/dominio en firewall | X | Riesgo de FP, requiere revisión | ||
| Deshabilitar cuenta de usuario | X | Impacto en usuario, requiere confirmación | ||
| Restaurar desde backup | X | Decisión de negocio, riesgo de pérdida de datos | ||
| Eliminar malware / reimaginar host | X | Requiere análisis forense previo | ||
| Comunicar a dirección | X | Requiere criterio y contexto | ||
| Notificar a autoridades (AEPD, INCIBE) | X | Implicaciones legales |
La progresión natural es: empezar con pocos pasos automatizados (full), aumentar la cobertura a medida que el equipo gana confianza y reducir los checkpoint (semi) a medida que se reducen los falsos positivos.
Recursos
- NIST SP 800-61 Rev. 2 (Computer Security Incident Handling Guide): el estándar de referencia para respuesta a incidentes. Define las cuatro fases que estructuran nuestros playbooks.
- CISA Incident Response Playbooks: playbooks de referencia publicados por CISA (Cybersecurity and Infrastructure Security Agency) para diferentes tipos de incidentes. Disponibles en cisa.gov.
- RE&CT Framework: framework open source que mapea técnicas de respuesta a incidentes contra la matriz ATT&CK. Útil para verificar que tus playbooks cubren las técnicas relevantes.
- Swimlane Turbine (ex-SOAR): plataforma SOAR que ejecuta playbooks definidos en código. Referencia de cómo los SOAR enterprise interpretan playbooks YAML/JSON.
- MITRE ATT&CK (Impact Tactic): técnicas de impacto relevantes para playbooks de ransomware (T1486, T1490, T1489) y otros tipos de incidentes destructivos.
- Palo Alto Cortex XSOAR Playbook Guide: documentación de diseño de playbooks de Cortex XSOAR. Aunque es una herramienta comercial, la guía de diseño es aplicable a cualquier formato.
Preguntas frecuentes
Libros recomendados
Artículos relacionados
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.