A hidden two letter mailbox rule was concealing a full account takeover. We found it, proved it, and shut it down in under a day.
A boutique executive search firm nearly lost the one thing its whole business runs on, the trust in its founder's inbox. Here is exactly how the attack worked, the questions we asked, and every PowerShell step we ran to contain it.
During a routine review of a client's mailboxes, we found a hidden inbox rule named "cx" that silently deleted every incoming message. It was the tell of a live account takeover. An attacker had stolen a sign in session, planted the rule to stay hidden, and blasted a fraudulent proposal to 263 of the founder's external contacts from her trusted address. We ran an evidence first response through PowerShell against Exchange Online and Microsoft Graph, proved the compromise, pinned the attacker to a datacenter IP, measured the full blast radius, killed the stolen session, and swept every mailbox. The account was contained in under 24 hours and proven isolated to one inbox.
- A hidden delete rule was not the attack, it was the alarm. It proved the account was already compromised
- Evidence first, always. Every rule and log was captured before anything was changed or removed
- Contained in under 24 hours, proven isolated to one mailbox, with all 263 affected contacts reconstructed to warn
The numbers that mattered
The story in one minute
During a routine review of a client's mailboxes, our team spotted something wrong in the founder's account. An inbox rule named cx, two letters and no description, quietly deleting every incoming message and marking it as read.
That rule was not a setting. It was a trap. Someone had already stolen a live sign in session, slipped into the mailbox, and planted the rule so the founder would never see the replies, warnings, or bounce backs while the attacker used her name. Minutes after planting it, they blasted a fake collaboration proposal to 263 of her external contacts, the exact people whose trust the firm is built on.
We ran a structured, evidence first response through PowerShell against Exchange Online and Microsoft Graph. We proved the compromise, pinned the attacker to a datacenter in Dallas, measured the full blast radius, killed the stolen session, swept every other mailbox, and handed leadership a plain language report the next morning. Total time from detection to containment was under 24 hours.
A rule that hides the attack is the tell that an attack is already underway.
The client, and why email was everything
The client is a boutique executive search firm. We will call them Meridian Executive Search. They place senior leaders, the CHROs and CFOs, into private equity backed companies. The firm is small, around 18 people, but it plays at the top of the market.
Their entire business runs on email relationships. Candidates, hiring clients, and private equity partners all live in the founder's inbox. That reliance is exactly what made an email compromise so dangerous. For a firm whose product is trust and introductions, a hijacked inbox is not an IT inconvenience. It is a direct line to every relationship they have ever built.
The compromised account belonged to the Managing Partner, Diana Whitfield. Her address is trusted by hundreds of senior people who would open anything she sent without a second thought. The attacker knew that too. That trust was the asset they came to steal.
The discovery: a rule called "cx"
It started during a routine security review of the firm's mailboxes. Our rule audit flagged an inbox rule in the Managing Partner's account that did not belong. A rule named cx, two letters, no description, set to delete every incoming message and mark it as read.
The detail that mattered most was what the rule did not have. It had no conditions. No sender filter, no subject match, nothing. With no conditions attached, the rule applied to all inbound mail. In plain terms, someone had planted a switch that made the executive's incoming email vanish silently, so she would never see replies, security alerts, or fraud bounce backs while an attacker operated in her name.
A hidden delete rule is a classic account takeover move. Attackers use it so their victim stays blind while the fraud goes out. Finding one is near proof that the account is already in someone else's hands. So we treated it as a live incident from that minute.
The rule itself does little damage. Its job is concealment. It hides the replies and warnings that would tip off the victim, buying the attacker time to abuse the trusted account. If you find one, assume the account is compromised and respond. Do not just delete the rule and move on.
The scoping questions we asked
Before we changed a single setting, we scoped the response around four questions leadership needed answered fast. Every command we ran afterward existed to answer one of these.
A benign rule and an attacker's rule can look similar at a glance. Leadership needed proof, not a hunch, before anyone hit the panic button. Step one was to prove it with evidence.
Password and MFA were both on. If the attacker still got in, we needed to know how, so the fix would actually close the door and not just rotate a password.
One compromised account can become many. Before we could tell the client they were safe, we had to sweep every mailbox and prove the blast radius.
Containment is a sequence, not a single click. We needed the exact steps to kill the stolen session, close backdoors, and warn everyone who received the fraud.
Our approach: evidence first
We ran a structured incident response entirely through PowerShell, against Exchange Online and Microsoft Graph. The guiding rule was simple. Capture proof at every step before changing anything. If you delete the attacker's rule before preserving it, you have destroyed your own evidence and learned nothing about who did it or how far it went.
The response moved through four stages, each mapped to one of the scoping questions:
- Detect and preserve. Audit inbox rules across the tenant, isolate the malicious one, and capture its full definition as evidence before removal.
- Attribute the intrusion. Query the Unified Audit Log to pin the exact time, actor, and source IP that created the rule, then geolocate the address.
- Measure the blast radius. Run message trace analysis to quantify outbound volume and reconstruct the full recipient list of the fraudulent email.
- Contain and harden. Reset credentials and MFA, revoke the stolen session, sweep every mailbox, and review OAuth grants for hidden persistence.
The PowerShell playbook
This is the real sequence of commands, cleaned up and anonymized. Each block is one move in the response. Use the copy button on any block to grab the command. Read them top to bottom and you can follow the whole investigation, from the first hunt to the command that killed the attacker's session.
Step 1. Hunt every inbox rule in the tenant
First we connected to Exchange Online and swept every mailbox for rules that delete, forward, hide, or carry a suspiciously short or blank name. The cx rule lit up on the first pass.
# Connect with the client admin account (a browser sign in pops up)
Connect-ExchangeOnline -ShowBanner:$false
# Scan every mailbox for risky inbox rules
$mailboxes = Get-Mailbox -ResultSize Unlimited
foreach ($mb in $mailboxes) {
Get-InboxRule -Mailbox $mb.PrimarySmtpAddress | ForEach-Object {
$flags = @()
if ($_.DeleteMessage) { $flags += 'DELETE' }
if ($_.ForwardTo -or $_.RedirectTo) { $flags += 'FORWARD/REDIRECT' }
if ($_.MarkAsRead -and $_.DeleteMessage) { $flags += 'STEALTH-READ' }
if ($_.Name.Length -le 2) { $flags += 'SHORT/BLANK-NAME' }
if ($flags) { [pscustomobject]@{
Mailbox = $mb.PrimarySmtpAddress; Rule = $_.Name; Flags = $flags -join '; ' } }
}
}
Step 2. Preserve the evidence before touching it
Evidence before remediation, always. We dumped the complete definition of every rule in the mailbox to a file, so the attacker's exact configuration is preserved even after we remove it.
# Capture the full rule definition to an evidence file first
$rules = Get-InboxRule -Mailbox d.whitfield@meridianexec.com
$rules | Format-List * | Out-String -Width 4096 |
Tee-Object -FilePath C:\evidence\cx-rule-evidence.txt
Step 3. Attribute the rule to an actor, time, and IP
Next we asked the Unified Audit Log who created the rule, when, and from where. This is the command that turned a suspicious setting into proof of intrusion.
# Who created the rule, when, and from which IP
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date) `
-UserIds d.whitfield@meridianexec.com `
-Operations New-InboxRule,Set-InboxRule,UpdateInboxRules |
ForEach-Object {
$d = $_.AuditData | ConvertFrom-Json
[pscustomobject]@{ Time = $_.CreationDate; Actor = $d.UserId; ClientIP = $d.ClientIP }
} | Format-Table -AutoSize
Step 4. Rebuild the attacker's full footprint from that IP
With the malicious IP confirmed, we pulled every action taken from it, so we could see the whole session rather than a single event.
# Every audited action from the attacker's address
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date) `
-IPAddresses 45.148.203.17 -ResultSize 5000 |
ForEach-Object {
$d = $_.AuditData | ConvertFrom-Json
[pscustomobject]@{ Time = $_.CreationDate; User = $_.UserIds; Op = $_.Operations; Result = $d.ResultStatus }
} | Sort-Object Time
Step 5. Measure the blast radius and rebuild the recipient list
Now the business critical question. Who received the fraud? A message trace gave us the volume spike and, filtered by the lure subject, the exact list of everyone who needed a warning.
$lure = 'Collaboration Project Proposal'
$trace = Get-MessageTraceV2 -SenderAddress d.whitfield@meridianexec.com `
-StartDate (Get-Date).AddDays(-10) -EndDate (Get-Date) -ResultSize 5000
# Volume per day (the spike stands out immediately)
$trace | Group-Object { (Get-Date $_.Received).ToString('yyyy-MM-dd') } |
Select-Object Name, Count
# The full, unique recipient list of the fraudulent email
$trace | Where-Object Subject -like "*$lure*" |
Select-Object -Expand RecipientAddress | Sort-Object -Unique
Step 6. Remove the rule, then verify it is gone
Only after the evidence was preserved did we remove the rule, and we confirmed the removal rather than assuming it.
$cx = Get-InboxRule -Mailbox d.whitfield@meridianexec.com |
Where-Object Name -eq 'cx'
Remove-InboxRule -Mailbox d.whitfield@meridianexec.com -Identity $cx.Identity -Confirm:$false
# Verify: this should return zero rows
Get-InboxRule -Mailbox d.whitfield@meridianexec.com |
Where-Object Name -eq 'cx' | Measure-Object
Step 7. Kill the stolen session and hunt an OAuth backdoor
This is the step most people miss. A password reset does not end a session an attacker has already stolen. We switched to Microsoft Graph, revoked every session to kill the stolen token, then checked for any rogue app consent left behind as a way back in.
Connect-MgGraph -Scopes 'User.RevokeSessions.All','AuditLog.Read.All','Application.Read.All'
$user = Get-MgUser -UserId d.whitfield@meridianexec.com
# Invalidate every refresh token: the stolen session dies here
Revoke-MgUserSignInSession -UserId $user.Id
# Look for an OAuth consent backdoor the attacker may have planted
Get-MgOauth2PermissionGrant -All |
Where-Object PrincipalId -eq $user.Id |
ForEach-Object {
$sp = Get-MgServicePrincipal -ServicePrincipalId $_.ClientId
[pscustomobject]@{ App = $sp.DisplayName; Scopes = $_.Scope }
}
Step 8. Sweep every mailbox to prove isolation
Before we could tell the client they were safe, we had to prove the attacker had not planted the same rule anywhere else. So we swept all 18 mailboxes for attacker style rules.
# Sweep ALL mailboxes for delete, forward, or stealth rules
Get-Mailbox -ResultSize Unlimited | ForEach-Object {
Get-InboxRule -Mailbox $_.PrimarySmtpAddress |
Where-Object {
$_.DeleteMessage -or $_.ForwardTo -or $_.RedirectTo -or
($_.MarkAsRead -and $_.DeleteMessage)
} | Select-Object @{n='Mailbox';e={$_.MailboxOwnerId}}, Name, DeleteMessage, ForwardTo
}
Where a client wants belt and braces, we add a Conditional Access policy that blocks only the attacker address, so no legitimate user is affected. It creates a named location for the single IP, then a policy that denies sign ins from it.
# A named location for just the attacker IP
$loc = New-MgIdentityConditionalAccessNamedLocation -BodyParameter @{
'@odata.type' = '#microsoft.graph.ipNamedLocation'
displayName = 'IR-Blocked-Attacker-IPs'
isTrusted = $false
ipRanges = @(@{ '@odata.type'='#microsoft.graph.iPv4CidrRange'; cidrAddress='45.148.203.17/32' }) }
# A policy that blocks sign ins from that location only
New-MgIdentityConditionalAccessPolicy -BodyParameter @{
displayName = 'IR-Block-Attacker-IPs'; state = 'enabled'
conditions = @{ users=@{includeUsers=@('All')}; applications=@{includeApplications=@('All')}; locations=@{includeLocations=@($loc.Id)} }
grantControls = @{ operator='OR'; builtInControls=@('block') } }
The attack timeline
The audit trail told a clean, damning story. The attacker had a valid session, most likely from an earlier adversary in the middle phishing link that stole the sign in token, which is why multifactor authentication never intervened.
- Day 0 · 2:41 PMMailbox accessed from a commercial hosting datacenter in Dallas, Texas. Not a home or office connection.
- Day 0 · 7:01 PMThe hidden cx rule was created from the same address. Delete, mark as read, and stop processing, with no conditions.
- Day 0 · about 7:05 PMA fraudulent collaboration proposal was blasted to 263 external contacts from the trusted account.
- Day 0 · that eveningThe Managing Partner spotted it and began warning recipients directly not to open the message.
- Day 1MStack360 investigated, contained, and swept the whole tenant, then delivered a plain language report to leadership.
What we found
Every indicator was captured from the audit log and message trace, then attributed. Here is the short list that mattered.
| Indicator | Attribution | Verdict |
|---|---|---|
45.148.203.17 | Hosting datacenter, Dallas. Created the rule and sent the mail. | Malicious |
24.34.118.90 | Residential ISP, out of region. Tied to the earlier lure. | Suspected |
Inbox rule cx | Delete, mark as read, stop processing. No conditions. | Malicious |
| Lure subject | "Collaboration Project Proposal" to 263 contacts. | Malicious |
| OneDrive share IP | Microsoft owned address, a legitimate file notice. | Cleared |
Outbound volume on the compromise day spiked to roughly seven times the normal daily baseline, with a cluster of bounce backs consistent with a mass send to a harvested contact list. Across all 18 mailboxes, no other malicious rule, forwarding, delegate, or send as permission was found. The compromise was isolated to one account.
The fixes we made
Containment is a sequence. Here is everything we did, in order, to lock the attacker out and prove the account was clean.
- Preserved the malicious rule as evidence, then removed it and verified the removal.
- Reset the account password and multifactor authentication, closing the credential path.
- Revoked all sign in sessions through Microsoft Graph, so the stolen token was killed, not just rotated.
- Swept all 18 mailboxes for attacker style rules and found none beyond the original.
- Confirmed no forwarding, delegate, or send as backdoors on the affected mailbox.
- Reviewed OAuth app consents for hidden persistence. None were rogue.
- Reconstructed all 263 affected recipients so every one could be warned directly.
- Delivered a plain language incident report to leadership the next morning.
The outcome
The account was contained in under 24 hours, and the compromise was proven to be isolated to a single mailbox. The firm kept the trust it runs on because every affected contact was warned directly, with a complete and accurate list rather than a guess.
Just as important, the client understood exactly what happened. No jargon, no hand waving. The report explained the attack, the evidence, and the fix in language leadership could act on, and it named the one remaining business task clearly. Reach every one of the 263 contacts and tell them the proposal was not real.
"You found in one afternoon what we did not even know to look for, and you could prove every word of it. We knew exactly who to call, and we called them the same day."
Managing Partner, an executive search firm
Hardening we recommended
Containment closes the incident. These four controls stop the next one, and they run on the Microsoft 365 the firm already owns.
- Conditional Access. Block legacy authentication, and challenge sign ins from unfamiliar locations and unmanaged devices, so a stolen session from a datacenter cannot simply walk in.
- Rule and sign in alerting. Real time alerts on new inbox rules, forwarding changes, and risky logins, so a future attempt is caught in minutes rather than during a review.
- Phishing resistant sign in. Move toward FIDO2 and passkeys, which defeat the exact session theft technique that started this incident.
- Recurring rule reviews. A scheduled tenant wide inbox rule audit as a standing control, so hidden rules never get to hide for long.
Questions people ask
How did the attacker get past MFA?
They did not break it, they went around it. An adversary in the middle phishing page captures a live, already authenticated session token. With that token, the attacker rides the session the user already passed MFA for, so no password or MFA prompt ever appears. That is why phishing resistant methods like passkeys matter, and why revoking sessions, not just resetting a password, is the real fix.
Why does a password reset alone not fix this?
A password reset stops future logins with the old password. It does not end a session the attacker already holds. Their stolen token keeps working until it is explicitly revoked. That is why we revoke all sign in sessions through Microsoft Graph as part of containment, which invalidates the token immediately.
Would you find a hidden rule like this in our tenant today?
That is exactly what our Microsoft 365 rule and sign in health check does. We run the same evidence first sweep across every mailbox, surface hidden delete or forward rules, and check for risky sign ins and OAuth backdoors, before any of it reaches your clients.
Would you find a hidden rule in your inbox today?
MStack360 runs a fast Microsoft 365 rule and sign in health check that surfaces exactly this kind of hidden compromise, before it reaches your clients. If you suspect an account is already in trouble, we respond the same day.
Book a security check Or email us directly at help@mstack360.com