11.6 Post-Compromise Activity Assessment
4-6 hours · Module 11
Post-Compromise Activity Assessment
The sign-in investigation (11.5) confirmed the compromise: the attacker replayed stolen tokens and accessed M365 services from 203.0.113.47. This subsection determines what the attacker did after gaining access. The results directly inform containment scope (11.7) and eradication actions (11.8).
Required role: Microsoft Sentinel Reader for KQL queries.
Step 1: Check for inbox rule creation
Inbox rules are the attacker’s first persistence and stealth mechanism. Rules redirect, delete, or hide incoming email — preventing the victim from seeing password reset notifications, security alerts, or replies to the attacker’s fraudulent emails.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // Inbox rules created by compromised users from attacker infrastructure
CloudAppEvents
| where TimeGenerated > datetime(2026-02-27T08:00:00Z)
| where ActionType in ("New-InboxRule", "Set-InboxRule", "Enable-InboxRule")
| extend RuleData = parse_json(RawEventData)
| extend Creator = tostring(RuleData.UserId)
| extend ClientIP = tostring(RuleData.ClientIP)
| extend RuleName = tostring(RuleData.Parameters
| mv-expand RuleData.Parameters
| where tostring(RuleData.Parameters.Name) == "Name"
| project tostring(RuleData.Parameters.Value))
| extend MoveToFolder = tostring(RuleData.Parameters
| mv-expand RuleData.Parameters
| where tostring(RuleData.Parameters.Name) == "MoveToFolder"
| project tostring(RuleData.Parameters.Value))
| extend ForwardTo = tostring(RuleData.Parameters
| mv-expand RuleData.Parameters
| where tostring(RuleData.Parameters.Name) in ("ForwardTo", "RedirectTo", "ForwardAsAttachmentTo")
| project tostring(RuleData.Parameters.Value))
| extend DeleteMessage = tostring(RuleData.Parameters
| mv-expand RuleData.Parameters
| where tostring(RuleData.Parameters.Name) == "DeleteMessage"
| project tostring(RuleData.Parameters.Value))
| project TimeGenerated, Creator, ClientIP, RuleName,
MoveToFolder, ForwardTo, DeleteMessage
| order by TimeGenerated asc
|
What to look for:
Forwarding rules. ForwardTo or RedirectTo containing an external email address (not @northgateeng.com). This is data exfiltration — every incoming email is copied to the attacker. Document the external address as an IOC.
Delete rules. DeleteMessage = “True” with conditions matching keywords like “password,” “reset,” “security,” “alert,” “suspicious,” “unauthorized.” The attacker is hiding security notifications from the victim.
Move-to-folder rules. MoveToFolder pointing to RSS Feeds, Conversation History, or a custom hidden folder. The attacker routes email out of the inbox so the victim does not see it.
Rules created from the attacker IP. If ClientIP matches the attacker IP (203.0.113.47), the rule was created by the attacker — not by the user. Rules created from the user’s known IP may be legitimate.
Create a bookmark for each malicious rule found. Tag: INC-2026-0227-001, post-compromise, inbox-rule. Record the exact rule name and conditions — you will need these for eradication (11.8).
Step 2: Check for mail forwarding configuration
Separate from inbox rules — Exchange Online has a mailbox-level forwarding setting.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Mailbox forwarding changes
CloudAppEvents
| where TimeGenerated > datetime(2026-02-27T08:00:00Z)
| where ActionType == "Set-Mailbox"
| extend Details = parse_json(RawEventData)
| extend TargetMailbox = tostring(Details.ObjectId)
| extend ForwardingAddress = tostring(Details.Parameters
| mv-expand Details.Parameters
| where tostring(Details.Parameters.Name) in
("ForwardingSmtpAddress", "ForwardingAddress", "DeliverToMailboxAndForward")
| project tostring(Details.Parameters.Value))
| where isnotempty(ForwardingAddress)
| project TimeGenerated, TargetMailbox, ForwardingAddress,
ClientIP = tostring(Details.ClientIP)
|
If results exist: Every email sent to the mailbox is forwarded to the attacker’s address — in addition to any inbox rules. This is a separate persistence mechanism that survives inbox rule removal. Note it for eradication.
Step 3: Check for email access (MailItemsAccessed)
Determine what the attacker read. This establishes the data exposure scope.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // Email access from attacker IP — what did they read?
CloudAppEvents
| where TimeGenerated > datetime(2026-02-27T08:00:00Z)
| where ActionType == "MailItemsAccessed"
| where AccountObjectId in (
// Object IDs of confirmed compromised users
SigninLogs
| where IPAddress == "203.0.113.47"
| where ResultType == "0"
| distinct UserId
)
| extend Details = parse_json(RawEventData)
| extend ClientIP = tostring(Details.ClientIPAddress)
| where ClientIP == "203.0.113.47" // Only access from attacker IP
| summarize
EmailsAccessed = count(),
FirstAccess = min(TimeGenerated),
LastAccess = max(TimeGenerated),
AccessMethods = make_set(tostring(Details.ClientInfoString), 5)
by AccountObjectId
|
Why this matters: The number of emails accessed determines the data breach scope. If the attacker accessed 5 emails from a finance manager’s mailbox, the exposure is limited. If they accessed 500 emails over 3 hours, they may have harvested financial data, employee PII, or business-sensitive communications. This number goes into the IR report (11.11) and may trigger data breach notification obligations depending on the content of the accessed emails.
Compliance mapping: NIST CSF RS.AN-2 (The impact of the incident is understood). ISO 27001 A.5.25 (Assessment and decision on information security events). Determining what data was accessed is a regulatory requirement — you must be able to state the scope of data exposure.
Step 4: Check for MFA method modification
The attacker may register a new MFA method to maintain access after the stolen token expires.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // MFA method changes for compromised users
AuditLogs
| where TimeGenerated > datetime(2026-02-27T08:00:00Z)
| where OperationName has_any (
"User registered security info",
"User registered all required security info",
"Admin registered security info",
"User changed default security info")
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| where TargetUser in ("j.morrison@northgateeng.com", "s.chen@northgateeng.com", "a.patel@northgateeng.com")
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| project TimeGenerated, TargetUser, OperationName, IPAddress,
ModifiedProperties = tostring(TargetResources[0].modifiedProperties)
| order by TimeGenerated asc
|
What to look for: Any MFA registration from the attacker IP or from an IP that is not the user’s known corporate IP. If the attacker registered their own Authenticator app, they can survive a password reset — they authenticate with the new password (obtained from a follow-up phishing attempt or password spray) using their own MFA method.
This is critical for eradication (11.8). If a new MFA method was registered, it must be removed in addition to revoking tokens and resetting the password. Failure to remove the attacker’s MFA method means containment is incomplete.
Step 5: Check for OAuth application consent
The attacker may have granted consent to a malicious OAuth application to maintain persistent API access to the mailbox.
1
2
3
4
5
6
7
8
9
10
11
| // OAuth consent grants by compromised users
AuditLogs
| where TimeGenerated > datetime(2026-02-27T08:00:00Z)
| where OperationName == "Consent to application"
| where Result == "success"
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| where ConsentUser in ("j.morrison@northgateeng.com", "s.chen@northgateeng.com", "a.patel@northgateeng.com")
| extend AppName = tostring(TargetResources[0].displayName)
| extend AppId = tostring(TargetResources[0].id)
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| project TimeGenerated, ConsentUser, AppName, AppId, IPAddress
|
If results exist: The attacker has persistent API access via the OAuth application. Even after password reset and token revocation, the OAuth app can access the mailbox using its own credentials. This consent must be revoked during eradication (11.8).
Compile post-compromise activity summary
After running all five steps, compile the findings:
| User | Inbox Rule | Forwarding | Emails Accessed | MFA Modified | OAuth App |
|---|
| j.morrison | Yes — forward to evil@attacker.com, delete security alerts | Yes — SMTP forward to evil@attacker.com | ~340 emails over 3 hours | No | No |
| s.chen | Yes — move security alerts to RSS Feeds | No | ~120 emails over 1 hour | Yes — new Authenticator registered | No |
| a.patel | Yes — forward to evil2@attacker.com | No | ~85 emails over 45 minutes | No | No |
This table is the eradication checklist (11.8) and the scope section of the IR report (11.11).
Subsection artifact: The five post-compromise queries above plus the activity summary template. These form the post-compromise assessment section of your AiTM investigation playbook.
Knowledge check
Check your understanding
1. You find an inbox rule that deletes all incoming emails containing the words "password reset" and "security alert." What is the attacker's objective?
The attacker is hiding evidence of the compromise from the victim. If IT sends a password reset notification or a security alert about suspicious activity, the inbox rule deletes it before the victim sees it. This extends the attacker's dwell time — the victim does not know their account is compromised because they never receive the notifications that would alert them. The rule must be removed during eradication (11.8) before any password reset or security notification is sent.
The attacker wants to reduce the victim's inbox volume
The rule is a legitimate spam filter
The attacker is testing inbox rule creation
Hiding security notifications to extend dwell time. Remove the rule BEFORE sending password reset notifications — otherwise the victim never sees the reset email.
2. You revoke tokens and reset the password for s.chen. The attacker registered a new Authenticator app on s.chen's account. Is containment complete?
No. The attacker's registered Authenticator app survives password reset and token revocation. If the attacker obtains the new password (via a follow-up phishing attempt or credential stuffing with a reused password), they can authenticate using their own MFA method. Remove the attacker's MFA registration from the user's authentication methods before considering containment complete. Subsection 11.8 covers this eradication step.
Yes — password reset and token revocation are sufficient
Yes — the attacker cannot use the Authenticator without the password
Only if conditional access blocks the attacker's IP
Attacker's MFA method survives password reset. Remove it or containment is incomplete. Always check for MFA modifications during AiTM eradication.