RSA Book · Success Kit

Getting Started with AI in SecOps
Without Spending Money

Three hands-on phases that take you from pasting a raw log into ChatGPT to building your own AI agent — built on real attack simulations, not toy examples.

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"
}
📋 Use AI Chatbot – Explain This Alert

Copy the log event below, then paste it into ChatGPT with this prompt. You should get a plain-English explanation of what happened, why it's suspicious, and what to do next.

Prompt Template
You are a senior SOC analyst. I'm going to paste a raw Windows Sysmon log event. Please:
1. Explain in plain English what this event shows
2. Tell me why this is or isn't suspicious
3. If suspicious, what MITRE ATT&CK technique does this map to?
4. What should I do next as an analyst?

Here is the log event:
[PASTE LOG EVENT HERE]
Tip: Try this with the bitsadmin.exe event from Step 1 of the kill chain. Notice how the AI immediately spots the external URL, the Temp directory drop path, and maps it to T1197.

⚙️

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()

# ── 1. Triage output schema ────────────────────────────────────────────
# Force structured output so downstream
# ticket creation never breaks on a missing field.
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?")

# ── 2. Triage system prompt ──────────────────────────────────
SYSTEM_PROMPT = """
You are an automated SOC triage assistant.
An alert has fired. Analyze the raw event data and classify it.

Rules:
- severity=critical   → active intrusion, data exfil, ransomware
- severity=high       → confirmed malicious tool, lateral movement
- severity=medium     → suspicious but explainable, warrants investigation
- severity=low        → likely benign, log for trend analysis
- escalate=true only if a human should act within 30 minutes
- next_steps must be specific and actionable, not generic advice
"""

# ── 3. Webhook endpoint ──────────────────────────────
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,  # same pattern as Phase 3 hunt agent
    )
    return TriageResult.model_validate_json(resp.choices[0].message.content)

# ── 4. Run ─────────────────────────────
# uvicorn triage_handler:app --host 0.0.0.0 --port 8000
# Then point your SIEM webhook at: http://your-server:8000/triage
#
# N8N users: add an HTTP Request node pointing at this endpoint.
# The JSON response maps directly to ticket fields.
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
Tip: Use a cheap model (Haiku, GPT-4o-mini, Gemini Flash) for triage — it runs on every alert and cost adds up. Reserve Opus/GPT-4o for the Phase 3 investigation agent where capability matters. The confidence field lets you auto-close low-confidence low-severity tickets instead of flooding the queue.

🕵️

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.
Claude's Reasoning
Let me start by understanding the scope of the data - what time range, what hosts, what event types we're dealing with.
SELECT MIN("@timestamp") as earliest, MAX("@timestamp") as latest, COUNT(*) as total_events, COUNT(DISTINCT "host") as hosts FROM logs
[ { "earliest": "", "latest": "2026-03-10T00:20:13.208277+00:00", "total_events": 2451, "hosts": 1 } ]
2
+60 pts · 6 flags found
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.
Claude's Reasoning
Let me get a better understanding of the time range and the distinct event IDs present in the data.
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
[ { "EventID": "7", "cnt": 553, "earliest": "2026-03-09T22:20:06.756277+00:00", "latest": "2026-03-10T00:20:08.877277+00:00" }, { "EventID": "10", "cnt": 512, "earliest": "2026-03-09T22:20:03.482277+00:00", "latest": "2026-03-10T00:20:13.113277+00:00"
6
+90 pts · 15 flags found
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.
Claude's Reasoning
Let me now investigate the most suspicious events. First, let me look at log clearing events (1102 and 104), then suspicious process creation. I'll run multiple queries in parallel.
SELECT "@timestamp", "EventID", "SourceName", "Channel", "SubjectUserName", "SubjectDomainName", "Message" FROM logs WHERE "EventID" IN ('1102', '104') ORDER BY "@timestamp"
[ { "@timestamp": "", "EventID": "1102", "SourceName": "Microsoft-Windows-Eventlog", "Channel": "Security", "SubjectUserName": "", "SubjectDomainName": "", "Message": "The audit log was cleared.\r\nSubject:\r\n\tSecurity ID:\tS-1-5-21-3940915590-64593676-1414006259-500\r\n\tAccount Name:\twardog\r\n\tDomain Name:\tWORKSTATION5\r\n\tLogon ID:\t0xC61D9" }, { "@timestamp": "2026-03-09T22:20:03.888277+00:00",
8
+30 pts · 18 flags found
Now Claude goes hunting for the delivery mechanism. It searches for suspicious command-line patterns across all process creation events. Notice the SQL: it covers multiple attacker techniques in one pass.
Claude's Reasoning
Good, I have all the log clearing events. Now let me investigate suspicious process creation events. I'll look for suspicious command lines in Sysmon EventID 1 (process creation).
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
[ { "@timestamp": "2026-03-09T22:20:06.764277+00:00", "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\" ", "User": "CORP\\SYSTEM" } ]
10
+20 pts · 20 flags found
The credential theft is exposed. Outflank-Dumpert.exe opened lsass.exe with PROCESS_VM_READ access (0x1fffff) — the signature of a memory dump. Claude recognises the tool name and access mask immediately.
Claude's Reasoning
Found Outflank-Dumpert.exe - a known LSASS memory dumper tool used for credential theft. It has an empty timestamp though. Let me look at process access events (EventID 10) targeting lsass.exe and also look for other suspicious process creation events. Let me also check for PowerShell script block logging and other suspicious activities.
SELECT "@timestamp", "SourceImage", "TargetImage", "GrantedAccess", "CallTrace" FROM logs WHERE "EventID" = '10' AND "TargetImage" LIKE '%lsass%' ORDER BY "@timestamp" LIMIT 10
[ { "@timestamp": "", "SourceImage": "C:\\Users\\wardog\\Desktop\\Outflank-Dumpert.exe", "TargetImage": "C:\\windows\\system32\\lsass.exe", "GrantedAccess": "0x1fffff", "CallTrace": "C:\\windows\\SYSTEM32\\ntdll.dll+9c584|C:\\windows\\System32\\KERNEL32.DLL+17c24|C:\\windows\\SYSTEM32\\ntdll.dll+6cea1" } ]
16
+20 pts · 25 flags found
The C2 channel surfaces. By filtering network connections to exclude known-good IPs, Claude isolates the outbound beacon to the attacker's server. 151.101.208.133 is a GitHub CDN IP — consistent with the bitsadmin download.
Claude's Reasoning
Let me check all network connections and look for suspicious external connections. I also want to look at DNS queries (EventID 22), file creation events (EventID 11), and PowerShell script block logging. Let me also check for scheduled tasks and service installs.
SELECT "@timestamp", "UtcTime", "Image", "DestinationIp", "DestinationPort" FROM logs WHERE "EventID" = '3' AND "DestinationIp" NOT IN ('127.0.0.1', '0:0:0:0:0:0:0:1', '239.255.255.250', '-') AND "DestinationIp" != '' ORDER BY "@timestamp" LIMIT 10
[ { "@timestamp": "2026-03-09T22:20:10.055277+00:00", "UtcTime": "2020-10-23 06:36:42.882", "Image": "C:\\windows\\System32\\svchost.exe", "DestinationIp": "151.101.208.133", "DestinationPort": "443" } ]
22
+10 pts · 42 flags found
Seatbelt.exe pinpointed to a specific host and timestamp. Claude has now reconstructed three of the four kill chain stages purely from SQL queries against the log database.
Claude's Reasoning
Now let me get the timestamps for Seatbelt.exe execution events, which is a known post-exploitation reconnaissance tool.
SELECT "@timestamp", "UtcTime", "Image", "CommandLine", "ParentImage", "ParentCommandLine", "User" FROM logs WHERE "EventID" = '1' AND "Image" LIKE '%Seatbelt%' ORDER BY "@timestamp"
[ { "@timestamp": "2026-03-10T00:20:06.929277+00:00", "UtcTime": "2020-11-02 21:39:11.671", "Image": "C:\\Users\\wardog\\Desktop\\Seatbelt.exe", "CommandLine": "Seatbelt.exe -group=user", "ParentImage": "C:\\Windows\\System32\\cmd.exe", "ParentCommandLine": "\"C:\\windows\\system32\\cmd.exe\" ", "User": "CORP\\SYSTEM" } ]
25
+50 pts · 50 flags found
With the kill chain reconstructed, Claude pivots to flag submission. This is the transition from investigation to response — identifying the exact malicious event timestamps for downstream automation.
Claude's Reasoning
The logon events seem like normal SYSTEM logon type 5 (service). Let me now look at the process access events to lsass that were NOT from Dumpert - looking for other credential dumping tools. Also let me check for WMI events, pipe creation, and other suspicious registry modifications. Let me also look at file deletion events (EventID 23) and credential manager events (5379).
SELECT "@timestamp", "EventID", "UtcTime", "TargetFilename", "Image" FROM logs WHERE "EventID" = '23' ORDER BY "@timestamp"
[ { "@timestamp": "2026-03-09T22:20:07.509277+00:00", "EventID": "23", "UtcTime": "2020-10-23 06:36:44.280", "TargetFilename": "C:\\Users\\wardog\\AppData\\Local\\Temp\\BIT9584.tmp", "Image": "C:\\windows\\System32\\svchost.exe" } ]
📋 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

# ── 1. Structured output schema ───────────────────────────────────────
# Forcing JSON here does two things:
#   a) prevents hallucinated tool names
#   b) gives you a reasoning trace for free (the 'reasoning' field)
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'"
    )

# ── 2. System prompt ──────────────────────────────────────────────
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.
"""

# ── 3. ReAct loop ─────────────────────────────────────────────

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})

        # Think: model returns structured JSON
        resp = completion(
            model=model,
            messages=history,
            response_format=HunterAction,  # enforces schema
        )
        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]}...")

        # Act
        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
Tip: The magic is response_format=HunterAction. LiteLLM translates this Pydantic model into the correct structured-output format for every provider — OpenAI, Claude, Gemini, Bedrock — so the same loop works everywhere. Swap model= to benchmark different providers against the same hunt.