Skip to content

Incident Response Process

Incident response process covers the lifecycle that turns an escalated alert into a managed incident: the preparation (playbooks, tooling, contacts, legal/comms pre-agreements), triage-to-declaration (when does an alert become an incident — the criteria and the authority), containment (stopping the damage without destroying evidence — the strategic vs tactical decision), eradication and recovery (removing the adversary, verifying, restoring — with the re-implosion check), and post-incident learning (the retrospective that converts the incident into controls). The process craft is decision-making under pressure with incomplete information: containment choices that trade evidence for speed, communication tiers that inform without leaking, and the scope discipline (every incident is bigger than it first appears — plan for the second and third patient zero). The SANS PICERL and NIST 800-61 lifecycles [verify framework details] give the structure; the craft is adapting them to your organization’s realities — who can pull the network cable, who calls legal, who tells the board.

Incident response is where the blue team’s whole estate converts into outcomes: the detections (BT9), the telemetry (BT3), the triage (BT1) all funnel into moments where decisions have irreversible consequences — contain too slowly and the adversary reaches the domain controller; contain too aggressively and you destroy the evidence the investigation needs and the business context the recovery requires. The process failures are the industry’s war stories: the containment that wiped the memory forensics (BT6’s evidence gone), the eradication that missed the persistence (the re-implosion — the “fixed” incident that returns), the communication that leaked (the incident becoming public before the customers were notified), and the retrospective that named no owners (the same incident repeating in twelve months). A disciplined IR process fixes these structurally: playbooks that pre-decide the standard decisions, containment strategies that preserve forensics (isolate, don’t wipe), scope assumptions that plan for more (the second patient zero), and retrospectives with owners and dates (the controls that prevent recurrence). For the IR lead, the craft is also leadership under uncertainty: declaring incidents early (the authority and the courage), communicating at the right tiers, and running the bridge calls that make decisions without drowning in them.

The IR lifecycle and its decision points:

IR LIFECYCLE (SANS PICERL-shaped [verify])
[1] PREPARATION
playbooks per scenario, tooling staged,
contacts/legal/comms pre-agreed, evidence
procedures defined (BT5's chain of custody)
[2] IDENTIFICATION
alert (BT1) -> investigation -> DECLARATION
criteria: confirmed adversary action, or
probable with business impact
authority: WHO declares (named role, 24/7)
[3] CONTAINMENT
tactical: isolate host (EDR network-contain)
strategic: segment, disable accounts, block C2
EVIDENCE FIRST: memory capture (BT6) BEFORE
power actions; disk image before wipe
decisions logged with rationale + timer
[4] ERADICATION & RECOVERY
remove persistence (all of it - assume more),
patch the entry vector, restore from clean
backups, verify (hunt for re-implosion)
[5] POST-INCIDENT
retrospective within 2 weeks: timeline,
root cause, controls with OWNERS + DATES
detection improvements -> BT9 backlog
CONTAINMENT DECISION MATRIX
evidence value high | evidence value low
active damage | isolate+memory | isolate hard
| capture, then |
| network-contain |
dormant | image first, | contain at
| light contain | network layer

Preparation is the phase that decides everything downstream: the playbooks (per-scenario procedures — ransomware, BEC, credential compromise — with the standard decisions pre-made), the tooling staged (memory capture on every endpoint, forensic tooling ready), the contacts (legal, comms, insurance, law enforcement — with phone numbers tested), and the evidence procedures (BT5’s chain of custody defined before the first incident). Identification ends with the declaration decision: the criteria (confirmed adversary action, or probable with impact), the authority (a named role who can declare at 3 AM without asking permission), and the discipline to declare early — the incidents that hurt most are the ones declared late. Containment is the strategy decision: tactical (EDR network-containment of one host — reversible, evidence-preserving) versus strategic (segment isolation, account disablement, C2 blocking — broader, more disruptive), with the evidence-first rule: memory capture before any power action (BT6’s volatile evidence dies with the power), disk image before remediation. The containment decision matrix (evidence value × damage activity) is the tool that makes the choice discussable in minutes. Eradication and recovery carry the scope discipline: assume persistence beyond what you found (the adversary who had domain admin had everything — every credential, every path), verify eradication by hunting (the re-implosion check — BT7’s hunts run against the recovered estate), and restore from backups verified clean (the backup that was also encrypted is the ransomware lesson). Post-incident is the phase that pays for the whole process: the retrospective within two weeks (timeline, root cause, what detection missed, what the process slowed), the controls with owners and dates (the fixes that prevent recurrence — each one a BT9 backlog row or a design change), and the incident record that BT20’s metrics build from.

TechniquePhaseWhat it doesToolingNotes
Scenario playbooksPreparationPre-decided proceduresDocumented per scenarioRansomware/BEC/creds
Declaration criteriaIdentificationWhen alert → incidentWritten + named authorityDeclare early
EDR network containmentContainmentIsolate without power lossEDR contain functionEvidence-preserving
Memory-first captureContainmentVolatile evidence preservedBT6 acquisitionBefore power actions
Evidence chain of custodyAllForensic admissibilityBT5 proceduresHashes + handlers
Scope expansion protocolAllPlan for patient zero #2Assumption discipline“Assume more”
Re-implosion huntRecoveryVerify eradicationBT7 huntsThe returned check
Bridge call managementAllDecision-making cadenceRole-based callsWho decides what
Communication tiersAllLegal/comms/customerPre-agreed templatesLeak prevention
Retrospective disciplinePost-incidentControls with ownersTemplate + trackingWithin 2 weeks

ircase.py — an incident case manager: creates incidents with the lifecycle state machine (identified → contained → eradicated → recovered → closed), tracks containment actions with rationale and timers, manages the evidence-artifact list (with hashes — BT5’s chain), records the scope assumptions, and generates the retrospective skeleton (timeline from logged actions, the controls-with-owners table) — the IR process’s bookkeeping, structured so the retrospective writes itself. stdlib only.

#!/usr/bin/env python3
"""ircase.py — incident case manager with lifecycle + evidence.
Usage:
python ircase.py --new INC-2026-001 --title "DC compromise"
python ircase.py --case INC-2026-001 --action contain \\
--detail "EDR network-contain FIN-SRV-04" --rationale "active C2"
python ircase.py --case INC-2026-001 --evidence mem_fin04.bin
python ircase.py --case INC-2026-001 --status eradicated
python ircase.py --case INC-2026-001 --report
stdlib only.
"""
import argparse, hashlib, json, os, sys, datetime
STATES = ["identified", "contained", "eradicated",
"recovered", "closed"]
def cpath(case):
return f"ir_{case}.json"
def load(case):
if os.path.exists(cpath(case)):
return json.load(open(cpath(case),
encoding="utf-8"))
sys.exit(f"case not found: {case}")
def save(case, m):
json.dump(m, open(cpath(case), "w"), indent=2)
def now():
return datetime.datetime.now(
datetime.timezone.utc).isoformat()
def new_case(case, title):
m = {"case": case, "title": title,
"state": "identified",
"opened": now(),
"actions": [], "evidence": [],
"scope_assumptions": [
"assume persistence beyond initial findings",
"assume second patient zero possible"],
"findings": []}
save(case, m)
print(f"[+] case {case} opened ({title}) — state: "
f"identified")
def add_action(case, action, detail, rationale):
m = load(case)
m["actions"].append({"action": action,
"detail": detail,
"rationale": rationale,
"ts": now()})
if action == "contain" and \
m["state"] == "identified":
m["state"] = "contained"
print(f"[+] action logged: {action} — {detail}")
save(case, m)
def add_evidence(case, path):
if not os.path.exists(path):
print(f"[!] evidence missing: {path}")
return
h = hashlib.sha256()
with open(path, "rb") as fh:
for chunk in iter(lambda: fh.read(65536), b""):
h.update(chunk)
m = load(case)
m["evidence"].append({"path": path,
"sha256": h.hexdigest(),
"size": os.path.getsize(path),
"ts": now()})
print(f"[+] evidence: {path} "
f"sha256={h.hexdigest()[:16]}…")
save(case, m)
def set_status(case, status):
if status not in STATES:
sys.exit(f"invalid state {status}; valid: {STATES}")
m = load(case)
idx = STATES.index(status)
cur = STATES.index(m["state"])
if idx != cur + 1:
print(f"[!] warning: jumping states "
f"({m['state']} -> {status}) — "
f"lifecycle order is {STATES}")
m["state"] = status
m.setdefault("state_ts", {})[status] = now()
print(f"[+] state: {status}")
if status == "closed":
print("[!] closure checklist: retrospective done? "
"controls have owners? re-implosion hunt ran?")
save(case, m)
def report(case):
m = load(case)
L = [f"# Incident {m['case']}: {m['title']}", "",
f"- state: **{m['state']}** (opened {m['opened']})",
f"- actions: {len(m['actions'])}, "
f"evidence: {len(m['evidence'])}", "",
"## Timeline (from actions)", ""]
for a in m["actions"]:
L.append(f"- {a['ts'][:19]} **{a['action']}** "
f"{a['detail']} — _{a['rationale']}_")
L += ["", "## Evidence (chain of custody)", ""]
for e in m["evidence"]:
L.append(f"- `{e['path']}` "
f"sha256={e['sha256'][:16]}… "
f"({e['size']} bytes)")
L += ["", "## Scope assumptions", ""]
for s in m["scope_assumptions"]:
L.append(f"- {s}")
L += ["", "## Retrospective skeleton (fill)", "",
"| control change | owner | due date |",
"|---|---|---|",
"| (detection gap -> BT9 rule) | | |",
"| (process slowdown -> playbook fix) | | |",
"| (architecture weakness -> design change) | | |"]
return "\n".join(L) + "\n"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--new", metavar="CASE")
ap.add_argument("--title")
ap.add_argument("--case")
ap.add_argument("--action",
choices=["identify", "contain",
"eradicate", "recover",
"note"])
ap.add_argument("--detail")
ap.add_argument("--rationale", default="")
ap.add_argument("--evidence")
ap.add_argument("--status")
ap.add_argument("--report", action="store_true")
args = ap.parse_args()
if args.new:
new_case(args.new, args.title or "untitled")
return
if not args.case:
sys.exit("need --case (or --new)")
if args.report:
print(report(args.case))
return
if args.evidence:
add_evidence(args.case, args.evidence)
return
if args.status:
set_status(args.case, args.status)
return
if args.action:
add_action(args.case, args.action,
args.detail or "", args.rationale)
return
print(__doc__)
if __name__ == "__main__":
main()
  • The state machine (lines 12, 74-85): the lifecycle states enforced with an order warning (not a hard block — real incidents jump, but the jump is warned and recorded) — the process discipline that makes every case’s lifecycle visible.
  • The action log (lines 52-59): every containment/eradication action records detail and rationale — the timeline the retrospective builds from, and the decision record that defends the choices after the fact.
  • The evidence chain (lines 62-71): artifacts hashed at registration (SHA-256, size, timestamp) — BT5’s chain of custody, automated into the case file.
  • The scope assumptions (lines 40-42): every new case opens with the two assumptions pre-written (persistence beyond findings, second patient zero) — the scope discipline as default state, removable only deliberately.
  • The report (lines 88-112) generates the timeline from actions, the custody list, the assumptions, and the retrospective skeleton — the after-action document that writes itself from the case file.

The incident reaches the process from BT1’s escalation, and the process reaches each phase with its tools: the declaration reaches the organization as authority exercised (the named role declaring at 3 AM), the containment reaches the endpoint as the EDR contain call (evidence preserved by memory-first capture), the eradication reaches the estate as the verified removal (the re-implosion hunt running before closure), and the recovery reaches the business as the restored service with the confidence that the adversary’s persistence is gone. The evidence reaches BT5’s forensics as the chain-of-custody’d artifacts (the memory captures, disk images, log extracts — hashed, timestamped, handled), the findings reach BT9 as the detection backlog (the rules that would have caught this earlier), and the controls reach the retrospective as the owner-dated table that the next quarter’s reviews track. The communication reaches its tiers per the pre-agreements: legal for the regulatory questions, comms for the customer-facing statements, leadership for the business decisions (pay/no-pay, shutdown/no-shutdown) — each tier informed at the right moment with the right language, the leak-prevention discipline that the pre-agreements exist for. The metrics reach BT20: MTTD from BT1, time-to-contain, time-to-recover, re-implosion rate — the IR program’s numbers, trended per incident class. And the process itself reaches the next incident improved: every retrospective’s playbook fix lands before the next page-one call, which is the only measure of an IR process that matters.

  1. Declare — BT1 escalation “DC compromise suspected” → IR lead declares INC-2026-001 within 20 minutes
  2. Scope — assumptions logged: persistence beyond findings, second patient zero possible
  3. Contain — memory capture FIRST (evidence preserved), then EDR network-contain of affected hosts
  4. Strategic — C2 domains blocked at proxy, compromised accounts disabled, VPN sessions killed
  5. Investigate — BT6 memory analysis + BT5 disk forensics: initial access = WI4 evil-twin credential harvest
  6. Eradicate — persistence removed (3 Run keys + 1 scheduled task + 1 rogue service — “all of it” discipline)
  7. Recover — clean restore, re-implosion hunt runs 72h clean, state: recovered
  8. Learn — retrospective: 4 controls with owners; SIEM rule for evil-twin harvest lands in BT9 backlog

playbookgen.py — the playbook template generator: for a named scenario (ransomware, BEC, credential compromise, insider), emits the playbook skeleton — the pre-decided decisions (containment strategy, communication tiers, evidence priorities), the role assignments, the checklist sections — seeded from a scenario table the IR lead maintains. stdlib only.

  • Pre-decided decisions, not procedures. The playbook’s value is the decisions made calmly in advance (contain strategy, who declares, what evidence first) — the template structures those, leaving the tactical steps to the scenario table.
  • Role-based, not name-based. Every assignment references a role (IR-lead, comms-lead) — names live in the contact sheet that changes; the playbook doesn’t.
  • Evidence-priority column. Each scenario declares what evidence dies first (memory for live intrusions, logs for historical) — the capture order pre-decided.
  • Review dates. Playbooks carry review-by dates — the document that rots silently is the one without them.
#!/usr/bin/env python3
"""playbookgen.py — IR playbook template generator.
Usage:
python playbookgen.py --scenario ransomware --out pb.md
python playbookgen.py --list
stdlib only.
"""
import argparse, datetime, sys
SCENARIOS = {
"ransomware": {
"declare": "any confirmed encryption event = "
"major incident immediately",
"contain": "isolate storage VLANs first; EDR "
"contain hosts second; preserve one "
"encrypted host for forensics",
"evidence_first": ["memory of one live host",
"ransom note + extension sample",
"backup state verification"],
"comms": "legal before customer comms; insurance "
"notification within policy window",
"decisions": [
"negotiate/no-negotiate (pre-decided by policy)",
"restore priority order (business pre-ranked)",
"rebuild vs clean (pre-decided per system class)"],
},
"bec": {
"declare": "confirmed mailbox rule tampering or "
"fraudulent payment request = incident",
"contain": "disable mailbox rules, revoke tokens, "
"reset creds; preserve mailbox audit",
"evidence_first": ["mailbox audit log export",
"rule definitions",
"fraudulent message headers"],
"comms": "finance + legal first; counterparty "
"banks contacted per fraud procedure",
"decisions": [
"payment recall path (bank contact tree)",
"customer notification (legal decision)",
"credential reset scope"],
},
"credential-compromise": {
"declare": "confirmed valid-credential abuse "
"beyond self-service = incident",
"contain": "revoke sessions, reset, check "
"OAuth grants, audit for persistence",
"evidence_first": ["auth logs (4624 class)",
"session/token inventory",
"persistence sweep results"],
"comms": "identity team + data owners of "
"accessible systems",
"decisions": [
"reset scope (user vs estate)",
"MFA re-enrollment",
"privilege review trigger"],
},
"insider": {
"declare": "with HR+legal co-approval (dual key)",
"contain": "monitor-do-not-tip: evidence "
"collection before any visible action",
"evidence_first": ["DLP/file-exfil logs",
"device assignment records",
"access log timeline"],
"comms": "HR+legal only until action moment",
"decisions": [
"interview timing (HR owns)",
"access revocation moment (with HR)",
"law-enforcement referral (legal)"],
},
}
def render(scenario):
s = SCENARIOS[scenario]
L = [f"# IR Playbook: {scenario}", "",
f"_review by: "
f"{(datetime.date.today() + datetime.timedelta(days=180)).isoformat()}_",
"", "## Declaration", "", s["declare"], "",
"## Containment strategy", "", s["contain"], "",
"## Evidence priority (capture in order)", ""]
for i, e in enumerate(s["evidence_first"], 1):
L.append(f"{i}. {e}")
L += ["", "## Communication tiers", "", s["comms"], "",
"## Pre-decided decisions", ""]
for d in s["decisions"]:
L.append(f"- {d}")
L += ["", "## Roles (names live in contact sheet)",
"",
"- IR lead (declares, runs bridge)",
"- containment operator (executes)",
"- evidence custodian (chain of custody)",
"- comms lead (tiers only)",
"- scribe (log everything)", ""]
return "\n".join(L) + "\n"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--scenario")
ap.add_argument("--list", action="store_true")
ap.add_argument("--out")
args = ap.parse_args()
if args.list:
print("\n".join(SCENARIOS))
return
if not args.scenario or args.scenario not in SCENARIOS:
sys.exit(f"scenario must be one of: "
f"{', '.join(SCENARIOS)}")
text = render(args.scenario)
print(text)
if args.out:
open(args.out, "w").write(text)
print(f"[+] {args.out}")
if __name__ == "__main__":
main()
  • The scenario table (lines 12-48) encodes the pre-decided decisions per scenario: ransomware’s negotiate policy and restore priority, BEC’s recall path, insider’s dual-key declaration — the decisions that take hours of bridge-call time when made live, minutes when made in advance.
  • The evidence-priority lists (lines 58-62): each scenario’s capture order — ransomware captures memory of one live host before isolation kills volatility, insider collects DLP logs before any visible action tips the subject — the ordering that the pressure of the moment gets wrong without the playbook.
  • The role discipline (lines 70-76): roles not names, with the scribe listed explicitly — the bridge call’s decision log is the retrospective’s raw material, and it doesn’t exist unless someone owns writing it.
  • The review date (line 53): 180 days out, auto-computed — the playbook that rots silently is the one without a date.

generate the ransomware playbook before you need it

Section titled “generate the ransomware playbook before you need it”

$ python playbookgen.py —scenario ransomware —out pb_ransomware.md

any confirmed encryption event = major incident immediately

  1. memory of one live host
  2. ransom note + extension sample
  3. backup state verification

$ python ircase.py —new INC-2026-001 —title “DC compromise” $ python ircase.py —case INC-2026-001 —action contain \ —detail “EDR contain FIN-SRV-04” —rationale “active C2” $ python ircase.py —case INC-2026-001 —evidence mem_fin04.bin $ python ircase.py —case INC-2026-001 —status eradicated [!] warning: jumping states (contained -> eradicated) — lifecycle order is identified->contained->eradicated->recovered->closed $ python ircase.py —case INC-2026-001 —report | head -12

IR’s detection-adjacent role is the retrospective’s feedback: every incident produces detection-gap findings (BT9’s backlog), telemetry-gap findings (BT3’s coverage), and playbook improvements (this module’s templates) — the loop that makes each incident the last of its kind. Your own tooling hygiene: ircase.py stores cases locally — production IR platforms add collaboration, legal hold, and audit trails [verify per platform]; the tool is the process skeleton, not the platform. The declaration-speed metric (alert-to-declaration) is the process health number that matters most — incidents declared late are the incidents that arrive at containment with three more patient zeros. Wazuh/SIEM integration of IR actions (containment events visible as cases progress) [verify per platform] keeps the SOC’s picture current during the incident. The standing metrics: declaration latency, containment latency, re-implosion rate (zero tolerance), retrospective completion within 2 weeks (100% target), and controls-landed rate from retrospectives — five numbers that make the IR process a managed, improving system.

  1. Case timeline exporter: auto-render the action log as a visual timeline for the retrospective.
  2. Contact-sheet validation: scheduled contact-tree test (the phone numbers that work at 3 AM).
  3. Playbook drill scheduler: tabletop exercises per scenario with the review dates driving the calendar.
  4. Evidence custody signatures: multi-party hash confirmation for evidence transfer (BT5’s chain, tightened).
  5. Metrics exporter: BT20-shaped metrics from the case files automatically.
SignalSourceIDNotes
Declaration latencyircase timelineper incidentDeclare-early metric
Containment latencyircase timelineaction logEvidence-first verified
Re-implosion ratePost-recovery huntsper incidentZero tolerance
Retrospective completionCase audit2-week targetThe learning loop
Controls-landed rateRetrospective trackingper quarterOwner+date discipline
Playbook review statusplaybokgen datesper scenarioRot-document check
Evidence custody completenessircase evidence listper caseChain-of-custody
Scope-assumption accuracyPost-incident reviewper casePatient-zero #2 found?
Communication tier timingComms logper incidentLeak prevention
Wazuh/SIEM IR case joinSIEM[verify per platform]Case-to-telemetry link

The IR-layer evasion is adversary operations against the response process — every technique exploits a process failure mode. Persistence beyond scope: counting on the responder’s incomplete eradication (the “all of it” discipline failure) — countered by the scope assumptions (assume more), the re-implosion hunts (BT7 runs against the recovered estate), and the persistence-sweep checklists that enumerate the classes (not the instances). Evidence destruction timing: triggering log rotation, forcing reboots, deploying wipers as containment begins — countered by the memory-first rule (capture before power), the remote-forwarded logging (BT3), and the containment decision matrix that prices evidence loss into the choice. Response exhaustion: multi-front incidents (three simultaneous “incidents”) that dilute the bridge — countered by the declaration criteria (separate incidents from campaigns), the case manager’s campaign linking, and the staffing reality that the drill (T-series) rehearses. Communication exploitation: adversaries reading the response (their C2 watching the containment propagate) to adapt faster than the process — countered by opsec in response actions (staged containment, decoy preservation) and by the assumption that every action is observed. Legal/comms manipulation: phishing the response team during the incident (the crisis is the lure) — countered by the out-of-band verification procedures and the pre-agreed comms templates that make impersonation visible. The meta-point: the response process is itself an attack surface, and its defenses are the pre-decisions (playbooks), the evidence discipline (custody), and the metrics that show where the process — not the adversary — is the weak point.

⚠️ What caught me: my first contained incident — I network-contained the compromised host, verified the C2 died, and started eradication feeling efficient. Twelve minutes later the endpoint’s EDR showed a second C2 channel — over the BMC (the out-of-band management controller on the motherboard), a path my containment never touched because I didn’t know the host had one. The host had been “contained” and was still phoning home through its lights-out management card. Since then, every containment playbook includes the BMC/IPMI check (does this host class have one, is it on the management network, is its channel covered), and the containment definition in my runbooks reads “no channel we haven’t enumerated” — the phrase that would have saved those twelve minutes and the two hours of scope expansion they cost.

  1. Lifecycle drill: run a simulated case through all five states with ircase.py, including a deliberate state-jump and its warning — the bookkeeping, muscle-memoryed.
  2. Playbook generation: generate all four scenario playbooks, review each with a peer, and mark which pre-decided decisions your organization would actually make differently — the calibration, honest.
  3. Evidence-first drill: on a lab VM, practice the order — memory capture, then disk image, then containment — and time each; the containment decision matrix applied under a clock.
  4. Retrospective skeleton: from a completed drill case, generate the report and fill the controls table with real owners and dates — the learning loop, exercised.
  5. BMC enumeration: on your lab hosts, enumerate management controllers (BMC/IPMI presence, network, credentials) and add the check to your containment playbook — the twelve-minute lesson, institutionalized.
  • Technical: Walk the IR lifecycle phases and name the irreversible decision in each — what does getting it wrong cost?
  • Technical: Why does memory-first capture precede all power actions, and what evidence dies with the power?
  • Scenario: Mid-incident, you discover a second patient zero in a different business unit. What changes in your process, and what was missing from your scope assumptions?
  • Conceptual: Why is the declaration authority a named role with pre-granted power — and what happens in organizations without one?
  • Conceptual: How do the retrospective’s controls-with-owners differ from a lessons-learned document — and what makes the former work?
  • Deep-dive: Design the IR program for a 5,000-endpoint estate: playbooks, staffing, tooling, communication tiers, and the metrics that govern it.
  • Practical: The board asks “are we ready for a ransomware incident?” — what does your playbook, your drill record, and your backup-verification evidence let you answer honestly?

BT1 (SOC Operations & Alert Triage) · BT5 (Digital Forensics & Evidence Handling) · BT6 (Memory Forensics) · BT20 (Detection Program Metrics & Maturity) · F12 (Cyber Kill Chain & MITRE ATT&CK)