Signs Your M365 Tenant Is Hacked

How to recognize a Microsoft 365 compromise before the attacker decides what to do next.

By , CTO · Published · Last updated

TL;DR

  • The question is rarely "are we hacked," it's "how would we tell?" Median time-to-detection for an SMB-scale M365 compromise sits between 30 and 60 days.
  • Symptoms fall into three buckets ranked by certainty: user-visible (a forwarding rule appears, customers ask about emails nobody sent), admin-visible (legacy auth attempts, risky sign-ins, new role assignments), and audit-log-visible (the patterns that survive an attacker trying to cover their tracks).
  • Four tells get missed almost every time: an inbox rule named with hidden whitespace, forwarding to a Gmail address that looks like an employee, an authentication method the user did not add, and a Conditional Access policy with one new exclusion.
  • You can confirm or rule out most suspicions in under an hour with two Microsoft Graph queries and a unified audit log search. Block first, then investigate, is the wrong order.
  • Once you are confident the tenant is compromised, stop reading detection content and move to response. The 24-hour playbook is in the recovery checklist linked at the bottom.

The question is rarely "are we hacked"

A real M365 compromise looks nothing like the ones in the security awareness videos. There is no countdown timer, no skull-and-crossbones screensaver, no demand for Bitcoin. Most of the time the tenant looks completely normal. Mail flows. Calendars sync. Teams meetings start on time. The owner is sitting in a coffee shop wondering whether the weird DocuSign email last Tuesday was something they should have flagged.

That is the gap attackers depend on. The Verizon DBIR has measured median dwell time for SMB-scale email compromises at somewhere between 30 and 60 days. During those weeks the attacker reads mail, learns the org chart, watches for an invoice cycle, sets up persistence, and waits. Every additional day is more harvested data and more chances to spread laterally.

So the operational question is rarely "are we hacked." It is "how would we tell?" The rest of this guide is built around that question. The signals split into three groups: things any user can spot, things an admin can see in the portal, and things only a unified audit log query will reveal. Most real compromises leave traces in all three layers. False alarms tend to leave traces in only one.

Panic, investigate, or probable compromise

Before listing the symptoms, calibrate the response. A single anomaly is rarely worth a full incident response. A combination is almost always worth one.

  • Investigate. One user-visible symptom with no admin-visible or audit-log corroboration. Examples: a single suspicious sign-in alert, one MFA prompt out of nowhere, one OAuth consent screen the user closed without clicking. Spend 15 minutes confirming or ruling out before escalating.
  • Probable compromise. One symptom in two or more of the three layers, or any one of the four overlooked tells described later. Examples: a user reporting a "Sent" item they didn't send and a sign-in log entry from an unfamiliar IP. This is where you stop investigating casually and start treating it as an active incident.
  • Confirmed compromise — explicit attacker artefacts, regardless of how few they are. A new global admin account nobody created. A forwarding rule sending mail to an external address. A service principal added with broad scopes. None of these are "investigate." They are "respond."

Hold that framing in mind as you read the next three sections. The point is not to treat every anomaly as a breach. It is to know which combinations should pull the alarm.

User-visible symptoms (anyone can spot)

These show up in the inbox, the phone, and the customer support thread. Anyone using Microsoft 365 day to day can recognize them without admin access. They tend to be the earliest signal because attackers usually hit one mailbox first and only later move into the broader tenant.

  • An inbox forwarding rule the user did not create. Open Outlook, click the gear, go to Mail → Rules. Anything sending mail to an external address is a red flag, especially anything filtering by keywords like "invoice," "wire," "payment," or "ACH." This is the single most common artefact left behind in business email compromise.
  • "Sent" or "Deleted" folders contain messages the user did not write. Attackers send phishing or invoice-redirect emails from the compromised mailbox, then delete them to hide the activity. The Sent and Deleted folders are the first place to look. Recoverable Items is the second.
  • Customers, vendors, or coworkers ask about emails nobody sent. Often this is the first signal. A vendor calls to confirm a wire instruction. A customer replies asking why you're sending DocuSign links. The user has no idea what they're talking about. Take it seriously every single time.
  • Sign-in alerts at 3 AM from unfamiliar locations. Microsoft sends these when a sign-in does not match normal patterns. Most people ignore them. They should not. A successful sign-in at an hour the user is asleep, from a country they have never visited, is a sign-in that should be investigated immediately, not deleted.
  • MFA prompts triggered without an attempted login. This is "MFA fatigue" preparation. The attacker has the password and is trying to get the user to approve a prompt out of habit. If the user receives an MFA push they did not initiate, change the password and revoke sessions before doing anything else.
  • Unfamiliar OAuth app consent prompts. A pop-up asking the user to grant "MailBox.Read" or "Files.ReadWrite.All" to an app they don't recognize is consent phishing. Do not click. The link in the original email is what dropped the user there.
  • Password reset emails for accounts the user did not initiate. Unprompted password reset emails from Microsoft, GitHub, banking apps, or anywhere else are a sign someone is testing the account or trying to lock the user out. Always check, even if the email looks legitimate.

Any one of these alone is investigate territory. Two of them in the same week from the same user is almost always probable compromise. The pattern matters more than any single event.

Admin-visible symptoms (in the M365 admin center and Entra)

These require admin access to see. They are higher fidelity than user-visible symptoms because the attacker rarely cleans them up, and they survive a single mailbox compromise that has spread elsewhere.

  • Legacy auth attempts in sign-in logs. In Entra → Sign-in logs, filter Client app on every "Legacy Authentication Clients" option. POP, IMAP, SMTP AUTH, EWS, MAPI Over HTTP, Other clients. Successful legacy auth sign-ins on a tenant with users who only use Outlook for Microsoft 365 should not exist. If they do, somebody is authenticating around your MFA. The fix is in the legacy auth blocking guide on SecureYourTenant.
  • Risky sign-in alerts with elevated risk score. Entra → Protection → Risky sign-ins. Sign-ins flagged "high" risk are evaluated by Microsoft's threat intelligence as likely compromised, usually because the credentials appeared in a leaked password dump or because the IP is associated with known attacker infrastructure. Treat each one as probable compromise until proven otherwise.
  • Conditional Access policies modified or disabled. Entra → Security → Conditional Access. Sort by Last modified. Anything changed in the last 90 days that you didn't authorize is a finding. Attackers occasionally disable a CA policy to allow their own access; more often they add a one-user exclusion, which is harder to spot (covered in the overlooked tells section).
  • New admin role assignments. Entra → Roles and administrators. Click Global Administrator and review the member list. Then click each of Privileged Role Administrator, Exchange Administrator, SharePoint Administrator, Application Administrator, and Cloud Application Administrator. Any account in any of those roles that you didn't add is a confirmed compromise event.
  • New OAuth app registrations or consent grants. Entra → Enterprise applications. Sort by Created on. Look for apps registered recently with broad Mail or Files permissions. Then check Entra → App registrations for the same. Service principal additions over the last 30 days are the high-signal review.
  • Mailbox audit log showing MailItemsAccessed from unfamiliar IPs. The MailItemsAccessed audit event is the closest M365 has to a "what did the attacker actually read" log. It is enabled by default on E5; on E3 you have to turn it on with Set-Mailbox -AuditEnabled $true. Search for accesses from unfamiliar IPs after a suspicious sign-in.
  • Forwarding rules at the mailbox level. User-visible forwarding rules show up in Outlook. Mailbox-level forwarding (configured by an attacker who has admin access, not just user access) does not. Run Get-Mailbox | Where { $_.ForwardingSmtpAddress -ne $null } in Exchange Online PowerShell. Every result needs a known reason.

Most managed-services contracts include a monthly review of these surfaces. Most internal IT teams do not run them at all. The gap is where dwell time accumulates.

Audit-log-visible symptoms (deep inspection)

The unified audit log is what survives an attacker who knows what they're doing. They can delete a forwarding rule before they leave. They cannot un-write the audit log entry that records the rule being created. These are the patterns to query for when the user-visible and admin-visible signals are ambiguous, or when you need to confirm a suspected compromise after the fact.

  • Update-InboxRule or New-InboxRule on multiple mailboxes within a short window. One user creating an inbox rule is normal. Five users in the same hour creating rules with similar filtering criteria is an attack pattern, usually a phishing campaign that compromised multiple accounts and is automating persistence.
  • Add-MailboxPermission for a delegated access pattern. An attacker who wants long-term access without keeping the original credentials granted will sometimes give a separate (also compromised) mailbox FullAccess permission to the target. The original credentials can change, the rotated MFA token can expire, and the attacker still has access through the delegated mailbox.
  • Set-Mailbox -ForwardingSmtpAddress on multiple accounts. Same pattern as inbox rules but at the mailbox level. The audit log records every modification regardless of whether the rule is later deleted.
  • Set-OrganizationConfig modifying tenant-wide settings. This is rare but high-signal. Attackers occasionally disable audit logging itself, modify the default authentication policy, or change tenant-wide sharing settings to widen the attack surface.
  • New service principal created with broad scopes. Look for Operations of Add service principal followed by Add app role assignment grant to user on Mail.Read, Mail.ReadWrite, Files.Read.All, or any *.All scope.
  • Failed auth followed quickly by successful auth from the same IP. A burst of failed sign-ins followed within minutes by a success from the same IP, on the same account, is the textbook signature of credential stuffing or password spray. The success is the breach event. The failures are the noise that should have alerted somebody.

The unified audit log retains 90 days by default and 365 days on E5 with Audit Premium. If you are running E3 with default settings, your investigation window is short. Treat anything older than 80 days as urgent: re-investigation will not be possible once it ages out.

The four most-overlooked symptoms

These are the ones we see slip past internal IT, MSPs, and even some incident response teams. They get missed because each one looks deliberately like a normal artefact of legitimate use.

1. An inbox rule named with hidden whitespace

Open Outlook, look at the rules list, and what you see is a list with names. What you don't see is a rule named simply " " (a single space) or . (a single period) or just an invisible Unicode character. The rule still runs. It just doesn't catch the eye when the user scrolls past. Sort the rules list and look for entries with one-character or zero-character names. Run Get-InboxRule -Mailbox [email protected] | Select Name, Enabled, Description to see the actual filter logic on each one.

2. Forwarding to a Gmail address that looks like an employee

A forwarding address of [email protected] jumps out. A forwarding address of [email protected], on a tenant where John Smith is an actual employee, does not. Attackers register Gmail addresses with a username one character off from a real internal employee, then forward to that address. When an admin reviews the forwarding configuration, they think "John Smith forwarding to John Smith's Gmail, that's fine." It is not fine. Cross-reference every external forwarding address against the actual personal addresses your employees confirm using.

3. A new "Authentication Method" the user did not add

In Entra, each user has a list of registered authentication methods: Authenticator app, FIDO2 key, hardware OATH token, SMS, voice. An attacker with brief access can add their own TOTP authenticator or a hardware OATH token to the user's account, then drop the password. Future sign-ins that pass MFA continue working from the user's perspective. The attacker authenticates separately using the new method they added, on a future date, with a credential the user has never seen. Audit Authentication Methods on every privileged account at least monthly. Anything that doesn't trace to a device the user owns is a finding.

4. A Conditional Access policy with one user excluded that wasn't there yesterday

The MFA-enforcing policy is still on. The block-legacy-auth policy is still on. Everything looks fine. What changed is the Excluded users list. Yesterday it had two break-glass accounts. Today it has two break-glass accounts plus one regular user. That one user can now sign in without MFA, from anywhere, on any client. The change does not show up unless somebody runs Get-MgIdentityConditionalAccessPolicy and diffs the exclusion lists against last month's snapshot. Set up a monthly export and compare.

A detection workflow you can run in an hour

When you suspect something but you're not sure, here is the order of operations. The whole sequence takes about an hour and answers the question for almost every realistic scenario.

Step 1: Pull recent forwarding rules across the whole tenant. If a compromise exists, this is the most likely artefact. Connect to Exchange Online PowerShell, then:

Connect-ExchangeOnline

# Mailbox-level forwarding (set by Set-Mailbox)
Get-Mailbox -ResultSize Unlimited |
  Where-Object { $_.ForwardingSmtpAddress -ne $null -or $_.ForwardingAddress -ne $null } |
  Select-Object DisplayName, UserPrincipalName, ForwardingSmtpAddress, ForwardingAddress, DeliverToMailboxAndForward |
  Export-Csv mailbox-forwarding.csv -NoTypeInformation

# Inbox rules added in the last 30 days, all mailboxes
$cutoff = (Get-Date).AddDays(-30)
Get-Mailbox -ResultSize Unlimited | ForEach-Object {
  Get-InboxRule -Mailbox $_.UserPrincipalName |
    Where-Object { $_.WhenChanged -ge $cutoff -and ($_.ForwardTo -or $_.ForwardAsAttachmentTo -or $_.RedirectTo -or $_.DeleteMessage) } |
    Select-Object @{N='Mailbox';E={$_.MailboxOwnerId}}, Name, Enabled, ForwardTo, RedirectTo, DeleteMessage, WhenChanged
} | Export-Csv inbox-rules-30d.csv -NoTypeInformation

Open both CSVs. Anything forwarding externally needs a documented business reason. Anything with a one-character rule name, anything filtering on financial keywords, anything created at an hour none of your staff were online, gets investigated.

Step 2: Audit recent admin role assignments and service principal additions. If the attacker reached for persistence, this is where it lives:

Connect-MgGraph -Scopes "AuditLog.Read.All", "Directory.Read.All"

$start = (Get-Date).AddDays(-90)

# Role assignment events
Search-UnifiedAuditLog -StartDate $start -EndDate (Get-Date) `
  -Operations "Add member to role.","Add eligible member (permanent).","Add member to role completed (PIM activation)." -ResultSize 5000 |
  Select-Object CreationDate, UserIds, Operations, AuditData |
  Export-Csv role-assignments-90d.csv -NoTypeInformation

# Service principal and app consent events
Search-UnifiedAuditLog -StartDate $start -EndDate (Get-Date) `
  -Operations "Add service principal.","Consent to application.","Add app role assignment grant to user.","Add OAuth2PermissionGrant." -ResultSize 5000 |
  Select-Object CreationDate, UserIds, Operations, AuditData |
  Export-Csv app-consents-90d.csv -NoTypeInformation

Every line in either CSV needs to map to a known business action. Onboarding a new admin. Approving a new SaaS app. Granting a service account a scope it actually needs. If a line doesn't map, it is the start of an investigation.

Step 3: Cross-reference suspicious sign-ins with the events above. Open Entra → Sign-in logs, filter Status to Success, and look for sign-ins on the suspect account in the 24 hours before any role assignment, app consent, or forwarding rule creation. The IP, user agent, and location of those sign-ins are the attacker's working IPs. Every other sign-in from the same IP, across every user, becomes part of the scope.

If Step 1 and Step 2 come back clean and Step 3 finds no unusual sign-ins, you can mostly stand the suspicion down. Document what you found and move on. If any step finds something, the answer is not "investigate further informally," it is "open an incident."

False positives: what looks like a hack but isn't

Calibration matters. Some of the loudest signals turn out to be benign almost as often as they turn out to be real. Knowing the common false positives keeps the team from burning out on every alert.

  • Sign-in from an unfamiliar IP that resolves to a major mobile carrier. The user is on cellular instead of office Wi-Fi, sometimes routed through a different gateway than usual. Geolocation databases mis-locate cellular IPs constantly. Confirm with the user before escalating.
  • VPN-related "impossible travel" alerts. The user signed in from Chicago at 9 AM and Singapore at 9:05 AM, which Entra flags as impossible travel. Often the second sign-in is a corporate VPN egress in Singapore for a meeting with overseas staff. Check the IP against your VPN's known ranges.
  • OAuth consent prompts from legitimate but unfamiliar apps. A new Microsoft 365 add-in, a Power Automate flow set up by another admin, a third-party scanner the operations team approved last week. Consent prompts are not always malicious. Check Entra Enterprise applications for who registered the app before assuming the worst.
  • Inbox rules created by the user that they forgot about. Auto-forwarding from a personal Gmail to the work account, set up two years ago and forgotten. Confirm with the user before deleting; otherwise you'll fix nothing and break a workflow.
  • MFA prompts triggered by background sync on a device the user uses occasionally. An old iPad, a personal laptop that auto-launches Outlook on boot, a smart speaker with a calendar integration. Background processes refresh tokens and occasionally trigger an MFA challenge. Look at the device name in the MFA log before treating it as an attack.

The pattern: any single signal is more often a false positive than a real attack. Two signals that line up across layers (a sign-in alert and an inbox rule, a consent prompt and a new role assignment) almost never line up coincidentally. That is the threshold to escalate.

Once you're confident, switch from detection to response

This guide is deliberately about recognition, not remediation. If your conclusion at the end of the workflow is "yes, the tenant is compromised," stop reading detection content and move to the response track immediately. Every additional minute spent investigating instead of containing is a minute the attacker still has access.

The first 24 hours of response are covered step by step in the M365 tenant compromised recovery checklist. The longer-form, 25-step playbook covering disclosure obligations, forensic preservation, and customer communication is in the data breach response checklist. Common questions about scope, evidence, and notification timelines are in the M365 breach FAQ.

For the email-specific side of the picture (which is where most M365 compromises actually start), the deeper read is the email compromise detection guide on EmailShield365 and the deep-dive on persistence via mailbox forwarding at email forwarding attacks in Microsoft 365.

Why early detection actually matters

Most breach guides treat detection time as a footnote. It isn't. The financial and operational difference between a 3-day detection and a 60-day detection is enormous, and almost every variable in the cost equation is a function of dwell time.

IBM's Cost of a Data Breach report has put the average price of a sub-200-day breach at roughly 30 percent below the cost of one that runs longer. For SMB tenants, the same dynamic shows up in three concrete ways. First, more time means more harvested data. The attacker spends week one reading mail, weeks two and three exporting attachments, and week four through eight sending invoice-redirect emails to vendors. The longer the access, the more financial and reputational damage compounds.

Second, more time means more spread. A compromise that started with one user's inbox often grows to include shared mailboxes, Teams channels, OneDrive folders, and eventually a service principal granted broad scopes through OAuth consent. The cleanup at day 60 is not the cleanup at day 3 with extra steps. It is a different operation entirely, often touching every user.

Third, more time means worse insurance posture. Cyber policies increasingly tie payouts to evidence of monitoring and timely detection. A claim filed 60 days after the initial intrusion, with no logs older than 90 days because audit retention wasn't extended, is the kind of claim insurers reduce or deny. The difference between a paid claim and a denied one is often the difference between "we caught it in week one" and "we caught it when the bank called us."

Common questions

How quickly can an attacker do real damage after they get in?

Faster than most people expect. Within minutes of a successful sign-in, an attacker can register an OAuth app with Mail.ReadWrite scope, set up a forwarding rule, and exfiltrate the inbox over Graph API. The intrusion-to-persistence window in observed cases is often under 15 minutes. The financial-damage window (sending an invoice-redirect email to a real vendor) is usually one to three days, just long enough for the attacker to read the actual invoice cycle and pick a target.

If I see one of these symptoms, should I just change the password and move on?

No. Changing the password is necessary but never sufficient. By the time you see a symptom, the attacker has often already established secondary access through one of the persistence mechanisms in this guide: a forwarding rule that survives the password change, an OAuth app with its own refresh token, an additional MFA method registered to the user's account, or an inbox rule that exfiltrates new mail to an external address. Run the detection workflow before you assume the password change closed the door.

My MSP says everything looks fine. How do I know they actually checked?

Ask for the artefacts. A real check produces three CSV files: mailbox-level forwarding (output of Get-Mailbox filtered on ForwardingSmtpAddress), recent inbox rules (output of Get-InboxRule), and recent role and consent events (output of Search-UnifiedAuditLog on the relevant operations). If the MSP can produce those files with timestamps from this week, they checked. If they say "we looked at the dashboard and it's all green," they didn't.

Will the attacker know I'm investigating?

Sometimes, yes. PowerShell read operations against the tenant don't notify users. But if you reset the suspect user's password, revoke their sessions, or disable an OAuth app the attacker controls, the attacker may receive an immediate signal that they've been spotted. The right sequence is: investigate quietly first (read-only PowerShell, audit log searches), confirm scope, then take all containment actions in a single tight window so the attacker doesn't have time to deploy backup persistence between steps.

How long should I keep audit logs to investigate after the fact?

The default is 90 days on most M365 plans, which is shorter than the average dwell time we see in real cases. If you're on E3, enable Audit (Premium) or move retention to 365 days through the Microsoft Purview compliance portal. The cost is small and the value is enormous: an investigation in month four with no audit log left is mostly an exercise in guessing. The unified audit log is the single most valuable forensic surface in M365, and it ages out faster than anyone expects.

Get the free Breach Response Playbook

No spam. Unsubscribe anytime.

Don't Wait to Check Your Tenant

If you've spotted any of the signs in this guide, or you want to find out what's already running quietly inside your tenant, start with a risk assessment.

Check My Risk