$2.3 MILLION
OAUTH PHISH
WIRE FRAUD
21 DAYS SILENT
Incident Response DFIR Engagement BEC TLP:WHITE September 2024

The $2.3 Million Email Thread

How a threat actor silently read a law firm’s inbox for 21 days, waited for a real estate closing, and walked into the wire transfer conversation at exactly the right moment.

Scroll

The following is a lightly anonymised account of a real DFIR engagement. Client identifiers, staff names, and financial specifics have been altered. All technical artifacts, commands, and findings are presented as they occurred. We are publishing this case because BEC is the highest-grossing cybercrime category on earth and most organisations still do not understand how little technical sophistication it actually requires — or how much forensic evidence it leaves behind on the attacker’s side.

ParameterDetail
Case TypeBusiness Email Compromise (BEC) — Wire Fraud, Silent Inbox Monitoring
IndustryLegal Services (Mid-size Law Firm, Real Estate & Corporate Practice)
Duration4 days on-site, 11 days remote — concurrent with financial clawback attempt
YearYear 6 of operations
NotorietyThe one where the attacker’s patience was genuinely more impressive than their technique

The Engagement

How We Were Called In

The call came at 3:18 PM on a Thursday. The firm’s managing partner had just been informed by their client — a commercial real estate developer — that the $2.3 million wire transfer sent that morning to complete a property acquisition had arrived at the wrong bank. Specifically, it had arrived at a bank account that the developer had never provided, at a financial institution they had never heard of, in a jurisdiction they had not expected. The developer had called to confirm receipt. The funds were not there.

The wire had been sent six hours earlier. The originating bank had been notified and a recall initiated. We were engaged simultaneously. In BEC wire fraud, six hours is a very long time. The money was almost certainly already moving.

Key Observation

BEC is not a technical problem that occasionally involves money. It is a financial crime that occasionally involves technology. The forensics come second. The bank call comes first.

The Environment

The firm had 47 attorneys and approximately 90 total staff. Their email environment was Microsoft 365 Business Premium — an important detail, because Premium includes Defender for Office 365 Plan 1 and the full Unified Audit Log at 90-day retention. They had not configured any Defender for Office 365 policies beyond the defaults. Safe Links was enabled. Safe Attachments was not. Anti-phishing policy was at default sensitivity. The audit log was enabled but had never been reviewed.

The CFO’s account — the one that received and responded to the wire instruction — was the starting point for everything.

Finding 1: The Fraudulent Email — Header Analysis

Artifact: Email Message Headers — Full RFC 5322 Header Chain

Path: Exported from Outlook: File → Save As → .eml | Or: M365 Admin → Message Trace → Export headers

  • Full headers include: Received chain (hop-by-hop), Return-Path, Authentication-Results (SPF/DKIM/DMARC)
  • Authentication-Results header added by the RECEIVING mail server — not forgeable by sender
  • X-Originating-IP, X-Mailer, and X-Forwarded-To can reveal infrastructure even through webmail
  • Microsoft adds X-MS-Exchange-CrossTenant-AuthAs and X-MS-Exchange-Organization-SCL (spam confidence)

The fraudulent wire instruction had been sent from an email address that appeared, at a glance, to be the developer’s CFO: the display name was correct, the signature block was correct, and the email address shown in the Outlook preview was correct. The fraud was in the actual sending domain — a lookalike registered four days before the attack.

EMAIL HEADERS
# Email header dissection — SPF/DKIM/DMARC all pass for attacker-controlled lookalike domain

From: James ████████ <j.████████@████████-properties.com>   ← LEGITIMATE display

# Actual sending domain (visible only in full headers / raw message):
Return-Path: <j.████████@████████-propertiess.com>   ← NOTE: double 's' in 'properties'
X-Originating-IP: 185.234.██.██   ← Romanian VPS, not developer's known IP range

# Authentication results (added by firm's M365 on receipt):
Authentication-Results: spf=pass (sender IP 185.234.██.██)
  smtp.mailfrom=████████-propertiess.com;
  dkim=pass (signature was valid) header.d=████████-propertiess.com;
  dmarc=pass action=none header.from=████████-propertiess.com;

# All three authentication checks PASS — because the attacker controls the lookalike domain
# SPF: attacker added their VPS IP to the lookalike domain's SPF record
# DKIM: attacker generated valid DKIM keys for the lookalike domain
# DMARC: lookalike domain had p=none — monitors but takes no action

# Microsoft Spam Confidence Level:
X-MS-Exchange-Organization-SCL: -1   ← SCL -1 = bypass all spam filtering (trusted sender list)
# Why SCL -1? The developer's REAL domain was on the firm's trusted sender list.
# M365 applied the trust to the lookalike because domain-matching was not strict.

This is the mechanism that makes BEC effective at scale: the attacker registers a lookalike domain, configures legitimate SPF, DKIM, and DMARC records for that domain (a 20-minute task), and sends email that passes every authentication check. The authentication infrastructure is working exactly as designed — it is authenticating that the email genuinely came from the lookalike domain. It cannot tell you whether the lookalike domain is malicious.

OSINT / DNS
# Lookalike domain OSINT — SPF/DKIM/DMARC all pre-configured, MX active to receive replies

# WHOIS:
whois ████████-propertiess.com
# Registrar: Namecheap  |  Registered: 2024-██-██ (4 days before fraudulent email)
# Registrant: Privacy-protected

# DNS records (dig):
dig TXT ████████-propertiess.com
# v=spf1 ip4:185.234.██.0/24 ~all   ← attacker's VPS subnet explicitly authorized

dig TXT selector1._domainkey.████████-propertiess.com
# v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA...  ← valid DKIM public key

dig TXT _dmarc.████████-propertiess.com
# v=DMARC1; p=none; rua=mailto:reports@████████-propertiess.com
# p=none: even if DMARC failed, no action would be taken

# MX records (can this domain receive email?):
dig MX ████████-propertiess.com
# 10 mail.████████-propertiess.com   ← YES. Attacker could receive replies.

# VirusTotal domain report:
# First seen: 2024-██-██  |  Detections: 0/88  |  Categories: uncategorized
# Passive DNS: only 1 IP ever resolved — 185.234.██.██ (Romania, Frantech ASN)
Finding 01

Lookalike Domain Registered 4 Days Prior — Full SPF/DKIM/DMARC Authentication Bypasses Filtering

The fraudulent wire instruction was sent from a visually identical lookalike domain (double-letter typosquat) registered four days before the attack. The attacker pre-configured valid SPF, DKIM, and DMARC records — all three authentication checks passed. The domain had an active MX record, allowing the attacker to receive and reply to any responses. Microsoft’s SCL assigned -1 (trusted sender) because the firm’s allowed-sender list used domain prefix matching. The email bypassed all spam and anti-phishing controls.

Finding 2: The Silent Phase — 21 Days of Inbox Monitoring

Artifact: M365 Unified Audit Log — MailItemsAccessed Operation

Path: UAL Operation: MailItemsAccessed | Requires: E3+ licence with audit enabled

  • MailItemsAccessed: records every read operation against a mailbox — client type, IP, folder, message IDs
  • OperationProperties.MailAccessType: ‘Bind’ = specific message read; ‘Sync’ = bulk folder sync (IMAP)
  • ClientInfoString identifies the access method: OWA, Outlook, MAPI, IMAP, REST (Graph API)
  • CRITICAL: Sync events group many messages — look for unusual Sync bursts from non-Outlook clients
  • Availability: only in E3/E5 — M365 Business Basic does NOT log MailItemsAccessed

The immediate question in any BEC investigation is: how did the attacker know about this specific wire transfer? The answer is almost always the same — they had been reading the target’s email for weeks. The UAL MailItemsAccessed operation is the artifact that proves it.

POWERSHELL
# MailItemsAccessed — REST/Graph API reads from Romanian VPS, 21 days prior to fraud
# Pull MailItemsAccessed for CFO mailbox — 90 day lookback
Connect-ExchangeOnline

Search-UnifiedAuditLog \
  -StartDate (Get-Date).AddDays(-90) \
  -EndDate (Get-Date) \
  -Operations MailItemsAccessed \
  -UserIds cfo@firm.com \
  -ResultSize 5000 | ForEach-Object {
    $d = $_.AuditData | ConvertFrom-Json
    [PSCustomObject]@{
      Time       = $_.CreationDate
      IP         = $d.ClientIPAddress
      ClientInfo = $d.ClientInfoString
      AccessType = ($d.OperationProperties | Where-Object Name -eq 'MailAccessType').Value
      FolderPath = $d.Folders[0].Path
    }
} | Export-Csv .\cfo_mailaccess.csv -NoTypeInformation

# Group by IP and ClientInfo to identify anomalous access patterns:
# IP                ClientInfoString                           AccessCount  DateRange
# 192.168.█.███     Outlook/16.0 (Windows; MAPI)              4,847        [normal office access]
# 198.51.100.██     Outlook/16.0 (Windows; MAPI)              312          [confirmed home IP]
# 185.234.██.██     Client=REST;Action=GraphQL;Service=OWA     1,203        ← ANOMALOUS

# The anomalous IP: Romanian VPS (same as fraudulent email sender IP)
# ClientInfo: REST/GraphQL access — Graph API or OAuth app, NOT a standard Outlook client
# First anomalous access: 21 days before the fraudulent wire instruction
# Last anomalous access: 4 hours before the fraudulent email was sent

1,203 email access events from the attacker’s IP over 21 days. That averages to roughly 57 reads per day — not a flood, not an obvious anomaly in raw volume terms, but a steady cadence of someone checking in on a monitored inbox. The attacker was not downloading everything. They were reading selectively, and the UAL Bind events showed which threads they had prioritised.

POWERSHELL
# Bind event reconstruction — attacker-accessed message subjects, full 21-day thread surveillance
$anomalous_ip = '185.234.██.██'

$bind_events = Search-UnifiedAuditLog \
  -Operations MailItemsAccessed \
  -UserIds cfo@firm.com \
  -StartDate (Get-Date).AddDays(-90) \
  -EndDate (Get-Date) \
  -ResultSize 5000 | Where-Object {
    ($_.AuditData | ConvertFrom-Json).ClientIPAddress -eq $anomalous_ip
  }

# Extract InternetMessageIds from Bind events — cross-reference against mailbox
$bind_events | ForEach-Object {
  ($_.AuditData | ConvertFrom-Json).Folders.FolderItems
} | Select-Object -ExpandProperty InternetMessageId | Sort-Object -Unique |
  ForEach-Object {
    Get-MgUserMessage -UserId cfo@firm.com -Filter "internetMessageId eq '$_'" |
      Select-Object Subject, ReceivedDateTime, From
  }

# RESULTS (subset — attacker-accessed messages, chronological):
# ReceivedDateTime     Subject                                           From
# T-21d               Re: ████ Commercial Closing — docs attached        developer.cfo@...
# T-20d               Re: Wire instructions for ████ acquisition          developer.cfo@...
# T-18d               Updated wire confirmation — escrow contact          firm.partner@...
# T-15d               Re: Closing date confirmed — ████-██-██             developer.cfo@...
# T-7d                Final wire amount confirmed: $2,341,500             developer.cfo@...
# T-4d                Re: Tuesday closing — anything else needed?         developer.cfo@...
# T-1d                Wire ready to send — please confirm account details developer.cfo@...
Key Observation

The attacker read every email in the closing thread, in order, over three weeks. They waited until the wire amount was confirmed and the account details had been exchanged before they moved. This is patience, not technical sophistication.

Finding 02

21 Days of Silent Inbox Surveillance — 1,203 REST/Graph API Access Events from Attacker IP

UAL MailItemsAccessed records confirmed the attacker had authenticated to the CFO’s mailbox via REST API from a Romanian VPS beginning 21 days before the fraudulent email. Bind event analysis recovered the specific message IDs accessed — every email in the real estate closing thread, including the message confirming the exact wire amount ($2,341,500) and the legitimate bank account details. The attacker monitored the thread to completion, then struck in the final 24-hour window before the wire was due.

Finding 3: How the Account Was Compromised — OAuth App Consent

Artifact: Azure AD Audit Log — OAuth Application Consent Events

Path: Azure Portal → Azure Active Directory → Audit Logs → Activity: Consent to application

  • Activity type: ‘Consent to application’ — records when a user grants OAuth permissions to a third-party app
  • Key fields: InitiatedBy (user), Target (app name + AppId), additionalDetails (permissions granted)
  • Also check: ‘Add OAuth2PermissionGrant’ in audit log — same event, different display depending on tenant config
  • Entra ID Protection: OAuth phishing often precedes MailItemsAccessed anomalies by 24–72 hours
  • Revoke immediately: Azure AD → Enterprise Applications → [App] → Delete, then revoke all refresh tokens

The question that takes longest to answer in BEC investigations is almost always: how did they get in? In this case, there was no password compromise in the traditional sense. The CFO’s password had never been stolen. Instead, the attacker had used a technique that bypasses passwords entirely: OAuth application phishing, sometimes called ‘consent phishing’ or ‘illicit consent grant.’

The mechanism is elegant in a profoundly irritating way. The attacker sends a link to a Microsoft OAuth authorization page. The page is a real Microsoft page — no spoofing, no lookalike domain. The CFO clicks ‘Accept,’ and in doing so, they grant a malicious application registered in Azure AD read access to their mailbox. No password changes hands. MFA is never triggered. Microsoft’s own OAuth infrastructure does the rest.

POWERSHELL / GRAPH
# Azure AD Audit Log — illicit OAuth app consent, Mail.Read + offline_access granted by CFO
Connect-MgGraph -Scopes 'AuditLog.Read.All','Directory.Read.All'

Get-MgAuditLogDirectoryAudit -Filter \
  "activityDisplayName eq 'Consent to application' and " +
  "initiatedBy/user/userPrincipalName eq 'cfo@firm.com'" \
  -Top 50 | ForEach-Object {
    [PSCustomObject]@{
      Time        = $_.ActivityDateTime
      App         = ($_.TargetResources | Where-Object Type -eq 'ServicePrincipal').DisplayName
      AppId       = ($_.TargetResources | Where-Object Type -eq 'ServicePrincipal').Id
      Permissions = ($_.AdditionalDetails | Where-Object Key -eq 'Permissions').Value
      IP          = $_.InitiatedBy.User.IPAddress
    }
}

# OUTPUT:
Time                    App                           AppId            Permissions
2024-██-██T16:41:02Z    'Microsoft Secure Mail Reader' ██████-████-...  Mail.Read, offline_access

# 'Microsoft Secure Mail Reader' — not a Microsoft product. Attacker-registered app name.
# Permissions granted: Mail.Read (read all mailbox content) + offline_access (refresh token)
# offline_access = no expiry on access without user re-authentication
# This consent event occurred 21 days before the fraud — matching MailItemsAccessed first anomaly

The malicious application was named ‘Microsoft Secure Mail Reader’ — a name chosen specifically to look plausible on the Microsoft OAuth consent screen, which lists the app name prominently and the permissions in smaller text below. The CFO had received a phishing email directing them to click a link to ‘verify their Microsoft 365 account security settings.’ The link went to a legitimate Microsoft authorization URL with the malicious app’s client_id embedded.

OAUTH URL
# OAuth phishing URL reconstruction — legitimate Microsoft authorization endpoint, malicious app client_id
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
  ?client_id=██████-████-████-████-████████████    ← malicious app's Azure AD registration
  &response_type=code
  &redirect_uri=https://██████-██████.azurewebsites.net/callback   ← attacker's Azure Web App
  &scope=Mail.Read%20offline_access
  &state=██████

# This is a REAL Microsoft URL — nothing to block at network layer
# The only indicator is the client_id, which requires Azure AD app registration lookup

# Check for other users who consented to the same malicious app:
Get-MgServicePrincipal -ServicePrincipalId ██████-████-████-████-████████████ |
  Get-MgServicePrincipalOauth2PermissionGrant | Select-Object PrincipalId, Scope

# OUTPUT: 1 additional user — a junior associate who had also consented
# (their mailbox was not targeted — lower-value intelligence)
Finding 03

Illicit OAuth Consent Grant — Mail.Read + offline_access, No Password Compromise, MFA Irrelevant

The account compromise was achieved via OAuth application phishing (‘illicit consent grant’). The CFO was directed to a legitimate Microsoft OAuth authorization page and granted a malicious Azure AD-registered application named ‘Microsoft Secure Mail Reader’ permission to read their entire mailbox (Mail.Read) with non-expiring access (offline_access). MFA was never triggered because no password was used. The attacker’s app used the issued refresh token to read the mailbox via Graph API for 21 days without re-authentication. A second user (junior associate) had also consented to the same app.

Finding 4: The Interception — Thread Hijack Reconstruction

Artifact: M365 Message Trace + Exchange Online Mail Flow Logs

Path: Exchange Admin Center → Mail Flow → Message Trace | PowerShell: Get-MessageTrace

  • Message Trace covers 10 days in the EAC UI; 90 days via Start-HistoricalSearch
  • Key fields: MessageId, SenderAddress, RecipientAddress, Subject, Status, ToIP (destination MTA)
  • Status ‘Delivered’ = accepted by next-hop MTA; ‘GettingStatus’ = extended trace in progress
  • Cross-reference MessageId with UAL MailItemsAccessed to confirm attacker read the real replies
  • Use Get-MessageTraceDetail -MessageId to get full delivery hop chain per message

The attacker’s tradecraft in the interception itself was simple but effective. Rather than compromising the developer’s email account — a harder target — they impersonated it. After 21 days of surveillance, they knew the thread, the parties, the tone, the closing timeline, and the exact moment the CFO would be expecting wire confirmation details.

The fraudulent email arrived the morning of the closing. It was a reply to a real thread — the attacker had copied the In-Reply-To header from a legitimately observed email, making their message appear in the same conversation chain in Outlook. The content: a brief, professionally worded note from ‘James’ explaining that the escrow company had changed banks and providing updated wire instructions. The signature block was copied verbatim from a real email James had sent three days earlier.

POWERSHELL
# Message trace — fraudulent email delivery path, anti-spam bypass, and time-to-wire-sent
Get-MessageTrace -SenderAddress j.████████@████████-propertiess.com \
  -RecipientAddress cfo@firm.com \
  -StartDate (Get-Date).AddDays(-7) \
  -EndDate (Get-Date) |
  Select-Object Received, SenderAddress, RecipientAddress, Subject, Status, MessageId

# OUTPUT:
Received               SenderAddress                          Status    Subject
2024-██-██ 09:14:33    j.████████@████████-propertiess.com   Delivered Re: ████ Commercial Closing...

# Delivery hops:
# 1. Submitted from: 185.234.██.██ (Romanian VPS) → smtp.████████-propertiess.com (attacker MTA)
# 2. Relayed to: firm-com.mail.protection.outlook.com (M365 inbound MX)
# 3. Anti-spam verdict: SCL -1 (bypass — domain on trusted list)
# 4. Delivered to: cfo@firm.com inbox — 09:14:33

# Time from delivery to CFO opening the email:
# MailItemsAccessed Bind event for this MessageId: 09:16:51 — 2 min 18 sec

# Time from CFO opening to wire being sent:
# Bank wire log (provided by firm's finance team): 10:47:00 — 1 hour 30 minutes

The attacker also took a precaution that many BEC operators skip: they monitored the CFO’s mailbox in near-real-time during the critical window. Between 09:14 (delivery) and 10:47 (wire sent), the attacker’s REST API access log showed 14 Bind events against the CFO’s inbox — checking for replies, checking whether the CFO had forwarded the email for verification, checking whether anything had gone wrong. When the wire confirmation email arrived from the CFO’s bank at 10:52 — five minutes after the wire was sent — the attacker read it within 90 seconds.

Finding 04

Thread Hijack via In-Reply-To Header Spoofing — Wire Sent 93 Minutes After Fraudulent Email Opened

The fraudulent email used the legitimate conversation’s In-Reply-To header, causing it to appear within the correct thread in the CFO’s Outlook client. Delivery-to-open was 2 minutes 18 seconds. Open-to-wire was 93 minutes. The attacker monitored the mailbox in real-time during the attack window (14 REST API reads in 93 minutes) to detect any indication of suspicion. The bank confirmation email was accessed by the attacker 90 seconds after arrival — before the CFO’s firm had even begun to suspect the fraud.

Finding 5: Attacker Infrastructure — The Mule Account Chain

Artifact: Financial Transaction Records + OSINT on Receiving Account

Path: Wire transfer records (bank-provided) | FinCEN SAR database | Passive DNS | OSINT

  • Wire trace: correspondent bank chain from originating institution to first-hop receiving account
  • BEC mule accounts: typically domestic US/EU accounts registered 2–8 weeks before use, personal or LLC
  • FinCEN BSA E-Filing: SAR filings on accounts — searchable via law enforcement channels (not public)
  • Time is critical: most BEC funds move within 24–48 hours via layering to international accounts
  • FBI IC3 complaint + FinCEN Financial Crimes Enforcement Network contact = fastest clawback path

In parallel with the forensic investigation, the firm’s bank had initiated a wire recall and the firm’s litigation counsel had filed an emergency FBI IC3 complaint. We supported both by documenting the attack chain and providing IOCs to law enforcement. The financial trace is not traditional DFIR work — but in BEC cases, it is inseparable from the investigation, because the attacker’s infrastructure overlaps between the technical and financial layers.

FINANCIAL TRACE
# Wire trace and funds movement timeline — $2.1M converted to crypto within 9 hours

Originating Bank:     Royal ████ Bank — Toronto, Canada
Originating Account:  ████████████████ (firm's trust account)
Amount:               CAD $2,341,500.00
Value Date:           2024-██-██
Wire Method:          SWIFT MT103

Receiving Bank:       ████████████ Bank — Miami, Florida, USA
Receiving Account:    ████████████████
Account Name:         ████████ Global Solutions LLC  ← registered Florida LLC, 6 weeks old
BIC/SWIFT:            ████████XXX

# OSINT on receiving entity:
# Florida SOS lookup: ████████ Global Solutions LLC
#   Filed: 2024-██-██ (6 weeks before wire)  ← matches typical BEC mule account timing
#   Registered Agent: private registered agent service (no identifiable principal)
#   Authorized Member: name matches known money mule recruitment forum persona

# Funds movement (from FBI financial intelligence, 48hr post-complaint):
# T+4 hours:   $2,341,500 received at Miami account
# T+6 hours:   $2,100,000 wired to intermediary — ████████ Bank, Hong Kong
# T+9 hours:   $1,900,000 onward-wired to virtual asset service provider (VASP)
# T+11 hours:  Funds converted to USDT (Tether) — wallet address: 0x████████
# T+18 hours:  $241,500 remaining in Miami account — FROZEN on bank hold request

# Recovery: $241,500 recovered (10.3% of total wire amount)

Of the $2,341,500 wired, $241,500 was recovered via bank hold — a 10.3% recovery rate. The remaining $2.1 million moved through a domestic mule account, across a correspondent bank in Hong Kong, and into a virtual asset service provider within nine hours of the wire clearing. Once converted to USDT on a non-KYC exchange, traceability collapsed to blockchain analysis — possible but slow, and by that point the assets had already been further dispersed across multiple wallets.

Ten percent recovery is, by the grim arithmetic of BEC wire fraud, not a bad outcome. The median recovery rate for BEC wire fraud reported to FBI IC3 is approximately 4%. The reason we did better than median was that the bank call happened within six hours and the IC3 complaint was filed with complete technical documentation the same afternoon. Documentation quality and speed are the only variables that remain in the victim’s control at that point.

Finding 05

Funds Moved to Crypto in 9 Hours — $241,500 Recovered (10.3%), $2.1M to USDT

The receiving mule account (a 6-week-old Florida LLC) moved funds in three hops within 11 hours: domestic bank → Hong Kong correspondent → VASP → USDT. A bank hold request on the Miami account recovered $241,500. The VASP wallet received the remaining funds but had no KYC requirement; blockchain tracing identified subsequent dispersion to 7 downstream wallets. Law enforcement referral was made. Prosecution prospects at time of writing: the Florida mule was identified and charged; operators remain unattributed beyond the Romanian VPS infrastructure cluster.

Finding 6: Persistence — The Transport Rule Nobody Knew About

Artifact: Exchange Online Transport Rules + Inbox Rules Audit

Path: Exchange Admin Center → Mail Flow → Rules | UAL: New-TransportRule, New-InboxRule

  • Transport rules (admin-level) can silently redirect/delete mail before it reaches the inbox — invisible to users
  • Inbox rules (user-level) visible in Outlook — attacker-created rules may forward or delete replies
  • UAL New-TransportRule: confirms if attacker escalated to admin-level mail flow manipulation
  • Check: any rule created in the anomalous IP window that matches keywords related to the fraud topic
  • BEC operators frequently create deletion rules for terms like ‘fraud’, ‘verify’, ‘unusual’ to suppress warnings

On day two of the investigation, reviewing the CFO’s inbox rules, we found something that had not been in the initial scope: a rule created from the attacker’s IP address three days before the fraudulent wire instruction was sent. It was not a forwarding rule. It was a deletion rule.

POWERSHELL
# Inbox deletion rule — silently deletes fraud alerts and verification emails containing key terms
Get-InboxRule -Mailbox cfo@firm.com -IncludeHidden | \
  Select-Object Name, Enabled, DeleteMessage, MoveToFolder,
    SubjectContainsWords, SubjectOrBodyContainsWords, From,
    @{N='Created';E={$_.WhenChanged}} |
  Sort-Object Created

# RESULT — anomalous rule (attacker-created, already disabled post-incident):
Name:                       'junk-filter-update'
Enabled:                    False  ← disabled as part of IR containment
DeleteMessage:              True   ← permanently deletes matching email, bypasses Deleted Items
SubjectOrBodyContainsWords: {unusual, verify wire, confirm account, bank change, fraud alert,
                             suspicious, security alert, stop payment, recall}

# This rule would have silently deleted any email containing fraud-related keywords
# before the CFO ever saw it — including bank fraud alerts, IT security notifications,
# and any reply from the developer's real CFO saying 'we didn't send new wire instructions'

# UAL confirmation of rule creation:
# Operation: New-InboxRule | Time: T-3 days, 02:17 UTC | ClientIP: 185.234.██.██
# The rule was created at 2:17 AM — when the CFO was not at their desk.

The deletion rule is the detail that makes BEC investigations particularly unsettling on reflection. The attacker had anticipated the fraud-detection response and pre-deleted it. Any email containing words like ‘verify wire,’ ‘stop payment,’ ‘bank change,’ or ‘fraud alert’ would have been permanently deleted before the CFO saw it. The attacker was not just stealing — they were actively managing the victim’s awareness.

In this case, the fraud was detected not because the deletion rule failed, but because the developer’s real CFO had called on the phone to confirm receipt. Voice calls remain outside the attacker’s control surface. This detail ended up in the firm’s post-incident security training.

Finding 06

Deletion Inbox Rule Pre-Created 3 Days Before Fraud — Silently Suppressed Fraud Alerts

The attacker created an inbox deletion rule named ‘junk-filter-update’ at 02:17 UTC (2:17 AM local time) three days before the fraudulent email was sent. The rule permanently deleted any email containing fraud, verification, or payment-related keywords — including bank fraud alerts, security notifications, and any verification requests from the developer’s actual team. The rule was confirmed via UAL (New-InboxRule from attacker IP). Discovery was only possible because the developer’s CFO made a voice call rather than sending an email to verify receipt.

Reconstructed Attack Timeline

TimestampArtifact SourceEvent
T-25 days Azure AD Audit Log OAuth phishing email delivered to CFO. Linked to legitimate Microsoft OAuth authorization page for malicious app ‘Microsoft Secure Mail Reader.’
T-25d, 16:41 UTC Azure AD Audit Log CFO grants Mail.Read + offline_access to malicious OAuth app. Refresh token issued. No password compromise. MFA not triggered.
T-21 days UAL MailItemsAccessed First REST API read of CFO inbox from Romanian VPS (185.234.██.██). Attacker begins monitoring the real estate closing thread.
T-21 to T-4 days UAL MailItemsAccessed 1,203 read events. Attacker tracks full closing thread: wire amount ($2,341,500), account details, closing date, participant names and tone.
T-4 days, 02:17 UTC UAL (New-InboxRule) Deletion inbox rule ‘junk-filter-update’ created at 2:17 AM. Silently deletes emails containing fraud, verify, bank change, stop payment keywords.
T-4 days DNS WHOIS Lookalike domain ████████-propertiess[.]com registered. SPF, DKIM, DMARC, and MX records configured. Domain ready to send and receive email.
T-0, 09:14:33 Message Trace Fraudulent wire instruction sent from lookalike domain, using real thread’s In-Reply-To header. Delivered to CFO inbox — SCL -1 (trusted sender bypass).
T-0, 09:16:51 UAL MailItemsAccessed Attacker confirms delivery — Bind read event on fraudulent message ID from Romanian VPS. Real-time monitoring begins.
T-0, 09:14 to 10:47 UAL MailItemsAccessed 14 attacker read events during 93-minute fraud window. Attacker monitoring for verification attempts or suspicious replies.
T-0, 10:47 Bank wire records CFO initiates SWIFT MT103 wire for $2,341,500 to Florida mule account (████████ Global Solutions LLC).
T-0, 10:52 UAL MailItemsAccessed Bank wire confirmation email arrives in CFO inbox. Attacker reads it within 90 seconds — before the firm suspects fraud.
T-0, 14:45 Developer phone call Developer’s real CFO calls to confirm wire receipt. Fraud discovered. Bank notified. IR engaged. 6 hours post-wire.
T-0, 15:18 IR engagement Mjolnir Security retained. UAL collection begins. OAuth app identified and revoked. Deletion rule disabled.
T-0 + 4 hrs Bank records Funds arrive in Florida mule account. $2,100,000 immediately wired onward to Hong Kong.
T-0 + 9 hrs Bank records / blockchain $2,100,000 converted to USDT via VASP. Blockchain wallet identified. Funds dispersed to 7 downstream addresses.
T-0 + 18 hrs Bank hold $241,500 remaining in Florida account frozen. 10.3% recovery. FBI IC3 complaint filed with full technical documentation.

What This Engagement Teaches Us

For Incident Responders

  1. In BEC, the bank call is the most time-sensitive action — before forensics, before containment, before anything else. Every minute between wire completion and bank notification reduces the recovery probability. Document this in your IR runbook explicitly.
  2. UAL MailItemsAccessed with REST/Graph ClientInfoString is the canonical indicator of OAuth-based silent mailbox monitoring. Filter for access from non-Outlook client strings (REST, GraphQL, IMAP) and non-expected IP addresses in the days to weeks before the fraud date.
  3. Always check inbox rules AND transport rules. Attacker-created deletion rules targeting fraud-related keywords are a standard BEC persistence mechanism. These rules run silently, are not visible in the normal Outlook inbox rules pane, and will be enabled during the critical window.
  4. Reconstruct which specific messages the attacker read using Bind events in MailItemsAccessed — this lets you prove exactly what intelligence the attacker had, which is essential for the financial fraud investigation and any litigation.

For Security Engineers & Legal/Financial Sector IT

  1. Block illicit OAuth consent grants at the tenant level: Azure AD → Enterprise Applications → Consent and Permissions → User consent settings → ‘Do not allow user consent.’ Force all OAuth app approvals through an admin consent workflow. This single configuration change would have prevented this incident entirely.
  2. DMARC enforcement (p=reject or p=quarantine) on your OWN domain does not protect against lookalike domain fraud. Your vendors, clients, and counterparties also need DMARC enforcement. Consider a supplier email security standard for high-value transacting parties.
  3. Wire transfer verification must include a voice callback to a pre-registered number — not a number provided in the email requesting the change. This is the sole control that actually interrupted the fraud in this case. Make it mandatory for any wire instruction change, regardless of how legitimate the email appears.
  4. Audit connected OAuth applications in your M365 tenant quarterly: Azure AD → Enterprise Applications → All Applications → filter by ‘User Assigned.’ Any application with Mail.Read or Mail.ReadWrite that your security team did not approve is an immediate investigation. Tools like Hawk (open source) automate this audit.

Mjolnir Security — BEC Investigation & Incident Response

Mjolnir Security provides 24/7 incident response, M365 forensics, and BEC investigation services to law firms, financial institutions, and real estate companies. Our DFIR team has investigated over 150 BEC engagements and supported wire fraud recovery totalling over $40 million.

Incident Response M365 Forensics BEC Investigation OAuth App Audit Wire Fraud Recovery Email Security Assessment

mjolnirsecurity.com — 24/7 Incident Response Hotline: +1 833 403 5875

Written by Mjolnir Security DFIR team

Published September 2024 · DFIR Engagement Series · TLP:WHITE

Case #463 · Skuggaheimar · Mjolnir Security · All client details anonymized · TLP:WHITE