Module 15 — Cloud Logging & Detection¶
Type 5 · Detonate & Detect (+ Type 13 · Eval Harness) — take module 14's telemetry, predict which actions the default log captured, and write a Sigma rule for the one worth detecting. (Secondary: Eval Harness — tune against benign noise until it fires on the attack and not the noise, with an explicit false-positive analysis.) Go to the hands-on lab → · Cheat sheet →
Last reviewed: 2026-08
Cloud & Container Security — detection is not a logging problem; it's an attention problem. The log was always there.
In 60 seconds
In both Capital One and LastPass, the log existed the whole time — nobody was watching the stream it landed in. Cloud is comprehensively logged by default, and that abundance breeds a false sense of safety: a detection is a hypothesis scored against a stream that is 99.99% benign, so the hard part is never the true positive — it's making the rule quiet enough that a human still reads its alerts a month from now. You'll take module 14's telemetry, find the data-plane blind spot, write one Sigma rule, and tune it against benign noise. Precision, not recall, is the product — and a rule muted by week two is no coverage at all.
The case¶
In both of the breaches that bookend this track, the log existed the whole time. Capital One's attacker assumed a role and listed every bucket — and it was in CloudTrail; an outsider, not the company, reported the breach. In the LastPass 2022 incidents, an attacker used keys stolen from a senior engineer to reach S3 and a DynamoDB store and walk out with encrypted customer vaults plus configuration — and the activity ran against logged AWS APIs. Neither breach was a logging failure. The telemetry was generated, signed, and retained. Nobody was watching the stream it landed in.
That is the uncomfortable truth this module turns on: cloud is comprehensively logged by default in a way on-prem never was, and that abundance creates a false sense of safety. A detection is a hypothesis about attacker behaviour scored against a stream that is 99.99% benign — and the hard part is never the true positive. It's making the rule quiet enough that a human will still read its alerts a month from now. So before you read on:
Of the techniques you detonated in module 14, which one shows up loud and clear in CloudTrail by default — and which is nearly invisible?
Your job¶
By the end of this module you'll take the telemetry from module 14, predict which attacker actions the default log even captured, then write a Sigma rule for the one worth detecting and tune it against benign activity until it fires on the attack and not on the noise. You'll reproduce the same finding in a native detector's event model (GuardDuty / Defender / SCC) and rule on where native coverage is enough and where you must fill the gap. The deliverable is detection-as-code with an explicit false-positive analysis — the artifact a cloud detection engineer is actually paid for.
Call it before you read on¶
Don't scroll. Commit to these — being wrong is the teaching event, and you'll grade yourself in the lab.
Q1. In module 14 the attacker assumed a role (T1078.004), created an admin user (T1098), and bulk-downloaded objects from S3 (T1530 — the LastPass exfil move). By default, which of those is NOT in CloudTrail at all?
Q2. You write a rule: alert on any
CreateUser. It fires perfectly on the attack. Why is it a bad detection — what happens to it in week two?Q3. A single
GetObjectis benign a thousand times a day. So isAssumeRole. How do you turn "individually-benign events" into a high-fidelity detection without drowning in false positives?
What fires, revealed¶
Hold your answers against these. One picture carries the whole module — every API call lands in CloudTrail, but the two planes it splits into decide what a detector can ever see, and whether the rule that reads them survives to week two:
flowchart LR
API["every AWS API call"] --> CT["CloudTrail records it"]
CT --> MGMT["Management events<br/>AssumeRole · CreateUser · AttachUserPolicy<br/>on by default, free"]
CT -.-> DATA["Data events<br/>s3:GetObject · dynamodb:GetItem<br/>❌ off by default, costly, high-volume"]
MGMT --> DET["detector / Sigma rule"]
DATA -.T1530 bulk exfil — dark by default.-> DET
DET -->|precise: tuned on benign| ALERT["an alert a tired analyst still reads"]
DET -->|noisy: fires on every CreateUser| MUTE["❌ muted by week two = no coverage"]
Q1 — the data plane is dark by default. CloudTrail splits into two planes, and the split is the
single most important gotcha in cloud detection. Management events — AssumeRole, CreateUser,
AttachUserPolicy, PutBucketPolicy, RunInstances — are the control plane in log form, and they are
logged for free, by default. Data events — s3:GetObject, s3:PutObject, dynamodb:GetItem,
lambda:Invoke — are the actual reads and writes against your data, and they are off by default,
cost money, and generate enormous volume, so most orgs log them only on a few sensitive buckets. So the
answer to Q1 is T1530, the bulk download — the exact move that exfiltrated LastPass's vaults is, in
a default account, invisible. The first question in any cloud incident is "were S3 data events enabled
for this bucket?" — and the honest, common answer is no, so we cannot tell which objects left. The
attacker's loudest action (the one that did the damage) is the one your default log is silent on.
The gotcha
"We have GuardDuty enabled" is not "we detect," and "the log exists" is not "we'd see it." Bulk S3 exfil — the move that did the LastPass damage — leaves no record in a default account because data events are off. The first question in any cloud incident is "were S3 data events enabled for this bucket?", and the common, honest answer is no, so we can't tell which objects left.
Q2 — the false-positive economics is the whole craft. A rule that fires on every CreateUser is
correct on the attack and useless in production, because legitimate automation creates users all day.
By week two it's muted, ignored, or routed to a folder no one opens — and a muted rule is a non-existent
rule that feels like coverage. A detection isn't scored on whether it catches the attack; it's scored
on its signal-to-noise on a 99.99%-benign stream. Precision is the product. This is why "we have
GuardDuty enabled" is not the same as "we detect": coverage you don't tune is alert fatigue with a
dashboard.
The mental model
A detection isn't scored on whether it catches the attack — it's scored on its signal-to-noise on a 99.99%-benign stream. Recall is easy and cheap; precision is the craft. The product you ship is not "a rule that fires," it's "a rule a tired analyst still trusts at 3am."
Q3 — sequence and qualifying context turn benign atoms into a signal. The fix for Q2 is to stop
detecting single events and start detecting behaviour. CreateUser alone is noise; CreateUser
followed by AttachUserPolicy attaching AdministratorAccess within five minutes from an IP
that isn't your CI range is almost never legitimate — and that compound is precise. This is Sigma's
job: a YAML detection that names the logsource, the field matches (the interesting fields are nested,
in requestParameters and userIdentity — not top-level), the temporal condition, and — the part
beginners skip — the falsepositives you've reasoned through. Native detectors (GuardDuty, Defender for
Cloud, GCP SCC) ship pre-built versions of exactly this logic over the same control-plane logs; their
strength is one-click coverage of common patterns, their weakness is opacity and lag behind the ATT&CK
Cloud matrix. The practitioner posture is not "native or open" — it's native as the baseline, Sigma
for the gap, and every rule tuned against benign traffic before it's trusted.
The reason Sigma is the gap-filler and not a one-off script is that one rule, written once, compiles to every backend you might run — that's what makes a judgment into version-controlled detection-as-code:
flowchart LR
Y["Sigma rule (YAML)<br/>logsource · detection · condition · falsepositives"] --> C["sigma convert<br/>(sigma-cli)"]
C -->|-t splunk| S["Splunk SPL"]
C -->|-t elastic| E["Elastic / ES-QL"]
C -->|-t loki| L["Grafana Loki"]
S --> Q["one rule → many SIEMs<br/>reviewed in a PR, tuned in git"]
E --> Q
L --> Q
And the "native as baseline, Sigma for the gap" posture is a decision you re-run per technique — never a one-or-the-other choice — and both paths still terminate in the same non-negotiable step:
flowchart TB
T["an ATT&CK Cloud technique"] --> N{"covered by a native<br/>detector finding type?"}
N -->|yes, common pattern| B["GuardDuty / Defender / SCC<br/>baseline — one-click, enriched"]
N -->|"no / lags the matrix"| G["gap"]
G --> SIG["write a Sigma rule<br/>over the same CloudTrail logs"]
B --> TUNE["tune against YOUR benign baseline"]
SIG --> TUNE
TUNE --> TRUST["a rule a tired analyst trusts at 3am"]
The two aren't competitors so much as different points on the same curve — read the contrast as when each earns its place, not which one wins:
| Native detector (GuardDuty / Defender / SCC) | Sigma over CloudTrail | |
|---|---|---|
| Setup | One click; fully managed | You author → convert → deploy |
| Coverage | Common patterns, pre-built | Anything you can express — fills the gap |
| Transparency | Opaque scoring you can't read | Readable YAML you own and review |
| Freshness | Vendor-paced; lags ATT&CK Cloud | You ship the day a technique lands |
| Tune to your baseline | Limited knobs | Full — precision is yours to set |
| Enrichment (geo / ASN / threat-intel) | Built in | You add it |
| Data-plane exfil (T1530) | Blind if S3 data events were off | Blind if S3 data events were off |
The last row is the point: the loudest attacker action is a blind spot for both unless someone turned S3 data events on — no tool choice rescues a log that was never written.
AI caveat
A model drafts a passable Sigma rule in seconds — it knows the syntax and the common field
names. That's the cheap 80%. The owned 20% it can't do: it doesn't know your benign
baseline, so its falsepositives block is generic and its rule is almost always too broad. Run
the draft against the lab's benign events, watch it false-fire, then tighten it — the tuned rule
plus the explicit "what this must NOT fire on" is yours to own, not the model's to guess.
In the lab you'll do exactly this against the module-14 telemetry: confirm the data-plane blind spot, write and tune one Sigma rule, and reproduce the finding in a native detector's model.
Go deeper (~3.5 hrs · optional)¶
The sections above teach the two-plane model, the false-positive economics, and Sigma structure
end-to-end — you can predict the blind spot and write the tuned rule from them alone. These links go to
the primary sources (the CloudTrail plane split is AWS's own doc; the rules are SigmaHQ's real
collection) and deepen the case; they are not the path you must click through to understand the module.
Read the CloudTrail management-vs-data-events doc first. Optional depth is tagged [depth].
The logging surface and its blind spot (~1 hr)
- AWS — CloudTrail concepts: management vs. data events (~30 min) — the primary source. Read "Management events vs. data events"; this is why T1530 is dark by default. The cost/volume model is the reason, not an accident.
- AWS — CloudTrail record contents reference (~20 min, bookmark) — every rule references eventName, eventSource, userIdentity.type, sourceIPAddress, requestParameters. Learn where the interesting fields are nested.
Native detectors as the baseline (~1 hr) - AWS — GuardDuty finding types (~30 min, skim) — read the IAM and S3 sections: which techniques native coverage catches and at what confidence. Note what it doesn't cover. - Microsoft — Defender for Cloud security alerts (~20 min) — the Azure equivalent; note the structural parity (finding type, severity, evidence) so the model transfers across clouds.
Sigma — detection-as-code for the gap (~1.5 hrs)
- Sigma — rule structure & detection logic (~30 min) — read the Detection section and condition/temporal correlation. This is the format your judgment ships in.
- SigmaHQ — CloudTrail rule collection (~1 hr) — read 8–10 real rules. For each, mentally run it against a CloudTrail event: what must match to fire, and what benign thing might also match? Study how the good rules document falsepositives.
The log was there, nobody watched — the case-study seam (~20 min) ([depth] — the case above already makes the point; read to see it in the primary sources)
This module's whole thesis is a track callback: in Cloud Fundamentals, Capital One's role listed every bucket and the calls sat in CloudTrail the entire time — an outsider, not a detection, reported it. The gap you close here is exactly the "logged but not detected" hop from that verdict memo.
- DOJ indictment — United States v. Thompson (Capital One) (~10 min) — the primary source; read it as the API activity was recorded and signed, and still no one was watching the stream. The detective control, not the log, was the missing piece.
- LastPass — Notice of recent security incident (2022) (~10 min) — the second bookend: keys stolen from an engineer reached S3 and DynamoDB over logged APIs, and the data-plane exfil was the hard part to scope — the exact T1530 blind spot this module trains you to name first.
Key concepts¶
- Management events (default, free) vs. data events (off by default, costly) — and that T1530/S3 exfil is invisible without data events on
- A detection is a hypothesis scored on a 99.99%-benign stream; precision, not recall, is the product
- A rule that fires correctly but noisily is muted by week two — a muted rule is no coverage at all
- Sequence + qualifying context (
CreateUser→AttachUserPolicy(AdministratorAccess)in 5 min from a non-CI IP) turns benign atoms into a signal - Sigma structure:
logsource, nested-fielddetection, temporalcondition, and a reasonedfalsepositivesblock - Native detector as baseline, Sigma for the gap — and every rule tuned against benign traffic before trust
AI acceleration¶
Hand a model a plain-English detection ("alert when a role is assumed from an unexpected region, then a
bucket policy is changed") and it will draft a passable Sigma rule in seconds — it knows the syntax and
the common field names. That's the cheap 80%. The expensive, owned 20% is the part the model can't do
for you: it doesn't know your benign baseline, so its falsepositives block is generic and its rule
is almost always too broad. Run its draft against the lab's benign events; watch it false-fire; then
tighten it and write the FP analysis from what you saw. The judgment-as-code here is the tuned rule plus
the explicit "what this must NOT fire on" — that's yours to own, not the model's to guess.
Check yourself
- Which module-14 technique is not in CloudTrail at all by default, and what setting would have captured it?
- A rule that alerts on every
CreateUserfires perfectly on the attack. Why is it a bad detection by week two? - How do you turn individually-benign events (
AssumeRole,GetObject) into a high-fidelity detection?
Comments
Sign in with GitHub to comment. Choose the type: Feedback (errors or suggestions on this page) · Hints (help for fellow learners — no spoilers) · General (anything else).