Domain 4 of 4 · Chapter 1 of 3

Hunt for Threats by Using Microsoft Defender XDR

KQL fundamentals: reading a query as a pipeline

A hunt for a suspicious PowerShell launch looks like this in advanced hunting[1]:

DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName =~ "powershell.exe"
| where ProcessCommandLine has_any ("-enc", "-EncodedCommand", "FromBase64String")
| project Timestamp, DeviceName, AccountName, ProcessCommandLine, InitiatingProcessFileName
| order by Timestamp desc

Read it top to bottom and every line is one transformation of the table above it. Kusto Query Language (KQL) is a read-only query language, so this query searches telemetry and returns rows; it never changes a device. The query starts at a table (DeviceProcessEvents) and flows through operators joined by the pipe character |, each taking the previous step's rows as input and passing its output down, exactly like a Unix pipe. That is the whole mental model: a table goes in the top, each line filters or reshapes it, a result table comes out the bottom.

Filter first, project last

The one performance rule the exam leans on: put your time filter and your most selective where conditions as early as possible, so every operator after them works on fewer rows. Microsoft's query best practices[2] say to apply time filters first and to filter before you summarize or join. Projecting columns (project) belongs near the end, once you know which rows survive.

The operators you must recognize

Operator What it does Note for the exam
where Keeps rows matching a predicate Filter early; chain several where lines
project / project-away Selects / drops columns Narrows the output; do it last
extend Adds a computed column Does not drop existing columns
summarize Aggregates with count(), dcount(), make_set(), grouped by Collapses rows into groups
join Combines two tables on a key Default is innerunique, not inner
union Stacks rows from multiple tables Use when several tables share an entity
order by / sort by Sorts rows Pair with take to cap output
take / limit Returns N arbitrary rows For sampling, not for "top N"; use top for ranked

String matching that trips candidates

KQL string operators are the classic exam distractor. == is case-sensitive equality; =~ is case-insensitive equality, which is why the example uses =~ "powershell.exe". has matches a whole indexed term and is fast; contains matches any substring and is slower because it cannot use the term index. The datatypes and operators reference[3] lists the full set. Prefer has/has_any over contains for performance, and remember that =~ (case-insensitive) is the safe default when matching file names that an attacker can re-case.

DeviceProcessEventstable source (input rows)where Timestamp > ago(7d)filter time firstwhere FileName =~ ...filter selective nextsummarize / joinaggregate or combineproject | order bychoose columns, sort lastResult tablepivot from herefewerrows down
A KQL advanced hunting query reads top to bottom: the table is the input, each pipe step transforms it, filter early so later steps process fewer rows.

The advanced hunting schema: which table owns the entity

The first real decision in any hunt is which table to start from, because Defender XDR splits its telemetry into tables grouped by the product that emits them. There is no single "events" table; you query the table that owns the entity you care about. The schema reference[4] documents every table and column, and the portal's schema pane plus a quick TableName | take 10 let you inspect columns before committing.

The table families

  • Device* (Microsoft Defender for Endpoint): DeviceProcessEvents, DeviceNetworkEvents, DeviceFileEvents, DeviceRegistryEvents, DeviceLogonEvents, DeviceImageLoadEvents, plus DeviceInfo and DeviceNetworkInfo for current device state. These are your process, network, file, and sign-in telemetry from onboarded endpoints.
  • Email* and Url (Microsoft Defender for Office 365): EmailEvents (every message), EmailAttachmentInfo, EmailUrlInfo, EmailPostDeliveryEvents, and UrlClickEvents (Safe Links verdicts when a user clicks). This is how you trace a phishing campaign from delivery to click.
  • Identity* (Microsoft Defender for Identity): IdentityLogonEvents, IdentityDirectoryEvents, IdentityQueryEvents for on-premises Active Directory and hybrid identity activity.
  • Cloud and sign-in: CloudAppEvents (Defender for Cloud Apps), AADSignInEventsBeta and AADSpnSignInEventsBeta for Microsoft Entra ID sign-ins, and AlertEvidence / AlertInfo which expose the alerts themselves so you can hunt over what already fired.

Joining across products is the point

Cross-product hunting is what advanced hunting is for: follow an attacker from a phished mailbox (EmailEvents) to the click (UrlClickEvents) to a process on the device (DeviceProcessEvents) to a sign-in (IdentityLogonEvents). You combine tables with join (on a shared key like AccountUpn or DeviceId) or union (when several tables share the same entity column). One subtlety the exam tests: KQL join defaults to the innerunique flavor, which de-duplicates the left side, so when you need every matching pair you state join kind=inner explicitly. The join operator docs[5] list all the kinds.

Lookback and limits

Advanced hunting queries the last 30 days[1] of data and a single query is bounded by a result and time budget, so an estate-wide hunt over months is a Microsoft Sentinel job (long-term retention and search jobs), not an advanced hunting one. Keep the lookback as tight as the hunt allows; ago(7d) returns faster than ago(30d) and is usually enough to confirm or rule out an indicator.

Advanced huntingone KQL query surfaceDevice* tablesDefender for EndpointEmail* / Url tablesDefender for Office 365Identity* tablesDefender for IdentityCloudApp / AADCloud Apps + Entra IDjoin / union across productsmailbox to click to process to sign-in
Defender XDR tables are grouped by source product; advanced hunting queries them with one KQL surface and joins across products to follow an attack.

Creating custom hunting queries and promoting them

Writing a good hunting query is an iterative loop, then a promotion. You start broad, confirm the results are real and not noisy, then narrow until the query reliably finds the threat with few false positives. The exam frames this as the difference between a one-off interactive hunt and a standing detection.

Save, share, schedule

A query you will reuse is saved as a shared or private query[6] so the team can run it again. To make it run by itself, you promote it to a custom detection rule with Create detection rule directly from the query editor, which is documented in custom detections[7]. That is the hand-off point: this subtopic owns authoring the KQL, and the rule's frequency, alert tuning, and response actions are the detections subtopic.

Required columns for promotion

For the promotion to produce a usable alert, the query must project the columns Defender XDR needs to build one. The rule-creation guidance[7] requires a timestamp column, an entity to map the alert to a real asset, plus a ReportId for non-endpoint tables or a DeviceId for Defender for Endpoint tables so the alert ties back to the originating event. A query that aggregates everything away with summarize and drops these columns cannot become a detection.

// A hunt shaped so it can become a detection:
DeviceLogonEvents
| where Timestamp > ago(1h)
| where ActionType == "LogonFailed"
| summarize FailedAttempts = count(), ReportId = take_any(ReportId)
    by DeviceId, AccountName, bin(Timestamp, 1h)
| where FailedAttempts > 50

Use the built-ins instead of starting blank

Don't hand-write everything. The query editor ships functions (built-in helpers like FileProfile() that enrich a file hash with prevalence and reputation), a library of community and Microsoft sample queries on GitHub[1], and the guided hunting mode where you build a query with dropdowns instead of writing KQL, which lets analysts who don't know the language still hunt. Reach for a sample query or a function before composing a complex join from scratch.

Write KQLadvanced huntingValidate resultsreal + low noise?Save query, rerun by handone-off, interactiveone-offPromote to custom detectionneeds Timestamp + entity + ReportId/DeviceIdstanding
Author a hunt, validate it, then either keep it as a saved one-off or promote it to a custom detection rule, which requires timestamp, entity, and ReportId or DeviceId columns.

Interpreting threat analytics in the Defender portal

Threat analytics answers a different question from advanced hunting. A hunt asks "who touched this indicator"; threat analytics[8] asks "is this active campaign affecting us right now". It is a feed of expert reports written by Microsoft security researchers, each describing a threat actor, campaign, or technique, and then overlaid onto your own tenant's data so the answer is specific to you, not generic malware encyclopedia content. The analyst task on SC-200 is interpretation and response, not authoring intel.

What a report shows you

Each threat analytics report has tabs that turn intel into action against your estate, per the docs[8]:

  • Overview / Analyst report: the narrative, the actor, and the techniques, with MITRE ATT&CK[8] technique references.
  • Impacted assets: the exact devices, users, and mailboxes in your tenant with related activity, so you can scope the blast radius.
  • Related incidents / alerts: the alerts already raised in your queue that map to this threat, linking the campaign to live work.
  • Endpoints exposure and Recommended actions: two tabs that together show your exposure level and the recommended mitigations and secure-configuration settings still missing, ranked so you can close the gaps the campaign exploits. Mitigation status is measured against your applied configuration, so it updates as you remediate.

The dashboard's three lenses

The threat analytics dashboard summarizes the feed three ways to drive triage: Latest threats (most recently published or updated reports), High-impact threats (those with the greatest effect on your organization, by impacted-asset count), and Highest-exposure threats (those where your configuration leaves you most exposed). Use high-impact and high-exposure, not a generic severity label, to decide which campaign to hunt or harden first. The reports also surface hunting queries you can run directly, which is the bridge back to advanced hunting: read the report, then pivot into a KQL hunt for the same indicators.

Microsoft intelcampaigns + actorsYour tenant telemetrydevices, users, mailThreat analytics reportintel overlaid on youImpacted assetsscope the blast radiusRelated alertslink to live workExposure + mitigationsclose the gapsDashboard lensesLatest | High-impact | Highest-exposure
A threat analytics report overlays Microsoft intel on your tenant: impacted assets, related alerts, and exposure with mitigations, surfaced through three dashboard lenses.

Exam-pattern recognition

SC-200 items on this subtopic cluster into a few recognizable stems. Learn the tell and the right answer.

"Filter as early as possible" / fastest query

When a stem shows a query and asks how to make it faster or which line order is correct, the answer is filter first: the time filter (where Timestamp > ago(...)) and selective where conditions go immediately after the table, before summarize, join, or project. A query that filters after aggregating, or projects before filtering, is the wrong choice. Prefer has over contains, per the best practices[2].

Case sensitivity and string operators

If the stem matches a file name or account that an attacker could re-case, the correct operator is the case-insensitive one: =~ for equality, and the has family is term-based. The distractor is == (case-sensitive), which silently misses PowerShell.exe when the query looked for powershell.exe. See the string operators reference[3].

"Run on a schedule and alert" vs "find it once"

A stem describing a recurring need ("alert whenever", "every time this happens", "continuously detect") points to promote the query to a custom detection rule, not to running the hunt repeatedly by hand. A stem describing a single investigation ("find which devices ran", "did anyone receive") points to a plain advanced hunting query. The trap is choosing a custom detection for a one-time lookup, or re-running a manual hunt for something that should be a standing rule.

Required-columns trap

When a stem says a query "cannot be turned into a custom detection" or "the alert has no asset", the cause is missing required columns: no timestamp, no entity, or no ReportId (non-endpoint tables) / DeviceId (Defender for Endpoint tables). The fix is to project those columns, not to change the frequency. This is documented in custom detection rules[7].

Threat analytics vs advanced hunting

When the question is "are we affected by campaign X" or "which mitigations reduce our exposure to this actor", the answer is threat analytics, because it overlays Microsoft intel onto your tenant. When the question is "search our data for this hash/IP/command line", the answer is advanced hunting. The trap is reaching for advanced hunting to assess a named campaign's impact, or expecting threat analytics to run an arbitrary KQL search.

Retention boundary: Defender XDR vs Sentinel

A stem mentioning data older than ~30 days, long-term retention, archived logs, search jobs, or MITRE ATT&CK coverage analysis is pointing at Microsoft Sentinel, not Defender XDR advanced hunting. Advanced hunting's window is about 30 days; anything beyond it, or the Sentinel-specific hunting features, belongs to the Sentinel hunting subtopic.

What does the stem ask?match the verbAdvanced huntingsearch our data oncefind / searchThreat analyticsaffected by campaign?campaignCustom detectionrecurring alertalert alwaysMicrosoft Sentinel> 30 daysold dataFilter early, use =~ / has, project required columns before promoting
Match the stem's verb to the tool: search once is advanced hunting, campaign impact is threat analytics, recurring alert is a custom detection, old data is Sentinel.

Three hunting jobs in Microsoft Defender XDR

JobAdvanced hunting (KQL)Threat analyticsPromote to custom detection
What you doWrite a KQL query over raw event tablesRead a curated report mapped to your tenantSave a validated query as a scheduled rule
Question it answersWho/what touched this indicator?Are we affected by this campaign now?How do I detect this automatically from now on?
Data it uses~30 days of Device/Email/Identity/CloudApp tablesMicrosoft intel overlaid on your telemetryThe same query, re-run on a frequency
OutputAn interactive result table to pivot fromImpacted assets, related alerts, exposure gapsAlerts plus optional auto-response actions
Read vs actRead-only; you pivot and decideRead intel, then act on exposureActs: alerts and can isolate/disable/delete

Decision tree

Data older than 30 days?Defender XDR lookback limitMicrosoft Sentinelretention, search jobsYesAssessing a named campaign?are we affected right nowNoThreat analyticsimpacted assets + exposureYesNeed a recurring alert?run on a scheduleNoAdvanced huntingwrite KQL, search onceNo, one-offSingle table, no joins?NRT eligibilityYesContinuous (NRT)near real-time ruleYesScheduled tier1h / 3h / 12h / 24hNo

Sharp facts the exam loves — give these one last read before exam day.

Cheat sheet

Sharp facts the exam loves — scan these before test day.

Advanced hunting is a read-only KQL search over ~30 days of raw Defender XDR telemetry

Advanced hunting in the Microsoft Defender portal lets you query the raw event tables Defender XDR collects from endpoints, identities, email, apps, and cloud, going back about 30 days, using Kusto Query Language (KQL). It is proactive: you run a query and pivot from the result table rather than waiting for an alert. The query is read-only against telemetry, so a hunt never changes a device or mailbox; any containment is a separate response action. When you need data older than ~30 days, that is a Microsoft Sentinel job, not advanced hunting.

Read a KQL query as a pipeline: a table in at the top, each pipe transforms the rows below

Every advanced hunting query starts at a table name and flows left to right through operators joined by the pipe character, each one taking the previous step's rows as input, just like a Unix pipe. Read it top to bottom and each line is one transformation of the table above it. The core operators are where (filter), project and extend (choose or compute columns), summarize (aggregate), join and union (combine tables), and order/take (sort and limit). Internalizing the pipeline shape is what lets you compose and debug a hunt rather than memorize snippets.

1 question tests this
Filter first: put the time window and selective where conditions right after the table

The performance rule SC-200 leans on is filter early. Place your time filter (where Timestamp > ago(...)) and your most selective where conditions immediately after the table, before summarize, join, or project, so every later operator works on fewer rows. Microsoft's query best practices say to filter before aggregating or joining and to project the columns you want last. A query that aggregates or projects before filtering is the slow, wrong answer on the exam.

Trap Filtering after a summarize or join; the aggregation already processed the full row set, so the late where saves nothing and the query stays slow.

1 question tests this
Match file names with =~ (case-insensitive), not == (case-sensitive)

In KQL == is case-sensitive equality and =~ is case-insensitive equality. When you match a file name or account that an attacker can re-case, use =~ so a query for powershell.exe still catches PowerShell.exe. The classic distractor is == , which silently misses the re-cased value and returns nothing. The same idea extends to string search: prefer the case-insensitive, term-based operators when the value's casing is not guaranteed.

Trap Using == to match a file name; it is case-sensitive, so a re-cased value like PowerShell.exe slips past a query written for powershell.exe.

Prefer has over contains for substring-style matching

KQL has matches a whole indexed term and is fast because it uses the term index; contains matches any substring and is slower because it cannot. For hunting on command lines, file names, or URLs, reach for has or has_any first and fall back to contains only when you genuinely need a partial-token match. On a large estate the operator choice is the difference between a query that returns and one that times out.

Trap Defaulting to contains for every text match; it skips the term index and runs far slower than has on large tables.

1 question tests this
Defender XDR splits telemetry into product-named tables; start at the one that owns the entity

There is no single events table. Defender XDR groups telemetry by emitting product: Device* tables (DeviceProcessEvents, DeviceNetworkEvents, DeviceFileEvents, DeviceLogonEvents) from Defender for Endpoint, Email* and Url tables (EmailEvents, EmailAttachmentInfo, EmailUrlInfo, UrlClickEvents) from Defender for Office 365, Identity* tables (IdentityLogonEvents, IdentityDirectoryEvents) from Defender for Identity, and CloudAppEvents plus AAD sign-in tables from Defender for Cloud Apps and Microsoft Entra ID. The first decision in any hunt is which table owns the entity you care about, because its columns and data depend on it.

Trap Querying DeviceLogonEvents for a directory or identity sign-in; Device* tables come from Defender for Endpoint, while domain logon and directory activity live in the Identity* tables from Defender for Identity.

3 questions test this
Inspect a table's columns with TableName | take 10 or the schema pane before writing the hunt

You do not have to know every column from memory. The portal's schema reference lists each table and column, and running TableName | take 10 returns a quick sample so you can see the real column names and shapes before you commit to a query. take returns arbitrary rows for sampling, not a ranked top-N; use top when you want the highest values. Inspecting first prevents the dead-end of joining or projecting a column that does not exist on that table.

Trap Treating take 10 as a top-10 ranking; take returns arbitrary rows, so for the highest counts or latest events you need top or order by.

Cross-product hunting is the point: join or union Device, Email, and Identity tables

Advanced hunting exists to follow an attacker across products that a single-product view cannot show: from a phished mailbox (EmailEvents) to the click (UrlClickEvents) to a process on a device (DeviceProcessEvents) to a sign-in (IdentityLogonEvents). You stitch the story with join on a shared key like AccountUpn or DeviceId, or union when several tables share the same entity column. This cross-domain query surface is what distinguishes advanced hunting from running each product's own search.

KQL join defaults to innerunique, so state kind=inner when you need every match

Unlike SQL, KQL join defaults to the innerunique flavor, which de-duplicates rows on the left table before matching. If a hunt needs every left-right pair (for example, all logons that match all of several suspicious devices), an unqualified join can silently drop rows. State join kind=inner explicitly when you want full inner-join semantics, and pick the kind (leftouter, rightsemi, anti, and so on) deliberately rather than relying on the default.

Trap Assuming an unqualified join behaves like a SQL inner join; KQL defaults to innerunique and de-duplicates the left side, so it can quietly drop matching rows.

3 questions test this
Promote a validated hunt to a custom detection rather than re-running it by hand

The lifecycle is hunt, then operationalize: refine a KQL query interactively until it reliably finds the threat with low noise, then choose Create detection rule to save it as a custom detection that re-runs on a schedule and alerts. A bare advanced hunting query is a one-time manual lookup, so a recurring need (alert whenever, continuously detect) means promote to a rule. Authoring the KQL is the hunting task; the rule's frequency, tuning, and response actions are the detections topic.

Trap Re-running an advanced hunting query by hand on a cadence for something that should be a standing custom detection rule; promotion is what makes it alert automatically.

A query can only become a detection if it projects the columns the alert needs

For a query to promote into a usable custom detection it must return the columns Defender XDR uses to build the alert: a timestamp, an entity that maps the alert to a real asset, plus ReportId for non-endpoint tables or DeviceId for Defender for Endpoint tables so the alert ties back to the originating event. A hunt that summarizes everything away and drops these columns cannot map its alerts to an asset; project them (use take_any or arg_max to carry ReportId through an aggregation) so the alert can build.

Trap Omitting ReportId on a non-endpoint table; without it Defender XDR cannot identify the originating event, so the alert is not tied back to its source even though the rule itself still creates and runs.

1 question tests this
Use built-in functions, sample queries, and guided mode instead of starting blank

Advanced hunting ships helpers so you do not compose every hunt from scratch. Functions like FileProfile() enrich a file hash with prevalence and reputation in one call, Microsoft and the community publish a library of sample queries on GitHub, and guided hunting lets an analyst build a query with dropdowns instead of writing KQL. Reaching for a sample query or a function before hand-rolling a complex join is faster and less error-prone, and guided mode keeps non-KQL analysts able to hunt.

Threat analytics answers "are we affected by this campaign", not "what is this malware"

Threat analytics is a feed of expert reports written by Microsoft security researchers that describe active campaigns, actors, and techniques, then overlay them onto your tenant's own data. So it tells you whether a named campaign is affecting you right now, with your impacted assets and your exposure, rather than giving generic malware background. The SC-200 task here is interpretation and response: read the report and act, not authoring intelligence. When a question asks which mitigations reduce exposure to a specific actor, the answer is threat analytics, not a KQL hunt.

Trap Reaching for advanced hunting to judge whether a named campaign affects you; threat analytics already overlays Microsoft intel on your tenant and shows impacted assets and exposure.

A threat analytics report shows impacted assets, related alerts, and exposure with mitigations

Each threat analytics report turns intel into action through its tabs: an Overview and analyst report with the narrative and MITRE ATT&CK techniques, an Impacted assets view of the exact devices, users, and mailboxes with related activity, Related incidents and alerts already in your queue for that threat, and an Endpoints exposure plus Recommended actions view ranking the missing secure-configuration settings the campaign exploits. Mitigation status updates as you remediate, so the report doubles as a checklist for closing the gaps that leave you exposed.

3 questions test this
Triage by the dashboard's High-impact and Highest-exposure lenses, not a generic severity

The threat analytics dashboard summarizes the feed three ways: Latest threats (most recently published or updated), High-impact threats (greatest effect on your organization by active and resolved alert count), and Highest-exposure threats (where your configuration leaves you most exposed). Use High-impact and Highest-exposure to decide which campaign to hunt or harden first, because they are scored against your own telemetry and configuration rather than a one-size severity label. Reports also surface ready hunting queries that pivot you straight into advanced hunting.

Trap Prioritizing by a report's generic severity rating; that ignores your tenant, whereas High-impact and Highest-exposure are scored against your own affected assets and configuration gaps.

Advanced hunting tops out near 30 days; long retention and search jobs are Sentinel

Advanced hunting queries about the last 30 days of Defender XDR data, so an investigation reaching months back belongs in Microsoft Sentinel, where long-term retention, archived log restore, and search jobs live. Keep each hunt's lookback as tight as it allows (ago(7d) returns faster than ago(30d) and is usually enough to confirm or rule out an indicator). A stem mentioning data older than 30 days, archived logs, or search jobs is pointing at Sentinel, not Defender XDR advanced hunting.

Trap Trying to hunt months-old data in Defender XDR advanced hunting; its window is about 30 days, so older investigations need Microsoft Sentinel retention and search jobs.

KQL hunting is read-only; remediation is a separate response action

A KQL query surfaces evidence but never changes anything: it cannot isolate a device, disable a user, or delete an email. Those containment actions are taken from the incident, device, or user page in the response workflow, or are wired into a custom detection rule's automatic actions, not into the hunt itself. Knowing the read-versus-act split keeps you from expecting a hunting query to remediate and points you to the incident-response surface when the question asks how to contain.

Network indicators (IP, URL, domain) do not support the BlockAndRemediate action

For network indicators in Microsoft Defender for Endpoint, only Allow, Audit, Block, and Warn actions are supported. A CSV import in which network indicators use BlockAndRemediate fails to import those entries; use Block instead.

Trap BlockAndRemediate is valid only for file indicators, not for IP/URL/domain network indicators.

4 questions test this
For URL/domain/IP indicators, conflicting actions resolve Allow > Warn > Block

When several Defender for Endpoint URL, domain, or IP indicators target the same value, the precedence order is Allow over Warn over Block, so an Allow indicator overrides both Warn and Block.

Trap This precedence differs from file-hash indicator conflict handling, where the more secure/longer hash (SHA-256 over MD5) wins instead.

5 questions test this

References

  1. https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-overview
  2. https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-best-practices
  3. https://learn.microsoft.com/en-us/kusto/query/datatypes-string-operators
  4. https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-schema-tables
  5. https://learn.microsoft.com/en-us/kusto/query/join-operator
  6. https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-save-query
  7. https://learn.microsoft.com/en-us/defender-xdr/custom-detection-rules
  8. https://learn.microsoft.com/en-us/defender-xdr/threat-analytics