Microsoft 365 Triage
Suspect a hacked mailbox or account? Investigate it right now, from your browser. Sign-in history, audit trail, inbox-rule and OAuth abuse - analyzed in seconds - and when it is real, walk the first-60-minutes containment runbook on the same account.
First time in your tenant? An administrator must
grant consent once. Signing in only ever requests
read-only permissions; the permissions that change something are requested separately,
the moment you arm containment.
New here? Read the help & data overview or the
security model.
Already ran a full extraction? View the output here - no sign-in needed.
What do you need right now?
Triage answers "is this account actually compromised?". Containment stops the attacker. In a live incident you normally run triage first and then contain the same account - the containment screen picks up the account and the findings from your search, so you do not type the UPN twice.
Who do we investigate?
Start typing a name or UPN. Pick the account you suspect is compromised.
Collecting evidence…
Everything is fetched straight from Microsoft Graph into this browser tab.
Findings by severity
Findings
| Severity | Finding | Time (UTC) |
|---|
Run a full extraction locally
This browser tool answers the fast "is this account compromised?" question. When you need a complete forensic acquisition - every mailbox rule, message trace, mailbox audit log and the full retention window - run the open-source Microsoft-Extractor-Suite by Invictus IR on your own machine in PowerShell. It writes CSV/JSON evidence you can then load back into this tool to read.
See nothing here? The mapping of what this Triage tool covers versus the full Suite lives in the repo's COVERAGE.md.
1 · Requirements
Windows PowerShell 5.1 or PowerShell 7 on any OS. You need a Microsoft 365 account with the Security Reader/Global Reader role (and a mailbox-enabled admin for the Exchange-only sources). Install the Microsoft modules the Suite depends on, then the Suite itself:
# Run once, in an elevated PowerShell prompt
Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser
Install-Module -Name Microsoft.Graph -Scope CurrentUser
Install-Module -Name Az -Scope CurrentUser # only for Azure activity logs
# The suite itself (PowerShell Gallery)
Install-Module -Name Microsoft-Extractor-Suite -Scope CurrentUser
Or skip the installs entirely and use the ready-made container - PowerShell 7
with ExchangeOnlineManagement, Microsoft.Graph and the Extractor-Suite preinstalled. It prints a
cheat sheet on start, and everything written to /evidence lands in
./evidence on your machine:
docker run --rm -it -v "$PWD/evidence:/evidence" ghcr.io/nurejev/triage-pwsh:latest
# no browser in a container - sign in with a device code:
Connect-ExchangeOnline -Device
Connect-MgGraph -UseDeviceCode -Scopes "AuditLog.Read.All","Directory.Read.All"
2 · Import & connect
Load the module, then sign in to the services you need. Exchange Online covers the Unified Audit Log, mailbox rules and message traces; Microsoft Graph covers sign-in logs, OAuth grants, MFA and risky users.
Import-Module Microsoft-Extractor-Suite
# Exchange Online (UAL, mailbox & transport rules, message trace, mailbox audit)
Connect-M365 # wraps Connect-ExchangeOnline
# Microsoft Graph (sign-in logs, OAuth apps, MFA, risky users/detections)
Connect-MgGraph -Scopes "AuditLog.Read.All","Directory.Read.All"
# Optional - Azure resource activity logs
Connect-AzureAZ
3 · Collect the evidence
Collect everything, or target the sources this Triage tool understands on import. Narrow the
window with -StartDate/-EndDate (UAL retention is 180 days on
Standard, 365 on Premium auditing).
# --- one user, the two sources this tool reads back in ---
Get-UAL -UserIds jan@contoso.com -StartDate 2026-04-01 -EndDate 2026-07-01
# -> .\Output\UnifiedAuditLog\UAL-*.csv
Get-GraphEntraSignInLogs -UserIds jan@contoso.com
# -> .\Output\EntraID\SignInLogsGraph.json
# --- other high-value sources for the full picture ---
Get-MailboxRules # live inbox-rule inventory (all mailboxes)
Get-TransportRules # tenant-wide transport rules
Get-OAuthPermissionsGraph # registered OAuth apps + granted scopes
Get-AdminAuditLog # admin cmdlet history
Get-MailboxAuditLog -UserIds jan@contoso.com
Get-MessageTraceLog -UserIds jan@contoso.com
Get-MFA ; Get-RiskyUsers ; Get-RiskyDetections
# --- or collect (almost) everything in one go ---
Get-AllEvidence # automated collection across sources
Start-MESTriage # quick per-user triage template
Every function writes to an Output\ folder in your current
directory, one subfolder per source. UAL defaults to CSV, sign-in logs to JSON - both are
what the importer below expects. Nothing is sent anywhere; it is your local evidence archive.
4 · View the output here
Bring the UAL-*.csv and SignInLogsGraph.json files back into this
tool to get the same severity-ranked findings report - parsed entirely in your browser.
Microsoft-Extractor-Suite is © Invictus Incident Response (GPL-2.0). This tool shares no code with it - it reads the files the Suite produces.
View extraction output
Drop the CSV/JSON files the Microsoft-Extractor-Suite produced - or a Triage Evidence JSON you exported earlier - and read them as a findings report. Everything is parsed in this browser tab; nothing is uploaded.
Drop files here or
Recognized: Unified Audit Log (Get-UAL, CSV/JSON),
Entra sign-in logs (Get-GraphEntraSignInLogs, JSON), OAuth grants, and a
Triage Evidence JSON. Select several at once to combine them.
Help
How to use the tool
- Sign in with Microsoft. Use an account with at least the Security Reader or Global Reader role. The very first time in a tenant, an administrator must grant consent once (the link on the start page opens the consent screen). The permissions requested at sign-in are read-only - signing in cannot change anything in your tenant.
- Pick triage or containment. Triage investigates and stays read-only. Containment walks the first-60-minutes runbook and does make changes - it asks for its write permissions separately, at the moment you arm it.
- Search the user. Type a name or UPN and pick the account you suspect is compromised. Both triage and containment use this same search.
- Choose the window. 24 hours, 7, 30 or 90 days back. Use 24 hours for a fast "what just happened" check during an active incident. Leave "Unified Audit Log" enabled for the most complete picture - it uses an asynchronous Microsoft query that can take a few minutes on busy mailboxes.
- Read the report. Findings are ranked by severity; click one for details and the recommended action. Export everything as CSV/JSON for your ticket or forensic archive.
Just looking around? The demo on the start page runs a staged BEC incident without signing in.
What data is pulled (and why)
Everything is fetched from Microsoft Graph straight into this browser tab. Nothing is sent to Limon-IT or any other server, and nothing is stored after you close the tab.
| Source | Graph API | What we look for |
|---|---|---|
| User profile | /users/{upn} | Account state, creation date, password age. |
| Sign-in logs | /auditLogs/signIns | Unexpected countries, brute force followed by a success, impossible travel, legacy authentication (bypasses MFA), Conditional Access outcome, risk level per sign-in. |
| Directory audits | /auditLogs/directoryAudits | Directory changes initiated by the account. |
| Identity Protection | /identityProtection/riskyUsers, /riskDetections | Microsoft's own risk verdicts: anonymous IPs, unfamiliar sign-in properties, leaked credentials. Needs Entra ID P2. |
| MFA methods | /users/{id}/authentication/methods | Whether strong methods are registered - and whether the user recognizes each one (attackers register their own authenticator). |
| OAuth grants | /users/{id}/oauth2PermissionGrants | Apps with access to the account, scored against risky scopes (mail read/send, mailbox settings, directory write). Malicious app consents survive password resets. |
| Unified Audit Log | /security/auditLog/queries | Inbox rules created or changed, mailbox-level forwarding, OAuth consents, admin role changes, mailbox searches ("invoice", "IBAN"...), mass file downloads, eDiscovery. |
What the report shows
- Critical - strong indicators of active compromise (forwarding to an external mailbox, password attack followed by a successful sign-in, MFA disabled). Act immediately.
- High - serious signals that usually accompany a real incident. Investigate the same day.
- Medium / Low - worth reviewing; often explainable but attackers hide in "explainable".
- Info - context, no action implied.
Every finding includes a recommended action. A step shown as skipped during collection means that source was not available in your tenant (licensing, permissions or auditing not enabled) - the report simply covers what could be read.
Findings are indicators, not verdicts - verify before acting. Suspect a real breach? Preserve the evidence exports and contact Limon-IT for incident response support.
Containment - the first 60 minutes
The second option after sign-in. Where triage answers "is this account compromised?", containment is the runbook for the hour after the answer is yes. It opens on the account you searched for, carries the triage findings across, and works through the steps in the order that does not leak:
- Preserve the evidence first. Remediation destroys artefacts - export the triage evidence JSON before changing anything.
- Revoke sign-in sessions. Refresh tokens are invalidated. Most compromises are token theft, so this comes before the password reset - not after.
- Disable the account (never delete - deletion destroys the audit trail).
- Reset the password to a throwaway generated in your browser and shown once.
- Strip attacker-added authentication methods - the step most people skip, and the reason accounts come back eight hours after a "successful" reset.
- Inbox rules, forwarding and delegates - loaded inline with suspicious rules flagged, and disable/delete/clear buttons, when the optional Exchange containment backend is deployed. Without it you get copy-ready Exchange Online PowerShell instead, because Microsoft does not let a browser change another user's mailbox settings.
- Revoke OAuth consent grants - the persistence that survives both the password reset and the session revocation.
After the steps come the fifteen blast-radius checks. Thirteen of them answer themselves - from Graph, from the Unified Audit Log, or from the Exchange backend - and tell you what they found rather than asking you to go and look: which countries produced a successful sign-in, whether a stolen token is still being renewed non-interactively, whether mailbox reads were individual messages or a whole folder synced down, the busiest download hour, whether credentials were added to a service principal. Run all checks sweeps the lot and logs which ones it flagged. Each result exports as JSON, and exporting one ticks the evidence item it satisfies. The two Power Platform checks cannot be automated - the admin API is not reachable with the permissions this tool holds - so they say so and hand you the PowerShell.
Then the evidence-preservation list, where each item says which check produces it and ticks itself when you export that check, so the list reflects the files you actually have rather than what somebody remembered to tick. Two items - the Defender XDR incident export and the communication log - are marked as yours to collect by hand.
Finally the communication pattern: never notify the user on the channel you suspect is compromised. Each audience downloads as a markdown template prefilled with what this session already knows - the twenty-second call script (including what not to say), the manager brief, the one-paragraph CISO status, a DPO note with the personal-data exposure table filled in from the checks, the leadership brief and a communication log - with blanks left where judgement is required. Everything you run, tick or download is written to a timestamped action log that exports as CSV or as a Markdown handover report for the ticket.
Containment needs delegated write permissions
(User.RevokeSessions.All, User.ReadWrite.All,
UserAuthenticationMethod.ReadWrite.All, DelegatedPermissionGrant.ReadWrite.All)
and an Entra role that can use them - typically some combination of User Administrator,
Authentication Administrator or Privileged Authentication Administrator, and Application or Cloud
Application Administrator. They are requested only when you press Arm containment,
never at sign-in, so a tenant that only wants triage never consents to them. Every action is confirmed
in a dialog first. The mailbox step needs the optional Exchange backend, which is the only component
holding a standing credential - the full trust model is written out in
SECURITY.md.
Structure and ordering follow Tiago S. Carvalho's
Microsoft 365 IR runbook: first 60 minutes;
validate the commands and least-privilege roles against Microsoft Learn before adopting them as a
formal procedure.
Going deeper: full extraction & import
Triage answers the fast per-user question. When you need a complete forensic acquisition - every mailbox's live rules and delegations, message traces, mailbox audit logs and the full retention window - use the Full extraction page (button in the top bar). It walks you through running the open-source Microsoft-Extractor-Suite locally in PowerShell, with the exact install, connect and collection commands.
Already have Suite output? Use View the output here on the start page (or the
import button on the extraction page) to load it - no sign-in needed. The importer reads the
Unified Audit Log (Get-UAL, CSV or JSON), Entra sign-in logs
(Get-GraphEntraSignInLogs, JSON) and OAuth grants, plus any
Evidence JSON you exported from Triage, and runs the same severity-ranked
findings report. Everything is parsed in your browser tab - nothing is uploaded.
Sign-in logs import best from the Suite's JSON output (its default); a flattened CSV export loses the nested location/status fields. The Unified Audit Log imports from either CSV or JSON.
Removing access
The tool holds no standing access: tokens expire after about an hour and nothing runs when the tab is closed - including any containment permissions, which are dropped with the rest of the session. A page reload also returns you to the sign-in screen rather than restoring the session. To remove it entirely, delete the enterprise application "Limon-IT M365 Triage" in your Entra portal (Enterprise applications → select it → Properties → Delete). That revokes the consent for the whole tenant.
📋 What's new
Everything added or changed in M365 Triage, newest first. The overlay after sign-in shows you only what landed since your last visit - this is the full record.