Skip to content

Kill Chain Incident Response Playbook

This playbook is for active security incidents detected across the Cirius kill chain detection stack. It is structured for high-stress use — start at the top, follow the steps in order, do not skip sections.

SecOps: secops.bedrockcybersecurity.org
LAW workspace: cirius-logging-law-central (ID: 5d76d1f2)
Arctic Wolf portal: Arctic Wolf Concierge Security Team — phone number in Keeper → Emergency Contacts → Arctic Wolf
Cortex XDR: https://ciriusgroup.xdr.us.paloaltonetworks.com
Twingate admin: https://auth.twingate.com
Panorama: Palo Alto Panorama — credentials in Keeper → Vendor Accounts → Panorama


1. Alert Triage — How Findings Become Incidents

How Detection Works

17 security agents run in a Container Apps Job every 4 hours. Findings go to POST /api/ingest/findings. The SecurityAgent makes the incident determination:

  • Findings that match a known-good rule → auto-closed, no incident created
  • Findings with no match → incident opened (NEW → OPEN)
  • Detection agent enriches OPEN tickets with LLM triage

After each run you receive a summary email. Log into secops.bedrockcybersecurity.org and review the Incidents queue. A clean run has 0–3 OPEN incidents.

Severity Levels

SeverityMeaningResponse
CRITICALActive threat or confirmed high-risk condition. immediate_action_required may be set.Within 15 minutes. Cannot defer. If you cannot disprove in 5 minutes, contain first.
HIGHMeaningful risk, not necessarily active. Elevated activity, detected gap, approaching threshold.Same day. Investigate before closing.
MEDIUMWorth knowing, not urgent.Within 48 hours.
LOW / INFOBackground signal.Weekly review, batch-close if benign.

When to Call Arctic Wolf Immediately — No Exceptions

Call Arctic Wolf before you finish investigating if any of the following are true:

  • CRITICAL finding with immediate_action_required: true and you cannot disprove it in 5 minutes
  • Any two kill chain stages firing within the same 4-hour window (correlated attack signal)
  • Break-glass account activity (any login = CRITICAL, always real until proven otherwise)
  • Cortex XDR showing active behavioral detection (credential dumping, lateral movement, ransomware behavior)
  • Log cleared (EventID 1102) on any server
  • Evidence of active exfiltration (large outbound transfer, App-ID identifying exfil tools)

Arctic Wolf has act-first authority — they do not need your approval. They act 24/7. They can isolate hosts, block IPs, and take containment steps on your behalf. Let them.

When to Handle Solo First

  • Single-stage finding that is plausibly benign with a straightforward explanation
  • HIGH finding with a matching CM ticket in SecOps (GitHub Actions auto-creates these on merge)
  • Operational findings (backup failure, cert expiry, VM status) — not kill chain stage alerts
  • MEDIUM/LOW findings with no corroborating signal

2. First 15 Minutes — CRITICAL Alert Response

Rule: isolate first, investigate second. The cost of isolating a clean host is a support ticket. The cost of not isolating a compromised host is lateral movement.

Minute 0–2: Confirm and Classify

  1. Read the SecOps incident in full — severity, source system (PROD/DDE/AWS), kill chain stage, evidence block, affected hostnames or accounts
  2. Open Arctic Wolf portal — check if they have any alerts in the same window
  3. Open Cortex XDR → Incidents → check for any active incidents on the affected hostname
  4. Note the timestamp you discovered the alert — this is your breach discovery time for HIPAA

Minute 2–5: Call Arctic Wolf (CRITICAL Only)

Call Arctic Wolf. Give them:

  • Incident description from SecOps (read it to them verbatim if needed)
  • Affected hostname(s) or account(s)
  • Kill chain stage(s) detected
  • Whether ePHI systems are involved
  • Your SecOps incident number

Stay on the line or in the chat until they confirm they've opened a case.

Minute 5–10: Isolate the Affected Asset

Do not wait for Arctic Wolf confirmation before isolating — do both in parallel.

If the threat is on a specific host → Cortex XDR isolation:

  1. Cortex XDR console → Endpoints → find hostname
  2. Right-click → Isolate → confirm
  3. The host retains the Cortex XDR agent channel (management still works) but all other network traffic is cut
  4. Note: isolation blocks the host from communicating with everything except the XDR console

If the threat is identity-based (compromised account) → Entra block:

  1. Entra ID → Users → find user → Account → toggle Account enabled to Off
  2. Entra ID → Users → find user → Revoke sessions
  3. If user has Twingate access: Twingate admin portal → Users → find user → Suspend

If the threat is remote access abuse → Twingate revoke:

  1. Twingate admin portal → Users → find user → Suspend
  2. This immediately cuts all Twingate tunnels for that user — no grace period

If the host cannot be reached via Cortex → Palo Alto emergency block:

  1. Panorama → Policies → Security → top of ruleset
  2. Add new rule: source = affected VM private IP, destination = any, action = deny
  3. Commit and push to all firewalls

Minute 10–15: Preserve Evidence

Before any further remediation, capture what exists right now:

bash
# Export relevant LAW table for the incident window — run from Azure CLI
az monitor log-analytics query \
  --workspace 5d76d1f2 \
  --analytics-query "SecurityEvent | where TimeGenerated > ago(4h) | where Computer == 'AFFECTED-HOSTNAME' | order by TimeGenerated desc" \
  --output json > /tmp/incident-$(date +%Y%m%d-%H%M%S)-securityevent.json

# Sign-in logs for affected user
az monitor log-analytics query \
  --workspace 5d76d1f2 \
  --analytics-query "SignInLogs | where TimeGenerated > ago(24h) | where UserPrincipalName == 'user@ciriusgroup.com' | order by TimeGenerated desc" \
  --output json > /tmp/incident-$(date +%Y%m%d-%H%M%S)-signinlogs.json

Screenshot:

  • SecOps incident dashboard (current state, before any status changes)
  • Cortex XDR alert list for affected endpoints
  • Cortex XDR causality view (process tree) for the triggering alert
  • Arctic Wolf alert in their portal

3. Per-Stage Response Actions

Stage 1 — Initial Access

Detects via: SignInLogs, Twingate_CL, Palo Alto firewall threat logs

Signals:

  • Sign-in from new country or impossible travel
  • Twingate authentication from unrecognized device or unusual time
  • Palo Alto threat log: blocked brute force or credential stuffing to VPN or OWA
  • Failed MFA challenge spike followed by success (MFA fatigue)

Containment:

  1. Identify the account — is this a valid user or a service account?
  2. Entra ID → Users → [user] → Revoke sessions (cuts all active tokens immediately)
  3. Entra ID → Users → [user] → Account enabled → Off
  4. Twingate admin → Users → [user] → Suspend
  5. Block the source IP at Palo Alto: Panorama → Policies → Security → deny rule for external IP

Investigation KQL:

kql
// Establish the full sign-in history for this account in the last 7 days
SignInLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName == "user@ciriusgroup.com"
| project TimeGenerated, ResultType, ResultDescription, IPAddress, Location,
    AppDisplayName, DeviceDetail, AuthenticationRequirement, ConditionalAccessStatus
| order by TimeGenerated desc
kql
// Find the initial compromise window — when did the first successful auth from the new IP happen?
SignInLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName == "user@ciriusgroup.com"
| where ResultType == 0
| where IPAddress == "ATTACKER-IP"
| project TimeGenerated, IPAddress, Location, AppDisplayName
| order by TimeGenerated asc
kql
// Twingate access logs — what resources did this user access?
Twingate_CL
| where TimeGenerated > ago(24h)
| where user_email_s == "user@ciriusgroup.com"
| project TimeGenerated, user_email_s, resource_name_s, connector_name_s, bytes_sent_d, bytes_received_d
| order by TimeGenerated desc
kql
// Check for MFA fatigue — multiple denied MFA requests before success
SignInLogs
| where TimeGenerated > ago(6h)
| where UserPrincipalName == "user@ciriusgroup.com"
| where ResultType in (50074, 500121, 500133)
| summarize DeniedMFA = count() by bin(TimeGenerated, 5m), UserPrincipalName, IPAddress
| order by TimeGenerated desc

Evidence to preserve:

  • Full sign-in log export for the account (7-day window minimum)
  • Twingate access log for the same window
  • Palo Alto threat log entries showing the source IP
  • List of all applications the account accessed after the suspicious login (from AppDisplayName in SignInLogs)

Stage 2 — Execution

Detects via: EventID 4688 (process creation), EventID 4103/4104 (PowerShell script block)

Signals:

  • Known attacker tools spawned: mimikatz, cobalt strike beacons, mshta.exe, wscript.exe, rundll32.exe from unusual parents
  • Processes executing from %TEMP%, %APPDATA%, user profile directories
  • Encoded PowerShell (-enc, -encodedcommand)
  • Process creation where parent is Office, browser, or help-desk software

Containment:

  1. Cortex XDR → Endpoints → isolate the affected host immediately
  2. Do NOT shut down the host — running memory contains forensic evidence
  3. Cortex XDR → Response → Live Terminal (available even in isolation) to dump running process list if Arctic Wolf IR team requests it

Investigation KQL:

kql
// Find process creation events from known attacker tools
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID == 4688
| where Computer == "AFFECTED-HOSTNAME"
| where NewProcessName has_any ("mimikatz", "mshta.exe", "wscript.exe", "cscript.exe",
    "regsvr32.exe", "certutil.exe", "bitsadmin.exe", "powershell.exe", "cmd.exe")
| where ParentProcessName !in ("<known-legitimate-parents>")
| project TimeGenerated, Computer, Account, NewProcessName, CommandLine, ParentProcessName
| order by TimeGenerated desc
kql
// Find processes launched from suspicious directories
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID == 4688
| where Computer == "AFFECTED-HOSTNAME"
| where CommandLine has_any ("\\AppData\\", "\\Temp\\", "\\Users\\Public\\", "\\ProgramData\\")
| project TimeGenerated, Computer, Account, NewProcessName, CommandLine, ParentProcessName
| order by TimeGenerated desc
kql
// PowerShell encoded commands or suspicious script blocks
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID in (4103, 4104)
| where Computer == "AFFECTED-HOSTNAME"
| where MessageNumber has_any ("-enc", "-encodedcommand", "IEX", "Invoke-Expression",
    "DownloadString", "WebClient", "bypass")
| project TimeGenerated, Computer, Account, EventData
| order by TimeGenerated desc
kql
// Full process tree for a specific parent process (pivot from suspicious process)
SecurityEvent
| where TimeGenerated > ago(6h)
| where EventID == 4688
| where Computer == "AFFECTED-HOSTNAME"
| where ParentProcessName has "SUSPICIOUS-PROCESS-NAME"
| project TimeGenerated, Computer, Account, NewProcessName, CommandLine, ParentProcessName
| order by TimeGenerated asc

Evidence to preserve:

  • Cortex XDR causality view screenshot (process tree) — export as PDF if available
  • Full EventID 4688 export for the affected host (6-hour window)
  • PowerShell 4103/4104 events with full script block text
  • Running process list captured via Cortex Live Terminal before the session ends

Stage 3 — Persistence

Detects via: EventID 7045 (new service), EventID 4698 (new scheduled task), FIM registry alerts

Signals:

  • New Windows service installed outside a CM window
  • New scheduled task with encoded command or unusual executable
  • FIM alert on HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run or similar run keys
  • Service with ServiceFileName pointing to temp directory or non-standard path

Containment:

  1. Cortex XDR → isolate the host if not already isolated
  2. Identify the persistence mechanism (service name, task name, registry key) from the alert evidence
  3. Do NOT remove the persistence artifact yet — preserve it for forensics first

Investigation KQL:

kql
// New services installed in the last 24 hours
SecurityEvent
| where TimeGenerated > ago(24h)
| where EventID == 7045
| project TimeGenerated, Computer, ServiceName = tostring(EventData), Account
| order by TimeGenerated desc
kql
// New scheduled tasks — look for encoded commands or unusual paths
SecurityEvent
| where TimeGenerated > ago(24h)
| where EventID == 4698
| project TimeGenerated, Computer, Account, TaskName = tostring(EventData)
| order by TimeGenerated desc
kql
// Correlate new persistence with the execution timeline — did service install follow suspicious process?
SecurityEvent
| where TimeGenerated > ago(6h)
| where Computer == "AFFECTED-HOSTNAME"
| where EventID in (7045, 4698, 4688)
| project TimeGenerated, EventID, Account, EventData
| order by TimeGenerated asc
kql
// Check for new local accounts created (another persistence mechanism)
SecurityEvent
| where TimeGenerated > ago(24h)
| where EventID == 4720
| project TimeGenerated, Computer, TargetAccount = TargetAccount, SubjectAccount = SubjectUserName
| order by TimeGenerated desc

Evidence to preserve:

  • Service binary path and hash (get from Cortex Live Terminal: sc qc <servicename>)
  • Scheduled task XML export: schtasks /query /fo XML /tn "<taskname>" > task.xml
  • FIM alert contents in full from SecOps
  • Registry key value (captured via Cortex Live Terminal if host is already isolated)

Stage 4 — Credential Dumping

Detects via: EventID 4688 showing LSASS access, multi-target authentication failures

Signals:

  • 4688 with NewProcessName or CommandLine referencing LSASS (process handle, memory access)
  • Cortex XDR behavioral alert: "Credential Dumping via LSASS Access"
  • Multiple authentication failures from a single host to multiple targets in rapid succession
  • Unusual account accessing LSASS (rundll32.exe, comsvcs.dll MiniDump, taskmgr.exe)

Containment:

  1. Cortex XDR → isolate the affected host immediately — credential dumps mean the attacker has or is acquiring credentials; lateral movement is imminent if not already occurring
  2. Block the host at Palo Alto (belt and suspenders with XDR isolation):
    Panorama → Policies → Security → add deny rule for affected VM private IP (any direction)
  3. Assume all credentials on that host are compromised — initiate password rotation for any account that has logged into that machine
  4. Check Entra ID → Users → any accounts that authenticated to this host → Revoke sessions for all of them

Investigation KQL:

kql
// LSASS access attempts — process creation referencing LSASS
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID == 4688
| where Computer == "AFFECTED-HOSTNAME"
| where CommandLine has_any ("lsass", "MiniDump", "comsvcs", "sekurlsa", "wce ", "fgdump")
| project TimeGenerated, Computer, Account, NewProcessName, CommandLine, ParentProcessName
| order by TimeGenerated desc
kql
// Multi-target auth failures from a single source (credential spray from dumped creds)
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID == 4625
| summarize FailCount = count(), Targets = make_set(Computer) by SubjectUserName, IpAddress
| where FailCount > 5
| where array_length(Targets) > 2
| order by FailCount desc
kql
// Accounts that authenticated to the compromised host — potential credential exposure
SecurityEvent
| where TimeGenerated > ago(7d)
| where EventID in (4624, 4648)
| where Computer == "AFFECTED-HOSTNAME"
| project TimeGenerated, Account, LogonType, IpAddress, WorkstationName
| summarize LastSeen = max(TimeGenerated), LogonCount = count() by Account, LogonType
| order by LastSeen desc

Evidence to preserve:

  • Cortex XDR alert details for the LSASS access (causality view, full alert export)
  • List of all accounts that authenticated to the affected host (from query above) — this is your "credentials to rotate" list
  • Memory dump of LSASS if Arctic Wolf or IR firm requests it — do not do this yourself without IR firm guidance

Stage 5 — Lateral Movement

Detects via: EventID 4624/4648 logon chains, EventID 5140 admin share access

Signals:

  • Same account authenticating to 3+ machines within 30 minutes
  • EventID 4648 (explicit credentials) from workstation to server — attacker using credentials they stole to move
  • EventID 5140 showing access to C$, ADMIN$, or IPC$ administrative shares
  • Logon type 3 (network logon) from a host that does not normally access the target

Containment:

  1. Identify all machines the attacker has touched — run the lateral movement query below
  2. Isolate ALL confirmed affected hosts via Cortex XDR (can isolate multiple hosts simultaneously from the Endpoints view with bulk select)
  3. Block the compromised account(s) in Entra and revoke sessions
  4. Block compromised host IPs at Palo Alto if XDR isolation is not available

Investigation KQL:

kql
// Lateral movement pattern — same account hitting multiple machines in short time window
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID == 4624
| where LogonType == 3
| summarize MachinesAccessed = dcount(Computer), Machines = make_set(Computer),
    FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated)
    by TargetUserName, IpAddress
| where MachinesAccessed >= 3
| order by MachinesAccessed desc
kql
// Explicit credential use (4648) — attacker using stolen creds to authenticate elsewhere
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID == 4648
| where Computer == "AFFECTED-HOSTNAME" or TargetServerName == "AFFECTED-HOSTNAME"
| project TimeGenerated, Computer, SubjectUserName, TargetUserName,
    TargetServerName, ProcessName, IpAddress
| order by TimeGenerated desc
kql
// Admin share access (5140) — C$, ADMIN$, IPC$ access
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID == 5140
| where ShareName has_any ("C$", "ADMIN$", "IPC$")
| project TimeGenerated, Computer, SubjectUserName, ShareName, IpAddress, ObjectType
| order by TimeGenerated desc
kql
// Build a full timeline of all machines touched by the attacker account
SecurityEvent
| where TimeGenerated > ago(24h)
| where EventID in (4624, 4648, 5140)
| where TargetUserName == "COMPROMISED-ACCOUNT" or SubjectUserName == "COMPROMISED-ACCOUNT"
| project TimeGenerated, EventID, Computer, SubjectUserName, TargetUserName, IpAddress, LogonType
| order by TimeGenerated asc

Evidence to preserve:

  • Full lateral movement timeline (last 24–48 hours) for all affected accounts
  • List of all machines confirmed touched by attacker (use for blast radius assessment)
  • Any admin share access events with the files or directories accessed (from EventData)

Stage 6 — Exfiltration

Detects via: Palo Alto traffic logs, App-ID, DNS Security

Signals:

  • Large outbound transfer to a destination not seen in the past 30 days
  • App-ID identifying RClone, WinSCP, MEGAsync, or similar exfil tools
  • DNS Security blocking C2 or exfil domains
  • Sustained outbound traffic outside business hours (especially from servers)
  • Traffic on unusual ports from endpoints that don't normally initiate outbound

Containment:

  1. Block the destination IP/domain at Palo Alto immediately:
    Panorama → Policies → Security → add deny rule for destination IP
    Panorama → Objects → Addresses → add exfil destination to block list
  2. If domain-based: add to Palo Alto URL Filtering block list or DNS Security block
  3. Isolate the source host via Cortex XDR
  4. If exfiltration is still active (traffic ongoing): DNS Security can be used to sinkhole the domain — work with Arctic Wolf on this step

Investigation KQL — check Palo Alto CEF logs in LAW:

kql
// Large outbound transfers — find outlier destinations by bytes
CommonSecurityLog
| where TimeGenerated > ago(24h)
| where DeviceVendor == "Palo Alto Networks"
| where CommunicationDirection == "Outbound" or DestinationIP !startswith "10."
| summarize TotalBytes = sum(SentBytes + ReceivedBytes), Sessions = count(),
    Destinations = make_set(DestinationIP)
    by SourceIP, DestinationHostName
| where TotalBytes > 100000000  // 100MB threshold — adjust to baseline
| order by TotalBytes desc
kql
// App-ID detections for known exfil tools
CommonSecurityLog
| where TimeGenerated > ago(24h)
| where DeviceVendor == "Palo Alto Networks"
| where ApplicationProtocol has_any ("rclone", "winscp", "mega", "dropbox", "gdrive",
    "onedrive", "bittorrent", "teamviewer", "anydesk")
| project TimeGenerated, SourceIP, DestinationIP, DestinationHostName,
    ApplicationProtocol, SentBytes, ReceivedBytes
| order by TimeGenerated desc
kql
// DNS Security blocks — potential C2 or exfil domain callbacks
CommonSecurityLog
| where TimeGenerated > ago(24h)
| where DeviceVendor == "Palo Alto Networks"
| where Activity == "dns-security" or DeviceAction has "block"
| project TimeGenerated, SourceIP, DestinationHostName, Activity, DeviceAction, Message
| order by TimeGenerated desc
kql
// Outbound traffic outside business hours from servers (not endpoints)
CommonSecurityLog
| where TimeGenerated > ago(48h)
| where DeviceVendor == "Palo Alto Networks"
| where SourceIP startswith "10.0."  // adjust to server subnet range
| where hourofday(TimeGenerated) !between (7 .. 19)  // outside 7am-7pm
| where SentBytes > 10000000  // 10MB+
| project TimeGenerated, SourceIP, DestinationIP, DestinationHostName, SentBytes, ApplicationProtocol
| order by SentBytes desc

Evidence to preserve:

  • Palo Alto traffic log export for source IP (48-hour window) — export via Panorama Monitor
  • App-ID session details for any exfil tool detections
  • DNS Security block logs with blocked domain list
  • Estimate of data volume: total bytes sent to the exfil destination (from query above)
  • This data feeds the HIPAA breach determination — capture it before it ages out of the Palo Alto local log buffer (logs age out faster than LAW)

Stage 7 — Defense Evasion

Detects via: EventID 1102 (log cleared), Cortex XDR silence detection

Signals:

  • EventID 1102: Windows Security audit log cleared — this is almost never legitimate
  • Cortex XDR returning 0 findings across multiple endpoints while other kill chain agents are elevated (EDR silence = attacker disabled or killed the agent)
  • Security tool processes terminated (Cortex agent process killed)
  • New exclusion added to Cortex XDR without a CM ticket
  • Palo Alto log forwarding stopped or gap in CEF log stream to LAW

Containment:

  1. If logs were cleared: the cleared log is gone — but the 1102 event itself is in LAW. The clearance time is your new earliest timestamp for that host — adjust your investigation window
  2. Verify Cortex XDR is still running on affected endpoints:
    Cortex XDR → Endpoints → check agent status (green = connected, red = disconnected)
  3. If Cortex agent is disconnected: attempt reinstall via Intune or Cortex console; if the host is isolated this will not work until you temporarily un-isolate
  4. If Palo Alto log forwarding stopped: Panorama → Device → Log Forwarding → verify syslog destination is configured and reachable; check if the LAW-bound syslog agent VM is still up

Investigation KQL:

kql
// Log clear events — EventID 1102 is the Windows Security log being cleared
SecurityEvent
| where TimeGenerated > ago(24h)
| where EventID == 1102
| project TimeGenerated, Computer, Account, EventData
| order by TimeGenerated desc
kql
// Identify the log clearance window — what's the gap before the 1102?
SecurityEvent
| where Computer == "AFFECTED-HOSTNAME"
| summarize EarliestEvent = min(TimeGenerated), LatestEvent = max(TimeGenerated),
    EventCount = count()
| project Computer, EarliestEvent, LatestEvent, EventCount,
    GapWarning = "Events before EarliestEvent may be lost due to log clear"
kql
// Check if Cortex XDR events have stopped flowing (silence detection)
// No native Cortex table in LAW — use this as a proxy via SecurityEvent heartbeat
SecurityEvent
| where TimeGenerated > ago(4h)
| where Computer == "AFFECTED-HOSTNAME"
| summarize LastEvent = max(TimeGenerated)
| extend SilenceDuration = now() - LastEvent
| where SilenceDuration > 2h
kql
// Check for service termination or stopping — Cortex agent running as service
SecurityEvent
| where TimeGenerated > ago(4h)
| where EventID in (7034, 7036)  // service crashed, service state change
| where EventData has_any ("CortexXDR", "traps", "cyserver")
| project TimeGenerated, Computer, EventData
| order by TimeGenerated desc

Evidence to preserve:

  • Screenshot of 1102 event with full EventData (who cleared, when)
  • S3 archive in cirius-archive-logs-prod — if the on-host log was cleared, the syslog-ng archives in S3 may still have the pre-clearance events (depends on whether syslog was forwarding in real time before the clear)
  • Cortex XDR endpoint health screenshot showing agent status before/after

Stage 8 — Encryption (Ransomware)

Detects via: FIM alerts (mass file modification), Cortex XDR behavioral (ransomware patterns)

Signals:

  • FIM alert: hundreds of file modification events in a short window (encryption in progress)
  • Cortex XDR behavioral alert: "Ransomware Behavior" or "Mass File Modification"
  • Users reporting files renamed with unknown extension or encrypted
  • Network shares becoming inaccessible
  • Veeam backup jobs failing (attacker targeting backups)

Containment — this is the maximum severity response:

  1. All hands immediately. Call Arctic Wolf — if encryption is active, this is a running clock. Call Greg and Kevin (DR-only escalation).
  2. Cortex XDR → Endpoints → select all affected hosts → Isolate — isolate every host that FIM or Cortex has flagged; isolate neighbors in the same VLAN if unsure
  3. Do NOT shut down affected hosts. Running memory may contain the encryption key or attacker tooling needed for forensics. Power-off only on Arctic Wolf or IR firm instruction.
  4. Azure subscription lockdown:
    Azure portal → Subscriptions → [subscription] → Resource locks
    → Add lock → Type: ReadOnly → Name: incident-lockdown-YYYYMMDD
    This prevents the attacker from deleting backup snapshots or storage accounts.
  5. Check Veeam immediately — Veeam console → Jobs → verify last successful backup time and confirm backup storage is intact. If attacker has reached backup infrastructure, stop Veeam services and disconnect backup storage from the network.
  6. Do NOT attempt to restore yet. The attacker may still be present. Restoration before eradication re-infects clean systems.

Investigation KQL:

kql
// FIM mass modification events — encryption signature
// Adjust table name to match your FIM data source in LAW
ConfigurationChange
| where TimeGenerated > ago(2h)
| where ConfigChangeType == "Files"
| summarize ModifiedFiles = count() by Computer, bin(TimeGenerated, 5m)
| where ModifiedFiles > 50  // threshold — encryption modifies files in bulk
| order by ModifiedFiles desc
kql
// Backup job status — did the attacker hit Veeam?
// Veeam logs in LAW if syslog forwarding is configured
Syslog
| where TimeGenerated > ago(24h)
| where ProcessName has "Veeam"
| where SeverityLevel in ("warning", "error", "critical")
| project TimeGenerated, Computer, ProcessName, SyslogMessage
| order by TimeGenerated desc
kql
// Identify the patient zero — earliest encryption event by host
ConfigurationChange
| where TimeGenerated > ago(24h)
| where ConfigChangeType == "Files"
| where FileContentChecksum != ""  // only actual file modifications
| summarize FirstModification = min(TimeGenerated), ModifiedFiles = count() by Computer
| order by FirstModification asc
// The first host in this list is likely patient zero

Evidence to preserve:

  • Sample of encrypted files (filenames, extensions, ransom note if present) — photograph on your phone, do not copy files to clean systems
  • Veeam job history showing last clean backup timestamp
  • Cortex XDR ransomware alert export
  • Full FIM event export for the 24 hours before and after discovery

4. Evidence Preservation

Before Any Containment Action

Take these captures in the first few minutes, before you change anything:

  1. SecOps incident screenshot — dashboard view showing all open incidents and their timestamps
  2. Cortex XDR alert list — screenshot of all active alerts for affected hostnames
  3. Cortex XDR causality view — process tree for the primary triggering alert (screenshot + export if available)
  4. Arctic Wolf portal — any active alerts for the same time window

LAW Query Export

bash
# Export SecurityEvent for affected host — last 24 hours
az monitor log-analytics query \
  --workspace 5d76d1f2 \
  --analytics-query "SecurityEvent | where TimeGenerated > ago(24h) | where Computer == 'HOSTNAME' | order by TimeGenerated desc" \
  --output json > incident-securityevent-HOSTNAME.json

# Export SignInLogs for affected account
az monitor log-analytics query \
  --workspace 5d76d1f2 \
  --analytics-query "SignInLogs | where TimeGenerated > ago(7d) | where UserPrincipalName == 'user@ciriusgroup.com' | order by TimeGenerated desc" \
  --output json > incident-signinlogs-user.json

# Export Palo Alto threat logs for the incident window
az monitor log-analytics query \
  --workspace 5d76d1f2 \
  --analytics-query "CommonSecurityLog | where TimeGenerated > ago(48h) | where DeviceVendor == 'Palo Alto Networks' | where SourceIP == 'AFFECTED-IP' or DestinationIP == 'AFFECTED-IP' | order by TimeGenerated desc" \
  --output json > incident-paloalto-AFFECTED-IP.json

Cortex XDR Forensic Data

  1. Cortex XDR → Incidents → select the incident → Export (downloads full incident report)
  2. Cortex XDR → Endpoints → select affected host → Collect Forensic Data
    • This packages running processes, open files, network connections, registry hives, and event log files into a bundle retrievable from the console
    • This is the primary forensic artifact for the affected host
  3. Cortex XDR → Response → Live Terminal (works even in isolation mode) — capture:
    powershell
    Get-Process | Select-Object Name, Id, CPU, Path, StartTime | Export-Csv c:\temp\processes.csv
    netstat -nao | Out-File c:\temp\netstat.txt
    schtasks /query /fo LIST /v | Out-File c:\temp\scheduled-tasks.txt
    Get-Service | Where-Object {$_.StartType -ne 'Disabled'} | Select-Object Name, Status, StartType | Export-Csv c:\temp\services.csv
    reg export HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run c:\temp\run-keys.reg

Palo Alto Log Export (Panorama)

Panorama → Monitor → Logs → Traffic or Threat → set time filter → Export to CSV

Capture both Traffic and Threat logs for:

  • Source IP = affected host's private IP
  • Destination IP = any suspected exfil destination
  • The 48-hour window around initial compromise (not just the detection window)

S3 Archive

If on-host logs were cleared, check S3 archive cirius-archive-logs-prod in the AWS Logging account (038901680748):

bash
# List available syslog archives for the date range
aws s3 ls s3://cirius-archive-logs-prod/syslogs/ --profile logging-ro

# Download logs for the affected host
aws s3 cp s3://cirius-archive-logs-prod/syslogs/HOSTNAME/YYYY/MM/DD/ ./evidence/ --recursive --profile logging-ro

Evidence Storage

Store all exported evidence in a dated folder: incident-YYYYMMDD/. Do not store on potentially compromised systems. Use the AWS DR environment or a local machine that was not part of the incident scope.


5. Containment Options Reference

Twingate — Revoke Remote User Access

Immediate effect. No grace period. Cuts all active tunnels.

Twingate admin portal → https://auth.twingate.com
→ Users → search for user → Suspend

To revoke a specific resource (not the whole user):

Twingate → Resources → find resource → Users → remove the user or their group

To revoke all access immediately and block re-authentication:

Remove user from all Twingate-mapped Entra groups
+ Suspend user in Twingate portal
+ Disable user in Entra ID

Cortex XDR — Host Isolation

Cuts all network traffic to/from the endpoint except the XDR management channel. Live Terminal remains available in isolation mode.

Cortex XDR → Endpoints → find hostname → right-click → Isolate

Bulk isolation:

Cortex XDR → Endpoints → check boxes on multiple hosts → Actions → Isolate

To un-isolate (when eradication is confirmed):

Cortex XDR → Endpoints → find hostname → right-click → Release from Isolation

NSG Emergency Block — Azure VM Network

Use if Cortex XDR isolation is unavailable or as belt-and-suspenders:

bash
# Add a deny-all inbound rule at priority 100 (overrides all other rules)
az network nsg rule create \
  --resource-group RG-NAME \
  --nsg-name NSG-NAME \
  --name EmergencyBlock-$(date +%Y%m%d) \
  --priority 100 \
  --direction Inbound \
  --access Deny \
  --protocol "*" \
  --source-address-prefixes "*" \
  --destination-address-prefixes "*" \
  --destination-port-ranges "*"

# Add outbound deny as well for exfiltration prevention
az network nsg rule create \
  --resource-group RG-NAME \
  --nsg-name NSG-NAME \
  --name EmergencyBlockOutbound-$(date +%Y%m%d) \
  --priority 100 \
  --direction Outbound \
  --access Deny \
  --protocol "*" \
  --source-address-prefixes "*" \
  --destination-address-prefixes "*" \
  --destination-port-ranges "*"

Remember to remove these rules after the incident. They block Terraform deploys.

Entra ID — Block Account and Revoke All Sessions

bash
# Disable the account
az ad user update --id user@ciriusgroup.com --account-enabled false

# Via portal: Entra ID → Users → [user] → Account → Account enabled → Off
# Via portal: Entra ID → Users → [user] → Revoke sessions

Revoking sessions invalidates all refresh tokens immediately. Existing access tokens remain valid for up to 1 hour (token lifetime) — MFA Conditional Access policies with sign-in frequency set to 1 hour or less close this gap.

For privileged accounts: also revoke any active PIM role assignments:

Entra ID → Identity Governance → Privileged Identity Management
→ Azure AD roles → Active assignments → [user] → Deactivate

Palo Alto — Emergency Block Rule

For external attacker IPs:

Panorama → Policies → Security
→ Add rule at the TOP of the ruleset (priority matters)
→ Source: [attacker IP or IP range]
→ Destination: any
→ Application: any
→ Action: Deny
→ Name: EmergencyBlock-YYYYMMDD-[reason]
→ Commit → Push to All

For internal compromised hosts (belt-and-suspenders with NSG/XDR):

Panorama → Policies → Security
→ Add rule at top
→ Source: [affected VM private IP]
→ Destination: any
→ Action: Deny
→ Log at session start and end
→ Commit → Push to All

To enable Zone Protection for active attack (blocks all new sessions in a zone):

Panorama → Network → Network Profiles → Zone Protection
→ Select the zone → Reconnaissance Protection → enable flood protection
(coordinate with Arctic Wolf before enabling — this affects all zone traffic)

6. Arctic Wolf Coordination

Opening a Case

Go to the Arctic Wolf portal → Concierge Security Team. Phone number is in Keeper → Emergency Contacts → Arctic Wolf. For P1 active incidents, call — do not rely on portal alone.

What to Give Them at First Contact

Have this ready before you call:

  • [ ] Incident summary: what was detected, from which source (SecOps agent, Cortex XDR, etc.)
  • [ ] Kill chain stage(s) triggered and timestamps
  • [ ] Affected hostnames and private IP addresses
  • [ ] Affected account UPNs
  • [ ] Whether ePHI systems are in scope (use CMDB PHI adjacency flags to confirm)
  • [ ] What containment has already been done (who isolated what, when)
  • [ ] SecOps incident number
  • [ ] Your best estimate of initial compromise time vs. detection time

Acting on Their Instructions

Arctic Wolf has act-first authority. When they tell you to do something, do it. They operate on information from their own sensor network, threat intelligence feeds, and monitoring stack — they may know things your tools haven't surfaced yet.

Common instructions they may give:

  • Isolate additional hosts you haven't yet identified — do it
  • Block specific IPs or domains at Palo Alto — do it immediately, log the change
  • Reset credentials for specific accounts — do it and confirm back
  • Do not restore from backup yet — respect this; they're preserving forensic state
  • Stand up clean systems in the AWS DR environment — escalate to Kevin or Greg if you need DR access

Log every instruction they give and what time you carried it out. Add these as analyst notes in the SecOps incident.

Acting on Their Behalf (They May Take Action Directly)

Arctic Wolf may take direct containment actions using their platform access. This is expected and authorized — they have:

  • Full read access to Azure tenant and sign-in logs
  • Access to Cortex XDR console (if connector is configured)
  • Access to all VLCs (network sensors)

They will notify you of actions they take. Log these in SecOps as well.

Handoff — What They Need for Extended IR

If Arctic Wolf is taking lead on the investigation:

ItemHow to Provide
Azure read accessGuest account with Reader role or coordinate via Microsoft support case
Entra sign-in log exportLAW query export → share via secure channel
Palo Alto threat log exportPanorama CSV export → share via secure channel
Cortex XDR console accessAdd their analyst account to Cortex admin if they request
SecOps incident URLShare secops.bedrockcybersecurity.org incident URL (they may need a viewer account)
S3 archive accessIAM read-only role assumption — coordinate with AWS account access
Veeam console read accessVeeam credentials in Keeper → share via phone, not message

7. HIPAA Breach Determination

When a Security Incident Becomes a Potential Breach

Under HIPAA §164.308(a)(6) and the Breach Notification Rule (§164.402), a breach is the unauthorized acquisition, access, use, or disclosure of unsecured PHI. The presumption is that any unauthorized access to ePHI is a breach unless you can demonstrate that the risk to the ePHI is low based on a four-factor risk assessment.

A potential HIPAA breach exists when:

  • An attacker accessed or had access to a system confirmed to contain ePHI
  • An account with ePHI access was compromised (even if you can't confirm ePHI was touched)
  • Exfiltration is detected from a system in the ePHI data path
  • Ransomware encrypted files on a system that processes ePHI (encryption = assumed access)

The four factors for breach risk assessment:

  1. Nature and extent of ePHI involved (type of data, identifiers included)
  2. Who accessed the ePHI (external attacker vs internal authorized user)
  3. Whether ePHI was actually acquired or viewed (or just accessible)
  4. Extent to which risk to ePHI has been mitigated

The 60-Day Clock

The HIPAA breach notification clock starts the day you discover the incident — not the day you confirm it is a breach. Breach discovery = when you first know or should have known about it. This is the SecOps alert timestamp.

NotificationDeadline from DiscoveryNotes
Affected individuals60 calendar daysWritten notice required
HHS60 calendar daysAll breaches; large breaches also have real-time web entry
Media notice60 calendar daysOnly required if 500+ individuals in a state
Business AssociatesWithout unreasonable delayBA must notify covered entity

60 days goes fast. Start the documentation the day of discovery.

Rory → Kevin → Greg Escalation

Rory is the sole IT/security resource. For a HIPAA breach:

  1. Rory discovers and contains — handles all technical response
  2. Rory notifies Kevin and Greg immediately by phone — they are the DR escalation path and must be aware for leadership and legal coordination
  3. Kevin or Greg engages Adriana — she manages BAA/vendor/risk docs and uploads compliance documentation to SharePoint
  4. Legal counsel engagement — must happen before sending any breach notification to individuals or HHS; Kevin/Greg coordinate this

If Rory is unreachable: Kevin or Greg contact Arctic Wolf directly (they have the contact number) and notify Rory as soon as possible.

Documentation Requirements

Start a breach documentation log immediately in SecOps (create an incident of type "HIPAA Breach Assessment"). Minimum required content:

  • [ ] Date and time incident was discovered (SecOps alert timestamp)
  • [ ] Best estimate of initial compromise date (from earliest IOC in LAW)
  • [ ] Description of what happened
  • [ ] Systems involved and their ePHI classification (refer to CMDB PHI adjacency flags)
  • [ ] Nature of ePHI on affected systems (types: demographics, diagnoses, billing, etc.)
  • [ ] Number of individuals potentially affected
  • [ ] Whether the ePHI was acquired/viewed or only accessible
  • [ ] Containment actions taken and timestamps
  • [ ] Four-factor risk assessment (document reasoning for each factor)
  • [ ] Determination: breach / not a breach / inconclusive (requires legal review)

This documentation is the audit trail for HIPAA §164.308(a)(6)(ii) — incident response procedures. Regulators will ask for it.


8. Eradication and Recovery

Do not begin eradication until:

  1. Arctic Wolf gives the all-clear (or incident is confirmed contained and attacker is no longer active in the environment)
  2. All forensic evidence has been captured
  3. The full blast radius is known — every affected host and account has been identified

Verify Clean Before Re-enabling Anything

For each affected host:

  1. Cortex XDR → Endpoints → run a full scan on the isolated host:
    Endpoints → [hostname] → Scan → Full Scan
  2. Review scan results — zero threats required before proceeding
  3. Check for persistence: review scheduled tasks, services, and run keys via Live Terminal
    powershell
    schtasks /query /fo LIST /v
    sc query type= all state= all
    reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
    reg query HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  4. If any attacker artifacts found: do not clear manually — restore from backup instead

For Veeam restoration:

  1. Identify the last clean backup: the backup run must pre-date the earliest IOC timestamp (from your LAW query for the affected host)
  2. Restore to an isolated network segment first — do not connect directly to production
  3. Run Cortex XDR scan on the restored VM before connecting to the main network
  4. Verify no attacker artifacts (repeat the persistence check above on the restored VM)

Credential Rotation — Required for All Confirmed Breaches

Rotate in this order:

  1. All accounts that authenticated to compromised hosts (from Stage 4/5 investigation queries)
  2. All service accounts on the affected systems
  3. All Palo Alto local admin accounts if firewall was in scope
  4. All Entra privileged accounts if credential dumping is confirmed
  5. AWS root accounts only if AWS environment was part of the blast radius

For on-prem AD accounts:

powershell
Set-ADAccountPassword -Identity "username" -Reset -NewPassword (ConvertTo-SecureString "NEWPASSWORD" -AsPlainText -Force)

For Entra accounts: Entra ID → Users → [user] → Reset password + require change at next sign-in

Re-enabling Access

  1. Cortex XDR → un-isolate the host (Release from Isolation)
  2. Remove emergency NSG deny rules:
    bash
    az network nsg rule delete --resource-group RG-NAME --nsg-name NSG-NAME --name EmergencyBlock-YYYYMMDD
  3. Remove emergency Palo Alto block rules: Panorama → Policies → remove rules → Commit → Push
  4. Re-enable Twingate: Twingate admin portal → Users → [user] → Unsuspend
  5. Re-enable Entra account: Entra ID → Users → [user] → Account enabled → On
  6. Remove Azure subscription ReadOnly lock if it was applied:
    Azure portal → Subscriptions → [subscription] → Resource locks → delete the lock
    Confirm with Rory before removing the lock — Terraform deploys will fail while it's active.

Validate Monitoring is Back

After restoring systems:

kql
// Verify SecurityEvent is flowing again from restored host
SecurityEvent
| where TimeGenerated > ago(2h)
| where Computer == "RESTORED-HOSTNAME"
| summarize EventCount = count(), LatestEvent = max(TimeGenerated) by Computer
kql
// Verify sign-in telemetry is flowing
SignInLogs
| where TimeGenerated > ago(2h)
| summarize EventCount = count(), LatestEvent = max(TimeGenerated)
kql
// Check that Palo Alto CEF is still flowing to LAW
CommonSecurityLog
| where TimeGenerated > ago(2h)
| where DeviceVendor == "Palo Alto Networks"
| summarize EventCount = count(), LatestEvent = max(TimeGenerated) by DeviceVendor

Check Cortex XDR → Endpoints — all hosts should show green (connected) status.

Run a test detection if needed: Cortex XDR → Policy → EICAR test or safe test payload to confirm behavioral detection is active.


9. Post-Incident Documentation

What to Record in SecOps

Before closing the incident, add an analyst note with:

  • [ ] Full incident timeline (discovery → containment → eradication → recovery) with timestamps
  • [ ] Root cause: how did the attacker get in? (initial access vector)
  • [ ] Blast radius: every affected host and account, with confirmation each was remediated
  • [ ] Evidence artifacts: what was captured and where it is stored
  • [ ] HIPAA breach determination: breach / not a breach / inconclusive, with reasoning
  • [ ] Containment actions taken (who did what, when)
  • [ ] Recovery actions taken (what was restored from what backup, when)
  • [ ] Change management: any emergency changes made during response that need a CM ticket
  • [ ] Any outstanding items (credential rotations pending, rules to add, etc.)

Change incident status to RESOLVED when all of the above are complete.

Lessons Learned

Schedule a post-incident review within 5 business days. Document:

  • What worked: which detections fired correctly, which containment actions were effective
  • What didn't: any detection gaps, delays in containment, evidence that wasn't captured
  • What to change: specific procedural or technical improvements

For any detection gap identified: file a work item against the kill chain agent that should have caught the stage, or identify the missing log source.

Detection Rule Updates

For each stage that fired late or not at all:

  1. Identify the specific query or agent that missed the signal
  2. Add the missed indicator to the relevant kill chain agent's detection logic (Execution agent, Persistence agent, etc.) in ops-automation/agents/
  3. Update the KQL threshold or pattern in the agent's detection query
  4. Create a CM ticket in SecOps for the rule change before deploying
  5. Verify the updated agent fires correctly in the next 4-hour cycle

For new IOCs discovered during the incident (attacker IPs, domains, hashes):

  1. Add external IPs to the Palo Alto block list (EDL or Security policy)
  2. Add domains to DNS Security block list in Panorama
  3. Add file hashes to Cortex XDR custom IOC list:
    Cortex XDR → Indicators → Custom Indicators → Add
  4. Document the IOCs in the SecOps incident for audit trail

Required Sign-offs

Before the incident is fully closed:

  • [ ] Rory confirms eradication complete and monitoring restored
  • [ ] Kevin/Greg notified of resolution (email or Teams message with incident summary)
  • [ ] HIPAA breach determination documented (even if determination is "no breach")
  • [ ] Any required notifications initiated (if breach confirmed — coordinate with legal)
  • [ ] Post-incident review scheduled

Document History

DateChangeAuthor
May 2026Initial draft — full kill chain playbook with per-stage KQL queries, containment commands, Arctic Wolf coordination, HIPAA breach determination, evidence preservation.Kobe

Internal use only — Cirius Group