Hunting is writing the query you can defend at 03:00
Four queries, one custom detection, one suppression record and one handover note. Every query is executed by a real (if small) KQL engine against synthetic tables in your browser — the row counts the validator expects are the row counts the engine gives you, so you can check your own work instead of guessing.
Required
i
KQL-Lite · what is and is not here.
Supported: let, where, project / project--away / project-rename, extend, summarize … by, distinct, order by, take, count, union, join kind=… and the common string, time and aggregate functions. Not modelled: mv-expand, datatable, externaldata, make-series, render visuals, window functions and arg_max as a row picker. Computed group-by columns need an alias (by HOUR = bin(Timestamp, 1h)). A real tenant has hundreds of tables and full engine semantics.
Objectives · 100 pts
What is graded
1
Bounded counting · 10Right table, right window, no take shortcut, honest row count.
2
Cross-domain pivot · 20Identity logon → endpoint process, on the account, not on vibes.
3
AdditionalFields · 15parse_json plus a typed extension so the field is queryable.
4
Aggregation & baselines · 15bin(), dcount() and an ordering that puts the anomaly on top.
5
Custom detection · 20Turn the query into a rule with a limit, an exemption and a safe remediation setting.
6
Alert tuning · 10Suppress the known-benign pattern narrowly, with an owner and an expiry.
7
Handover · 10Save the query, file the hunting note, create the incident from evidence.
Scenario
Why you are hunting
Two Medium alerts on 2026-08-27 mentioned an unusual service account (svc_print) starting an interactive logon, then a process with a network beacon. Neither alert was severe enough to become an incident, and the SIEM owner asked whether this is a pattern or noise. You have 14 days of endpoint data, 30 days of identity data and the email tables.
Simulated "now"
2026-08-28T09:00:00Z
Retention in this lab
Endpoint 14 days · identity 30 days (real tenants differ by plan)
Rows available
—
Who may see the output
SOC and IT operations only; the export is redacted by default
§
Compliance posture.
Console replica, not Microsoft software. Offline, synthetic data, no tenant connection, no telemetry. Nothing you type leaves this browser; the audit log redacts real-looking identifiers and the evidence pack is generated locally.
Reference
Saved queries · tenant workspace
Reuse is a control, not laziness: a query that has been reviewed and named is the one an auditor accepts.
Endpoint
Suspicious process by hash
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName !in ("expected.exe")
| summarize E = count() by DeviceName
Reviewed by · detection engineering · 2026-07-14
Cross-domain
Identity logon → endpoint process
let win = 14d;
IdentityLogonEvents
| where Timestamp > ago(30d) and ActionType == "LogonSuccess"
Incomplete on purpose: it never joins the endpoint side.
Anti-pattern
The query you must not ship
DeviceEvents
| take 100000
| where 1 == 1
Unbounded, always true, and it looks like it worked. This is the shape of a false sense of coverage.
Loading a sample never completes an objective. The validator re-runs your text, so editing a sample into something defensible is the work.
Objectives 1–4 · 60 pts
Four queries, four tabs
Each tab is graded separately and keeps its own text. Run first, then submit — the validator executes the query exactly as it stands.
Required
Task 1 · 10 pts
How many process events in the last 7 days?
Requirements: DeviceProcessEvents as the source, a time filter of 7 days or less, a single row whose Count is the true number for this tenant. No take, no 1 == 1.
not run
Objective 1
Submit the query
Required
Task 2 · 20 pts
Which devices did svc_print touch after a successful logon?
Join IdentityLogonEvents (successful logons, last 30 days) to DeviceProcessEvents on the account name, then return one row per device. Use let for at least one side and join kind=inner.
not run
Objective 2
Interpret, don't just run
Required
Task 3 · 15 pts
Pull the authentication package out of the JSON
On DeviceLogonEvents: bound to 14 days, extend a column AuthPkg from parse_json(AdditionalFields).AuthenticationPackage, keep only NTLM (the downgrade signal), and return the devices. Expected result: exactly 2 rows.
not run
Objective 3
Why extend before you filter
Required
Task 4 · 15 pts
Hourly beaconing baseline
On DeviceNetworkEvents: last 14 days, restrict to the C2 address 203.0.113.64, then summarize per hour with Devices = dcount(DeviceName) and Conns = count(), ordered so the busiest hour comes first. Aliased group-by is required here.
not run
Objective 4
Read your own chart
Required
Objective 5 · 20 pts
Turn a query into a rule without becoming the next noise problem
A custom detection runs on a schedule, creates incidents and can act on devices. Every field below has a blast radius; the validator checks the safety-critical ones.
Required
Create custom detection
Rule definition
Design review
Four things to get right
A window that matches the schedule. A rule that runs hourly and queries three days re-alerts on the same activity for 72 hours. Pair them deliberately.
Limit is a courtesy to your analysts. "Highest count" ordering with a small limit means the worst host surfaces first; unlimited turns one behaviour into a queue-clogging incident storm.
Exemptions are for known-good noise, not for machines you like. Name the device group and put the reason in the alert description so the next shift can read it.
Automatic remediation needs a tested rule. On a first run, choose no automatic action; add isolation only after the false-positive rate is known and the exception list exists.
In the real portal these controls live under Advanced hunting → Custom detections, and the incidents they create carry your query text as evidence — which is why personal data in the query is a policy problem, not a style problem.
Objective 6 · 10 pts
Silencing an alert is a control change, not a preference
The print-monitor process spawns a benign helper on every run. You want it gone from the queue. There is a right way and a very wrong way.
Required
Evidence
What justifies suppression
Alert
Occurrences
Investigated
Outcome
Unusual process (splweb.exe)
41
7 of 41, all identical command line
Informational
Suspicious connection to low-reputation IP
9
1 of 9
Open
Credential stealing behaviour
1
1 of 1
True positive
Sample rate matters: seven reviewed-and-benign out of 41 is a basis for a narrow suppression. One out of nine is not.
Objective 6
Create the suppression
Required
Objective 7 · 10 pts
A hunt that ends in nobody's queue
Save the work where the next shift will find it, and write the note as if the reviewer has never seen this tenant.
Required
Objective 7
Close the loop
Required
Peer review
What a reviewer looks for in a saved hunt
✓ Time window stated and consistent with the schedule
✓ Explicit filters; no 1 == 1, no take as a proxy for a filter
✓ Output columns named, ordered and stable between runs
✓ Exemptions and their reason inside the query text or the description
✕ Any personal identifier, mailbox address or raw log line
✕ A conclusion stronger than the query supports
i
Retention honesty.
Real tenants lose endpoint rows after the retention window of their plan. A query written for 90 days of data will silently return less; a hunting note that does not state the retention assumption is unverifiable later.
Reference
Marking, limits of the engine, and the standing rules
Marking
How the 100 points are awarded
#
Objective
Pts
Validation performed in your browser
1
Bounded count
10
Query parses, targets DeviceProcessEvents, contains a time filter, returns 1 row with the true count; forbidden shortcuts absent; rationale written
2
Cross-domain pivot
20
Uses let and join kind=inner across the identity and endpoint tables, returns exactly the three affected devices, plus two interpretation answers
bin() group-by aliased, dcount present, ordered descending, and the peak-hour answer matches your own result set
5
Custom detection
20
Named rule, severity/category/tactic set, schedule paired with a matching query period, device limit, exemptions, no auto-remediation on a first run, query runs and is free of personal data
6
Alert tuning
10
Suppression placed in alert tuning, narrowly scoped, expiring, owned, with a "must still alert" condition and a written justification
7
Handover
10
Saved query named, incident creation chosen over chat, redacted export scope, review date within 90 days, four hygiene items, 180-character note with no addresses
Standing rules
Seven habits this lab drills
Every query has a window. Unbounded scans are slow, expensive and impossible to reproduce; take is not a filter.
Pivot on a stable identifier. Hashes, account names, report IDs and registry value names survive renaming; file names do not.
Named output beats *. A rule whose projection changes breaks the alert format downstream.
Write the rule you can live with at 03:00. Severity, limit and exemptions are queue design, not decoration.
Suppress narrowly, and with an expiry. Disabling a detection is a control change that needs a change record and an owner.
Correlation is not causation. A join proves co-occurrence; the incident note must not claim more.
Leave the query where the next shift can find it. A hunt nobody saved is a hunt that will be repeated by two people at once.
§
Framework mapping
Queries and rules map to NIST SP 800-61r3 §3.2 (detection & analysis) and §3.3 (containment); detection engineering lifecycle to ISO/IEC 27001:2022 A.8.16 (monitoring activities) and A.5.28; alert-tuning governance to PCI DSS v4.0 requirement 10.4 and 13.3; regulator clocks referenced in the series are NIS2 Art. 23, DORA Art. 19 and GDPR Art. 33. Training aid only — not certification preparation and not affiliated with Microsoft.
Data
Synthetic by construction
Element
This lab
Real portal
Tables
6 hand-built tables, ≤ 14 rows each
Hundreds of tables, tenant-wide
Engine
KQL-Lite, deterministic and offline
Full KQL over the warehouse
Addresses
203.0.113.0/24, 2001:db8::/32
Public internet
Accounts
svc_print, fin.clerk @contoso-rcw.example
Live directory
Detections
Fabricated names, no real IOCs
Microsoft + custom rules
No network call is made from this page. localStorage only, and the reset control wipes the lab completely.
Accessibility
Keyboard and assistive tech
Tabs are real role="tab" buttons; panels are labelled by them and hidden with the hidden attribute, so screen readers never announce a hidden result set.
Editors are plain textareas — spellcheck and autocorrect off, and every error is written into a live region under the editor rather than only coloured red.
Result grids are keyboard-scrollable with column headers as <th scope="col">; row counts are announced in the caption.
Focus is visible at 3:1 contrast, Escape closes overlays, and forced-colors mode keeps the toolbar legible.