M365 Token Spraying and Token Replay (Huntress)
Executive Summary
Huntress documented (blog published 2026-10-22, observation window 2026-07-14 to 2026-09-22) a Microsoft 365 campaign in which the operator never needed a password. Victims signed in through an adversary-in-the-middle (AiTM) relay that passed the real Microsoft login page and MFA prompt through, and the relay kept the post-MFA "proof of login" token. Within minutes (12 minutes on 2026-07-21, four minutes on 2026-09-22) the stolen session reappeared from an address in 193.26.115.0/24, a block owned by the German hosting provider 1337 Services GmbH (AS210558 per Huntress and Krebs on Security). Huntress named the technique "token spraying": the same pattern as password spraying, but with many stolen tokens replayed at volume across many organizations. Tokens also came from device-code phishing and from social engineering during a legitimate login.
Huntress responded to incidents at 79 organizations, recording 497 successful sign-ins against more than 200,000 failed attempts. It saw at least 48 rogue Entra devices registered, existing MFA methods wiped and replaced within three hours of the first campaign sign-in in 19 organizations, an authenticator labelled "iPhone 13" activated about 38 minutes after one replay (Huntress timeline, 2026-07-21), and hidden inbox rules with junk names that hid payroll and banking mail. One mailbox was used to send spam to hundreds of outside recipients and another to send a payroll-related email to an outside accountant, which points at business email compromise.
Naming. This campaign has no public name and the operator is unattributed. "Token spraying" is Huntress's name for the technique, not for a campaign or an actor, and this pack uses no other name. The Hacker News headline says 97 compromised accounts; that figure does not appear in Huntress's own text, so it is carried here as an unverified secondary number and not used in any query.
Source Review & Web Hunter Notes
| Tier | Source | Key Finding | Carry |
|---|---|---|---|
| 1 · Vendor (primary) | Huntress: M365 Token Spray and Replay Campaign Compromises Business Email Accounts (published 2026-10-22) | Full chain, four phases, Build A and Build B replay tooling, IOC tables (six replay hosts, relays, client and resource IDs, rogue device names, rule names and folders), CA guidance. Source of every atomic value in the CSV. Snapshot 01. | yes |
| 2 · News | The Hacker News: Microsoft 365 Token-Replay Attacks Compromise 97 Accounts at 79 Organizations (2026-10-26) | Corroborates Huntress. Adds exy.sh, the VirusTotal AsyncRAT note, GreyNoise counts (29 addresses, 10 malicious, 5 suspicious) and the "97 accounts" figure. Snapshot 02. | partial |
| 2 · Journalism | Krebs on Security: Who's Behind the Seized Forums Cracked and Nulled (2025-02) | Ties 1337 Services GmbH (AS210558, Hamburg) to the StarkRDP and rdp.sh rental services and records the January 2025 seizure. Context only; no personal names carried into this pack. Snapshot 03. | partial |
| 2 · News | Infosecurity Magazine: Widespread Token-Spraying Attacks Target Microsoft 365 Accounts | Independent restatement of the technique, 497 sign-ins, persistence methods and CA advice. No new indicators. Snapshot 04. | partial |
| 2 · News | SecurityOnline: Token Replay Attack Hits 79 Organizations Through Stolen Microsoft 365 Sessions | Restates 497 and 200,000+, the four-minute replay gap, the 303 sign-ins from one address and 1,052 blocked attempts. No new indicators. Snapshot 05. | partial |
| 2 · News | Help Net Security: Microsoft 365 accounts compromised at 79 organizations, no passwords needed (2026-10-23) | States that Microsoft Entra ID Protection independently flagged several of the replay addresses and that blocking one address would stop a single phase at most. Snapshot 06. | partial |
| 1 · Vendor docs | Microsoft Learn: block authentication flows, token protection, revoke user access, Entra sign-in logs, Purview audit activities | Hardening authority (device-code flow block, token protection scope and limits, Revoke-MgUserSignInSession) and the real Exchange audit operation names New-InboxRule, Set-InboxRule, UpdateInboxRules. Snapshots 07 to 11. | yes |
Decisions and corrections to the brief.
- Publication date. Huntress published on 2026-10-22; The Hacker News covered it on 2026-10-26. The pack date is 2026-10-26 (build date).
- Inbox rule name list. The Hacker News prints the names with sentence punctuation inside the quotes (
".,"and",.,"). Huntress's own table lists the first name as a single dot and the fifth as a comma followed by a dot. The pack uses Huntress's table:.,sss,ss,iT,,.,........,s,d. - ASN. Huntress and Krebs on Security do publish an ASN for 1337 Services GmbH: AS210558. It is recorded as enrichment, not as a block key (see section 4).
- "iPhone 13" timing. The Hacker News says the authenticator was activated "less than 51 minutes after the session was hijacked". Huntress's timeline shows the hijack at 17:30:21 UTC and the activation at 18:08:50 UTC (38 minutes), and 50 minutes after the victim reached the MFA prompt at 17:18:20. The pack uses Huntress's timeline.
- Phase 3 counts. Huntress says 193.26.115.72 completed 303 successful sign-ins across 29 organizations, and later in the same post says 232 successful sign-ins across 28 organizations. Both are internal to Huntress; no query depends on either.
- Folders. Huntress names three hidden destination folders: RSS Feeds, RSS Subscriptions and Conversation History. The brief listed two.
- AsyncRAT. The VirusTotal AsyncRAT association is a note about 193.26.115.221's history, reported only by The Hacker News. No AsyncRAT indicator is imported and no malware family is attached to this campaign.
- Not reachable. The GreyNoise page returned HTTP 403 and the VirusTotal page is script-rendered, so the 29-address / 10-malicious / 5-suspicious counts rest on The Hacker News alone and are not used for any decision.
- Prompt-injection check. No fetched page contained instructions aimed at an AI agent, requests for further fetches or authority claims. Nothing was dropped for that reason.
Hunt Brief & Attack Chain
Working hypothesis: an operator with no public name captures post-MFA sessions through AiTM relays (plus device-code phishing and social engineering), then replays the tokens within minutes from rented addresses in 193.26.115.0/24 using one of two toolsets. Once a session is accepted the operator converts it into durable access: it wipes and re-registers MFA, registers fake Entra devices that mint tokens independently of the stolen session, and plants hidden inbox rules from different rented or VPN addresses to support payroll-themed business email compromise. Replay addresses rotate across four phases, so durable detection sits on the persistence artifacts and the sign-in fingerprints, with the address ranges as enrichment.
| Step | Behavior | Telemetry | Hunt Angle |
|---|---|---|---|
| 1 · Token capture | Victim signs in through an AiTM relay (or enters a device code, or is socially engineered); relay keeps the post-MFA token | Outbound network connection from the managed host to the relay (CrowdStrike: NetworkConnectIP4, NetworkConnectIP6. SentinelOne: IP Connect. Defender: DeviceNetworkEvents). The token theft itself is outside endpoint telemetry | Q13; the capture step is a GAP |
| 2 · Token replay | Within minutes the session is used from 193.26.115.0/24; Build A scripts OAuth2:Token with node, Build B replays browser sessions | Entra sign-in logs, interactive and non-interactive (Microsoft Sentinel: SigninLogs, AADNonInteractiveUserSignInLogs. CrowdStrike: Entra data in Next-Gen SIEM) | N1-N3, Q1-Q3, Q12 |
| 3 · Device registration | 48 fake DESKTOP- plus eight hex computers joined through the Device Registration Service | Entra sign-in logs (resource 01cb2876...) and Entra audit logs (Add device, Register device) | N3, N4, Q4, Q5, Q15 |
| 4 · MFA persistence | Existing MFA methods wiped, attacker authenticator ("iPhone 13") registered within three hours | Entra audit logs (security info deleted and registered) | N5, Q6, Q11 |
| 5 · Mail manipulation | Hidden rules (names ., sss, ss, iT, ,., ........, s, d) move or delete payroll and banking mail into RSS Feeds, RSS Subscriptions, Conversation History | Exchange Online audit events (Unified Audit Log: New-InboxRule, Set-InboxRule, UpdateInboxRules) | N6, Q7-Q10 |
| 6 · BEC and spam | Compromised mailbox sends a payroll email to an outside accountant, or spam to hundreds of outside recipients until Entra ID Protection rates the user high risk | Exchange send activity and Entra ID Protection risk events | N7 native only; Partial |
| 7 · Infrastructure | Six replay hosts in the /24 used one after another; rented VDI tunnels, consumer VPNs, other hosting for mailbox actions | Entra sign-in logs; endpoint DNS (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents DnsQueryResponse) | Q1, Q2, Q14 (enrichment) |
Hypotheses, highest fidelity first: H1 the persistence artifacts (rogue device names, MFA wipe-and-replace, hidden-folder rules) are rarer than the replay itself and survive address rotation. H2 the replay range is a hosting block with no business reason to appear in most tenants. H3 Build A has a fingerprint (Office or Broker client, node or bare Mozilla/5.0) distinct from normal Office use. H4 Conditional Access blocks (53003) and revoked-session errors (50133) mark valid credentials even when the attacker is stopped. H5 endpoint telemetry sees only the victim reaching the relay. H6 payroll-themed rule criteria identify the BEC objective.
Consolidated IOC Table
| Type | Value | Conf | Action | Context |
|---|---|---|---|---|
| IPv4 range | 193.26.115.0/24 | medium | detect (cidr in Q1) | 1337 Services GmbH block that 200,000+ failed and 497 successful sign-ins traced to. Not in the importable CSV; use cidr() or a report-only named location |
| IPv4 | 193.26.115.221 | high | detect | Build A replay and rogue device join host, Phase 1 (2026-07-14 to 2026-08-03); first detection. Flagged by Entra ID Protection per Huntress |
| IPv4 | 193.26.115.199 | high | detect | Build B browser session replay, Phase 1; hijacked the 2026-07-21 session |
| IPv4 | 193.26.115.16 | high | detect | Build A replay and device-code phishing, Phase 2 (from 2026-08-17). Flagged by Entra ID Protection per Huntress |
| IPv4 | 193.26.115.21 | high | detect | Build A replay with bare Mozilla/5.0, SharePoint Online, Phase 2. Flagged by Entra ID Protection per Huntress |
| IPv4 | 193.26.115.72 | high | detect | Build A replay, Phase 3 burst (2026-09-11 to 09-15): 303 successful sign-ins across 29 organizations per Huntress (it also cites 232 across 28). Flagged by Entra ID Protection per Huntress |
| IPv4 | 193.26.115.210 | high | detect | Build B session replay, Phase 4 (2026-09-02 to 09-22) |
| IPv4 | 124.198.132.33 | medium | detect | 1337 Services address that continued a hijacked session on 2026-07-21 (outside the /24) |
| IPv4 | 45.83.31.44 | medium | detect | 1337 Services address; failed replay of a revoked session in August (outside the /24) |
| IPv4 | 192.159.99.172 | medium | detect | 1337 Services address; failed replay of a revoked session in August (outside the /24) |
| IPv4 | 185.238.2.195 | medium | detect | AiTM relay that caught the 2026-07-21 victim at the MFA prompt |
| IPv6 | 2400:8d60:2::1:841d:5dd | medium | detect | AiTM relay at Evoxt Sdn. Bhd., Phase 4 |
| IPv4 | 167.148.41.96 | low | hunt | Interserver address that created the payroll-hiding rules in Phase 3. Shared hosting: pivot only, not in the CSV |
| Domain | exy.sh | low | detect (enrich only) | Commercial RDP rental service, rebranded StarkRDP per The Hacker News. Legitimate-service domain: never prevent |
| Domain | rdp.sh | low | detect (enrich only) | Commercial RDP rental service tied to 1337 Services GmbH. Legitimate-service domain: never prevent |
| Domain | starkrdp.io | low | hunt | Former StarkRDP domain named in the Krebs article; seized January 2025. Hunt in Q14 only, not in the CSV |
| ASN | AS210558 | medium | enrich | 1337 Services GmbH, Hamburg (Huntress; Krebs on Security). Provider-level, shared: never a block key |
| Entra device name | DESKTOP- + 8 uppercase hex | high | hunt | 48 rogue devices, examples DESKTOP-54521F46, DESKTOP-BAF88EF4, DESKTOP-CA85907C. Also Device- or Work Pc plus eight hex per Huntress |
| Authenticator label | iPhone 13 | low | hunt | Attacker-registered authenticator label; matches real iPhone 13 owners, so pivot only |
| Authenticator token | apns2-d8a3fedb1a3cd1287ca47c6851a49e6052ab6116b43666e674765ca892d798ce | medium | hunt | Device token listed by Huntress for the attacker's "iPhone 13" authenticator |
| Inbox rule names | . sss ss iT ,. ........ s d | high | hunt | Huntress rule-name table. Case sensitive; created from VPN or rented hosting, never from 1337 Services space |
| Rule destination folders | RSS Feeds, RSS Subscriptions, Conversation History | high | hunt | Obscure system folders used to hide mail |
| User agent | node; bare Mozilla/5.0 | medium | hunt | Build A. Bare Mozilla/5.0 seen on 193.26.115.21. Build B uses a current Chrome or Edge string and cannot be told from normal browsing |
| Request / error | OAuth2:Token, Login:reprocess, Cmsi:Cmsi; 53003, 50133 | medium | hunt | Request types and error codes Huntress lists; 53003 means a valid credential was stopped by Conditional Access |
Microsoft first-party identifiers Huntress lists for hunting are in the Entra identifier block of section 10. They are Microsoft constants, not campaign indicators. Values quoted from sources appear verbatim in the saved snapshots in the M365-TokenSpraying-Replay-Hunt-sources folder.
Affected Surface & Telemetry Matrix
| Surface | Required Telemetry | Priority | Gap Risk |
|---|---|---|---|
| Entra ID sign-ins (token replay, device registration) | Interactive AND non-interactive sign-in logs (Microsoft Sentinel: SigninLogs, AADNonInteractiveUserSignInLogs. CrowdStrike: Entra data connector in Next-Gen SIEM) | Critical | High: the replay is mostly non-interactive token traffic. If only interactive sign-ins are exported, Build A is invisible. Parser field names vary |
| Entra audit (devices, MFA methods) | Audit logs: Add device, Register device, security info deleted and registered (Microsoft Sentinel: AuditLogs. CrowdStrike: Entra audit connector in Next-Gen SIEM) | Critical | High: needs a second Entra export and 90+ day retention to see the July start |
| Exchange Online mailbox rules | Unified Audit Log with mailbox auditing: New-InboxRule, Set-InboxRule, UpdateInboxRules (Microsoft Sentinel: OfficeActivity. CrowdStrike: Microsoft 365 connector in Next-Gen SIEM) | Critical | High: rules made through the Outlook REST API or client may be recorded differently; four mailboxes in the report used the REST API |
| Entra ID Protection and Conditional Access | Risk detections and Conditional Access result (53003) on each sign-in | High | Medium: report-only policies hide what would have been blocked |
| Falcon Identity Protection | Entra ID coverage depends on the connector and licence in your tenant | Medium | Unverified here: this pack builds no Identity Protection rule and does not claim the module sees token replay |
| Managed endpoints (victim browser) | Outbound network connection (CrowdStrike: NetworkConnectIP4, NetworkConnectIP6. SentinelOne: IP Connect. Defender: DeviceNetworkEvents) and DNS lookup (CrowdStrike: DnsRequest. SentinelOne: DNS Resolved. Defender: DeviceEvents DnsQueryResponse) | Low | Sees only the victim reaching a relay whose hostname is unpublished; outside endpoint telemetry for the token theft, replay, device and rule steps |
ATT&CK Mapping
| Tactic | Technique | Observed Behavior | Query / Control |
|---|---|---|---|
| Credential Access | T1557 Adversary-in-the-Middle | AiTM relays captured post-MFA sessions | Q13 · section 12 (phishing-resistant MFA) |
| Initial Access | T1566.002 Phishing: Spearphishing Link | Fake sign-in pages and Office device-code phishing | N7 · Q13 · device-code block (playbook 1) |
| Defense Evasion, Lateral Movement | T1550.001 Use Alternate Authentication Material: Application Access Token | OAuth2:Token replay from the /24 with node user agent and Office or Broker client IDs; Build B browser replay | N1-N3, Q1-Q3, Q12 |
| Persistence | T1098.005 Account Manipulation: Device Registration | 48 rogue DESKTOP- devices registered through the Device Registration Service | N3, N4, Q4, Q5, Q15 |
| Persistence, Defense Evasion | T1556.006 Modify Authentication Process: Multi-Factor Authentication | Existing methods wiped, attacker authenticator registered | N5, Q6, Q11 |
| Defense Evasion | T1564.008 Hide Artifacts: Email Hiding Rules | Rules named ., sss, ss, iT, ,., ........, s, d moving or deleting mail | N6, Q7-Q10 |
| Collection | T1114.003 Email Collection: Email Forwarding Rule | Rules keyed on payroll, direct deposit, bank, bonus. Hide rules, not forwarding: mapped by the rule-criteria behavior, secondary | Q10 |
| Command and Control | T1090.003 Proxy: Multi-hop Proxy | Rented VDI tunnel and consumer VPN infrastructure; hosting rotation | Q1, Q2, Q14 (enrichment) |
T1557, T1566.002, T1550.001, T1098.005, T1556.006, T1564.008 and T1090.003 are Huntress's own mapping. T1114.003 is added by this pack for the rule-criteria behavior.
Native Audit-Log Hunts (Entra, Exchange, PowerShell)
OfficeActivity column layout differs by connector version: confirm on one row.N1 · Sign-ins from 193.26.115.0/24
union SigninLogs, AADNonInteractiveUserSignInLogs
| where ipv4_is_in_range(IPAddress, "193.26.115.0/24")
| project TimeGenerated, UserPrincipalName, AppDisplayName, AppId, ResourceDisplayName, ResourceId,
ClientAppUsed, UserAgent, IPAddress, ResultType, ConditionalAccessStatus
| order by TimeGenerated asc
N2 · Named replay hosts and relays outside the /24
union SigninLogs, AADNonInteractiveUserSignInLogs
| where IPAddress in ("124.198.132.33", "45.83.31.44", "192.159.99.172", "185.238.2.195", "167.148.41.96", "2400:8d60:2::1:841d:5dd")
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, ResultType, ConditionalAccessStatus
N3 · Build A fingerprint and Device Registration Service sign-ins
union SigninLogs, AADNonInteractiveUserSignInLogs
| where (AppId in ("d3590ed6-52b3-4102-aeff-aad2292ab01c", "29d9ed98-a469-4536-ade2-f981bc1d605e")
and (UserAgent =~ "node" or UserAgent == "Mozilla/5.0")
and ResourceId in ("00000002-0000-0ff1-ce00-000000000000", "01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9"))
or ResourceId == "01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9"
| summarize Events = count(), Addresses = make_set(IPAddress, 20), First = min(TimeGenerated), Last = max(TimeGenerated)
by UserPrincipalName, UserAgent, AppDisplayName, ResourceDisplayName
| order by Events desc
N4 · Rogue Entra device registration by name
AuditLogs
| where OperationName in ("Add device", "Register device")
| extend DeviceName = tostring(TargetResources[0].displayName),
ActorIp = tostring(InitiatedBy.user.ipAddress),
Actor = tostring(InitiatedBy.user.userPrincipalName)
| where DeviceName matches regex @"^(DESKTOP-|Device-|Work Pc[- ]?)[0-9A-Fa-f]{8}$"
| project TimeGenerated, OperationName, DeviceName, Actor, ActorIp, Result
N5 · MFA methods wiped then re-registered within three hours
AuditLogs
| where OperationName in ("User deleted security info", "User registered security info", "Admin deleted security info", "Admin registered security info")
| extend Target = tostring(TargetResources[0].userPrincipalName)
| summarize Deleted = countif(OperationName has "deleted"), Registered = countif(OperationName has "registered"),
First = min(TimeGenerated), Last = max(TimeGenerated) by Target
| where Deleted >= 1 and Registered >= 1 and (Last - First) <= 3h
// Verify the exact OperationName strings in your tenant before trusting a zero result.
N6 · Hidden or junk-named inbox rules
OfficeActivity
| where OfficeWorkload == "Exchange" and Operation in ("New-InboxRule", "Set-InboxRule", "UpdateInboxRules")
| extend P = tostring(Parameters)
| where P has_any ("RSS Feeds", "RSS Subscriptions", "Conversation History")
or P matches regex @"""Name""\s*:\s*""Name""\s*,\s*""Value""\s*:\s*""([^""]{1,3}|[.,;]{2,})"""
or (P has_any ("payroll", "direct deposit", "bonus") and P has_any ("DeleteMessage", "MarkAsRead", "MoveToFolder"))
| project TimeGenerated, UserId, Operation, ClientIP, P
N7 · Device-code sign-ins and BEC spam (posture checks)
// Device-code flow sign-ins (restrict this with Conditional Access if the count is small) union SigninLogs, AADNonInteractiveUserSignInLogs | where AuthenticationProtocol =~ "deviceCode" | summarize Events = count(), Addresses = make_set(IPAddress, 20) by UserPrincipalName, AppDisplayName | order by Events desc // Mailboxes that rated high risk at sign-in (Huntress: Entra ID Protection rated the spam mailbox high risk) // SigninLogs | where RiskLevelDuringSignIn == "high" | project TimeGenerated, UserPrincipalName, IPAddress, RiskState
N8 · Exchange Online PowerShell: enumerate rules, including hidden ones
Connect-ExchangeOnline
Get-Mailbox -ResultSize Unlimited | ForEach-Object {
Get-InboxRule -Mailbox $_.PrimarySmtpAddress -IncludeHidden |
Where-Object { $_.Name -match '^(\.|,\.|\.{8}|s{1,3}|iT|d)$' -or $_.MoveToFolder -match 'RSS|Conversation History' -or $_.Description -match 'payroll|direct deposit|bonus' } |
Select-Object MailboxOwnerId, Name, Enabled, MoveToFolder, DeleteMessage, MarkAsRead, Description
}
N9 · Microsoft Graph PowerShell: rogue devices and authenticator methods
Connect-MgGraph -Scopes "Device.Read.All","UserAuthenticationMethod.Read.All","User.Read.All"
# Devices named like the rogue pattern, with registration time (confirm with the owner before deleting)
Get-MgDevice -All | Where-Object { $_.DisplayName -match '^(DESKTOP-|Device-|Work Pc[- ]?)[0-9A-Fa-f]{8}$' } |
Select-Object DisplayName, Id, DeviceId, RegistrationDateTime, TrustType, OperatingSystem, ApproximateLastSignInDateTime
# Authenticator methods on one suspect user
Get-MgUserAuthenticationMicrosoftAuthenticatorMethod -UserId "jdoe@contoso.example" | Select-Object Id, DisplayName, DeviceTag, CreatedDateTime
CrowdStrike LogScale CQL Hunt Queries
repo=all over 7 days: widen the time picker to 90 days or more for this campaign (2026-07-14 start).Looks for: Any Entra sign-in (interactive or non-interactive) whose source address sits in 193.26.115.0/24, the block Huntress traced 200,000+ failed and 497 successful token-replay sign-ins to (2026-07-14 to 2026-09-22). Six addresses in the block were used one after another, so match the range, not one address. A hit with a success result means a stolen session was accepted; treat the user as compromised until sessions, MFA methods and devices are audited.
// HUNT: Entra sign-ins from the 193.26.115.0/24 token-replay range // MITRE: T1550.001, T1078.004 | CONF: high FP: low COST: low // REQUIRES: Microsoft Entra ID sign-in logs (interactive AND non-interactive) ingested into Next-Gen SIEM // ⚠ validate in tenant: Entra sign-in field names and the repo are parser-dependent. Names below follow crowdstrike-logscale-v3 (Entra: appId, appDisplayName, userPrincipalName, ipAddress, conditionalAccessStatus, clientAppUsed). Confirm on one real row before trusting a zero result. // FALSE POSITIVES: low. A legitimate customer of the same hosting provider is possible but rare; confirm business ownership of the address // LOOKBACK: 90d or more (campaign ran 2026-07-14 to 2026-09-22); set with the time picker cidr(ipAddress, subnet="193.26.115.0/24") | sort(@timestamp, order=asc, limit=200) | table([@timestamp, userPrincipalName, appDisplayName, appId, clientAppUsed, ipAddress, conditionalAccessStatus, "status.errorCode"], limit=200)
Looks for: Sign-ins from the other addresses Huntress named: three 1337 Services addresses outside the /24 (one continued a hijacked session, two failed to replay a revoked one), the two AiTM relay addresses, and the Interserver address that created the payroll-hiding inbox rules. The Interserver address is ordinary shared hosting, so it is a pivot, not a verdict. FP: the Interserver address can be shared by unrelated customers. Drop it from the list if your tenant sees legitimate traffic from it.
// HUNT: Entra sign-ins from named replay hosts and AiTM relays outside 193.26.115.0/24 // MITRE: T1550.001, T1557, T1090.003 | CONF: medium FP: medium COST: low // REQUIRES: Microsoft Entra ID sign-in logs (interactive AND non-interactive) ingested into Next-Gen SIEM // ⚠ validate in tenant: Entra sign-in field names and the repo are parser-dependent. Names below follow crowdstrike-logscale-v3 (Entra: appId, appDisplayName, userPrincipalName, ipAddress, conditionalAccessStatus, clientAppUsed). Confirm on one real row before trusting a zero result. // ⚠ validate in tenant: IPv6 text may be logged in expanded form; if the relay IPv6 never matches, compare against the expanded spelling // FALSE POSITIVES: 167.148.41.96 is shared Interserver hosting; the rest are rented or provider addresses reported by one vendor // TUNING: remove 167.148.41.96 from values if it produces unrelated hits; treat hits on it as a pivot to the mailbox, not as proof in(ipAddress, values=["124.198.132.33", "45.83.31.44", "192.159.99.172", "185.238.2.195", "167.148.41.96", "2400:8d60:2::1:841d:5dd"]) | sort(@timestamp, order=asc, limit=200) | table([@timestamp, userPrincipalName, appDisplayName, clientAppUsed, ipAddress, conditionalAccessStatus, "status.errorCode"], limit=200)
Looks for: Huntress's scripted-replay tool (Build A) signed in with the Microsoft Office or Microsoft Authentication Broker client IDs, a user agent of node or a bare Mozilla/5.0, and targeted Exchange Online or the Device Registration Service. The client IDs are Microsoft constants, not campaign indicators; the discriminator is the combination. Build B (browser session replay) uses a current Chrome or Edge user agent and is not caught here: use Q1 and Q2 for it. FP: legitimate Node.js automation that uses the Office client ID against Exchange Online, and some management agents that send a bare Mozilla/5.0. Huntress says to expect some automation.
// HUNT: Scripted token replay (Build A): Office or Broker client with node or bare Mozilla/5.0 user agent against Exchange Online or Device Registration // MITRE: T1550.001, T1098.005 | CONF: medium FP: medium COST: medium // REQUIRES: Microsoft Entra ID sign-in logs, NON-interactive stream included, ingested into Next-Gen SIEM // ⚠ validate in tenant: Entra sign-in field names and the repo are parser-dependent. Names below follow crowdstrike-logscale-v3 (Entra: appId, appDisplayName, userPrincipalName, ipAddress, conditionalAccessStatus, clientAppUsed). Confirm on one real row before trusting a zero result. // ⚠ validate in tenant: userAgent and resourceId are Graph signIn property names; your parser may spell them differently // Client IDs: d3590ed6... = Microsoft Office, 29d9ed98... = Microsoft Authentication Broker (Microsoft constants, not IOCs) // Resources: 00000002-0000-0ff1-ce00-000000000000 = Exchange Online, 01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9 = Device Registration Service // FALSE POSITIVES: sanctioned Node.js automation and management agents using the Office client ID // TUNING: exclude known automation egress with | !cidr(ipAddress, subnet=["192.0.2.0/24"]) and replace the documentation range with your own; focus on rows that touch user mailboxes or the Device Registration Service in(appId, values=["d3590ed6-52b3-4102-aeff-aad2292ab01c", "29d9ed98-a469-4536-ade2-f981bc1d605e"]) | userAgent = /^(node|Mozilla.5\.0)$/i | in(resourceId, values=["00000002-0000-0ff1-ce00-000000000000", "01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9"]) | groupBy([userPrincipalName, ipAddress, userAgent, appDisplayName], function=[count(as=Events), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)], limit=500) | sort(Events, order=desc, limit=200)
Looks for: Sign-ins to the Device Registration Service (resource 01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9), the endpoint Huntress saw used to join 48 rogue computers. Group by user and source address and read the addresses: hosting or VPN space joining a device is the signal, an office or Autopilot range is not. FP: normal device joins from corporate, home or Autopilot provisioning networks.
// HUNT: Device Registration Service sign-ins grouped by user and source address // MITRE: T1098.005 | CONF: medium FP: medium COST: low // REQUIRES: Microsoft Entra ID sign-in logs, NON-interactive stream included, ingested into Next-Gen SIEM // ⚠ validate in tenant: Entra sign-in field names and the repo are parser-dependent. Names below follow crowdstrike-logscale-v3 (Entra: appId, appDisplayName, userPrincipalName, ipAddress, conditionalAccessStatus, clientAppUsed). Confirm on one real row before trusting a zero result. // FALSE POSITIVES: ordinary device joins and Autopilot provisioning // TUNING: replace the documentation range below with your corporate egress and provisioning ranges (REPLACE_WITH_CORPORATE_EGRESS_RANGES) resourceId = "01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9" | !cidr(ipAddress, subnet=["192.0.2.0/24"]) | groupBy([userPrincipalName, ipAddress], function=[count(as=Events), count(appId, distinct=true, as=Clients), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)], limit=500) | sort(Events, order=desc, limit=200)
Looks for: Entra audit events that add or register a device whose display name is DESKTOP-, Device- or Work Pc followed by exactly eight hex characters. Huntress counted 48 distinct DESKTOP- plus eight uppercase hex names (for example DESKTOP-54521F46); Windows' own out-of-box default is DESKTOP- plus seven alphanumerics. Huntress warns the pattern is rare but not unique to this campaign, so confirm with the device owner before deleting.
// HUNT: Entra device registered with a DESKTOP-/Device-/Work Pc plus eight hex character name // MITRE: T1098.005 | CONF: high FP: low COST: low // REQUIRES: Microsoft Entra ID audit logs ingested into Next-Gen SIEM // ⚠ validate in tenant: Entra audit and Exchange audit field names are parser-dependent and arrays may be flattened differently. Confirm on one real row. // ⚠ validate in tenant: activityDisplayName values and the flattened targetResources/initiatedBy field names // FALSE POSITIVES: low. The pattern is rare but not unique to this campaign (Huntress); confirm with the device owner before deleting // LOOKBACK: 90d or more; set with the time picker activityDisplayName = /^(Add device|Register device)$/i | "targetResources.displayName" = /^(DESKTOP-|Device-|Work Pc[- ]?)[0-9A-F]{8}$/i | table([@timestamp, activityDisplayName, "targetResources.displayName", "initiatedBy.user.userPrincipalName", "initiatedBy.user.ipAddress", result], limit=200)
Looks for: Per user, a security-info deletion followed by a registration inside a three-hour span. In 19 organizations Huntress saw existing MFA methods removed and a new one registered within three hours of the first campaign sign-in; in the second wave the wipe ran through the security-info registration service and the new authenticator appeared minutes later. FP: helpdesk MFA resets and phone replacement. Check who initiated the change and whether the user asked for it.
// HUNT: Security info deleted and re-registered for the same user within 3 hours (MFA wipe and replace) // MITRE: T1556.006, T1098.005 | CONF: high FP: medium COST: medium // REQUIRES: Microsoft Entra ID audit logs ingested into Next-Gen SIEM // ⚠ validate in tenant: Entra audit and Exchange audit field names are parser-dependent and arrays may be flattened differently. Confirm on one real row. // ⚠ validate in tenant: the activity strings "User deleted security info" / "User registered security info" (and the Admin variants) come from the Entra Authentication Methods audit service, not from the campaign reports // FALSE POSITIVES: helpdesk-driven MFA resets, phone replacement, new-hire registration // TUNING: exclude helpdesk admin accounts by initiator, and require the change to follow a sign-in from an unfamiliar address (join with Q1/Q2/Q4 output) activityDisplayName = /^(User|Admin) (deleted|registered) security info$/i | case { activityDisplayName = /deleted/i | Wiped := 1 | Added := 0; * | Wiped := 0 | Added := 1; } | groupBy(["targetResources.userPrincipalName"], function=[sum(Wiped, as=WipedCount), sum(Added, as=AddedCount), min(@timestamp, as=FirstTs), max(@timestamp, as=LastTs)], limit=500) | WipedCount >= 1 | AddedCount >= 1 | SpanMs := LastTs - FirstTs | test(SpanMs <= 10800000) | sort(LastTs, order=desc, limit=200)
Looks for: Exchange audit events that create or change an inbox rule whose name is exactly one of the eight names Huntress listed. The names are case sensitive here: iT is the attacker's, IT may not be. None of these rules were created from the 1337 Services network; each came from a different rented or VPN address, so a source-address hunt will miss them. FP: a real user who names a rule s or d. Confirm with the mailbox owner and check the rule action and destination folder (Q8).
// HUNT: Inbox rule created or changed with a campaign rule name (., sss, ss, iT, ,., ........, s, d) // MITRE: T1564.008 | CONF: high FP: medium COST: medium // REQUIRES: Exchange Online audit events (Unified Audit Log) ingested into Next-Gen SIEM; mailbox auditing on // ⚠ validate in tenant: Entra audit and Exchange audit field names are parser-dependent and arrays may be flattened differently. Confirm on one real row. // ⚠ validate in tenant: this reads the rule name out of the raw JSON (Parameters: {"Name":"Name","Value":"..."}). UpdateInboxRules events from the Outlook client may carry the name elsewhere // FALSE POSITIVES: a real user with a one-letter rule name // TUNING: confirm the rule action (MarkAsRead, DeleteMessage, MoveToFolder) and destination before escalating; pair with Q8 // ⚠ validate in tenant: the normalized names event.action, user.email and source.ip (CrowdStrike Parsing Standard, ECS-aligned) depend on your tenant's M365 parser and connector version. Confirm them against your own ingested rows before trusting a zero result. // UAL raw record names if your parser preserves them instead: Operation (event.action), UserId (user.email), ClientIP (source.ip) event.action = /^(New-InboxRule|Set-InboxRule|UpdateInboxRules)$/i | @rawstring = /"Name"\s*:\s*"Name"\s*,\s*"Value"\s*:\s*"(?<ruleName>[^"]+)"/ | ruleName = /^(\.|sss|ss|iT|,\.|\.{8}|s|d)$/ | table([@timestamp, user.email, event.action, ruleName, source.ip], limit=200)
Looks for: Inbox rule events whose raw record names one of the three obscure folders Huntress saw mail hidden in. These are built-in system folders that users rarely target on purpose, which is what makes the destination a better discriminator than the rule name.
// HUNT: Inbox rule event referencing RSS Feeds, RSS Subscriptions or Conversation History // MITRE: T1564.008 | CONF: high FP: low COST: medium // REQUIRES: Exchange Online audit events (Unified Audit Log) ingested into Next-Gen SIEM; mailbox auditing on // ⚠ validate in tenant: Entra audit and Exchange audit field names are parser-dependent and arrays may be flattened differently. Confirm on one real row. // FALSE POSITIVES: low. A user moving mail to RSS Feeds deliberately, or an RSS-reader rule // ⚠ validate in tenant: the normalized names event.action, user.email and source.ip (CrowdStrike Parsing Standard, ECS-aligned) depend on your tenant's M365 parser and connector version. Confirm them against your own ingested rows before trusting a zero result. // UAL raw record names if your parser preserves them instead: Operation (event.action), UserId (user.email), ClientIP (source.ip) event.action = /^(New-InboxRule|Set-InboxRule|UpdateInboxRules)$/i | @rawstring = /(?<hiddenFolder>RSS Feeds|RSS Subscriptions|Conversation History)/i | table([@timestamp, user.email, event.action, hiddenFolder, source.ip], limit=200)
Looks for: The generic form of Q7: any inbox rule named with one to three characters or with only dots, commas and semicolons. Huntress told responders to look for rules with one to three character or punctuation-only names, because the operator varied them. FP: users who label rules with a letter or two. Expect a handful per thousand mailboxes.
// HUNT: Inbox rule with a 1-3 character or punctuation-only name // MITRE: T1564.008 | CONF: medium FP: medium COST: medium // REQUIRES: Exchange Online audit events (Unified Audit Log) ingested into Next-Gen SIEM; mailbox auditing on // ⚠ validate in tenant: Entra audit and Exchange audit field names are parser-dependent and arrays may be flattened differently. Confirm on one real row. // FALSE POSITIVES: users with terse rule names // TUNING: exclude mailboxes that created the rule from a managed office address; keep rules that also delete, mark as read or move to a system folder // ⚠ validate in tenant: the normalized names event.action, user.email and source.ip (CrowdStrike Parsing Standard, ECS-aligned) depend on your tenant's M365 parser and connector version. Confirm them against your own ingested rows before trusting a zero result. // UAL raw record names if your parser preserves them instead: Operation (event.action), UserId (user.email), ClientIP (source.ip) event.action = /^(New-InboxRule|Set-InboxRule|UpdateInboxRules)$/i | @rawstring = /"Name"\s*:\s*"Name"\s*,\s*"Value"\s*:\s*"(?<ruleName>[^"]{1,3}|[.,;]{2,})"/ | table([@timestamp, user.email, event.action, ruleName, source.ip], limit=200)
Looks for: Rules whose criteria mention payroll, direct deposit, bank or bonus and whose actions delete, mark read or move mail. Huntress saw rule s catch exactly those words, then rule d hide an outside accountant's replies. The attacker's goal was a payroll-diversion style business email compromise. FP: finance and HR staff who legitimately file payroll mail into folders.
// HUNT: Inbox rule matching payroll / direct deposit / bank / bonus that also deletes, marks read or moves mail // MITRE: T1564.008, T1114.003 | CONF: medium FP: medium COST: medium // REQUIRES: Exchange Online audit events (Unified Audit Log) ingested into Next-Gen SIEM; mailbox auditing on // ⚠ validate in tenant: Entra audit and Exchange audit field names are parser-dependent and arrays may be flattened differently. Confirm on one real row. // FALSE POSITIVES: finance and HR users filing payroll mail // TUNING: exclude known payroll and finance mailboxes by user.email, or require a hide action plus an unfamiliar source.ip // ⚠ validate in tenant: the normalized names event.action, user.email and source.ip (CrowdStrike Parsing Standard, ECS-aligned) depend on your tenant's M365 parser and connector version. Confirm them against your own ingested rows before trusting a zero result. // UAL raw record names if your parser preserves them instead: Operation (event.action), UserId (user.email), ClientIP (source.ip) event.action = /^(New-InboxRule|Set-InboxRule|UpdateInboxRules)$/i | @rawstring = /(payroll|direct deposit|bonus|\bbank\b)/i | @rawstring = /(DeleteMessage|MarkAsRead|MoveToFolder|SoftDeleteMessage)/ | table([@timestamp, user.email, event.action, source.ip], limit=200)
Looks for: Security-info registration events whose raw record carries the label iPhone 13 or the full device token Huntress listed for the attacker's authenticator. The token is the high-confidence half; the label alone matches every legitimate iPhone 13 owner, so correlate with Q6, Q4 and an unfamiliar sign-in before acting. FP: any user with an iPhone 13 enrolling the Authenticator app normally.
// HUNT: Authenticator method registered as "iPhone 13" or with the reported attacker device token // MITRE: T1556.006, T1098.005 | CONF: medium FP: medium COST: low // REQUIRES: Microsoft Entra ID audit logs ingested into Next-Gen SIEM // ⚠ validate in tenant: Entra audit and Exchange audit field names are parser-dependent and arrays may be flattened differently. Confirm on one real row. // FALSE POSITIVES: genuine iPhone 13 owners enrolling the Authenticator app // TUNING: the apns2- token is the high-confidence match; treat the label alone as a pivot and compare with the user's known devices activityDisplayName = /security info/i | @rawstring = /(iPhone 13|apns2-d8a3fedb1a3cd1287ca47c6851a49e6052ab6116b43666e674765ca892d798ce)/ | table([@timestamp, activityDisplayName, "targetResources.userPrincipalName", "initiatedBy.user.ipAddress", result], limit=200)
Looks for: Error 53003 means Conditional Access stopped a sign-in AFTER the credential was accepted, so the credential or token is valid; Huntress says to treat it as a sign of compromise, not a save. Error 50133 (SsoArtifactRevoked) is what a replay of an already-revoked session returns; the operator retried one such session three more times. Rank users by distinct source addresses, and prioritize by credential validity, not by spray volume. FP: 53003 is common for legitimate users who trip a location or device policy. 50133 follows ordinary password changes.
// HUNT: Sign-in errors 53003 (blocked by Conditional Access, credential valid) and 50133 (revoked session replay) // MITRE: T1550.001, T1078.004 | CONF: medium FP: medium COST: medium // REQUIRES: Microsoft Entra ID sign-in logs (interactive AND non-interactive) ingested into Next-Gen SIEM // ⚠ validate in tenant: Entra sign-in field names and the repo are parser-dependent. Names below follow crowdstrike-logscale-v3 (Entra: appId, appDisplayName, userPrincipalName, ipAddress, conditionalAccessStatus, clientAppUsed). Confirm on one real row before trusting a zero result. // ⚠ validate in tenant: the error code may be a string or a number, and its field name varies (status.errorCode, ResultType, resultType) // FALSE POSITIVES: users tripping location or device Conditional Access; password-change aftermath // TUNING: raise SrcIPs to 3 or more, or require one of the addresses to be unfamiliar or in the Q1/Q2 lists "status.errorCode" = /^(53003|50133)$/ | groupBy([userPrincipalName], function=[count(as=Events), count(ipAddress, distinct=true, as=SrcIPs), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)], limit=500) | SrcIPs >= 2 | sort(Events, order=desc, limit=200)
Looks for: Endpoint telemetry helps here only for the victim side. A managed host that connects to the AiTM relay address 185.238.2.195 (or the IPv6 relay) was used to open the fake sign-in page. The relay's hostname was not published, so an address match is the only anchor, and a clean result does not clear the host. Connections to the 193.26.115.0/24 replay range from a managed host are unexpected for the same reason.
// HUNT: Managed host connecting to AiTM relay or token-replay addresses // MITRE: T1557, T1566.002 | CONF: medium FP: low COST: low // REQUIRES: Falcon sensor network telemetry (NetworkConnectIP4, NetworkConnectIP6) // FALSE POSITIVES: low. The addresses are single-vendor, perishable and hosting-provider space; a relay address can be reassigned // LOOKBACK: 90d or more; set with the time picker #event_simpleName = /^(NetworkConnectIP4|NetworkConnectIP6)$/ | (RemoteAddressIP4 = /^(185\.238\.2\.195|193\.26\.115\.[0-9]{1,3}|124\.198\.132\.33|45\.83\.31\.44|192\.159\.99\.172)$/ or RemoteAddressIP6 = "2400:8d60:2::1:841d:5dd") | table([@timestamp, ComputerName, aid, ContextBaseFileName, RemoteAddressIP4, RemoteAddressIP6, RemotePort], limit=200)
Looks for: DNS lookups for exy.sh, rdp.sh and starkrdp.io from managed hosts. These are commercial virtual-server rental services, not malware domains: the sources link the operator's hosting company to them, and The Hacker News says the services were operating again after the January 2025 seizure. A lookup shows someone on the host reached the service, which is an enrichment signal at best. It must never be a block. FP: any admin or developer who legitimately rents a server from these services.
// HUNT: DNS lookups for exy.sh, rdp.sh, starkrdp.io (commercial RDP rental services, enrichment only) // MITRE: T1090.003 | CONF: low FP: medium COST: low // REQUIRES: Falcon sensor DNS telemetry (DnsRequest) // FALSE POSITIVES: legitimate use of these rental services // TUNING: hunt and enrich only, never block; these are legitimate-service domains. Exclude known admin hosts by ComputerName #event_simpleName = DnsRequest | DomainName = /(^|\.)(exy\.sh|rdp\.sh|starkrdp\.io)$/i | groupBy([ComputerName, aid, DomainName], function=[count(as=Lookups), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen)], limit=200) | sort(Lookups, order=desc, limit=200)
Looks for: The 48 rogue devices were fake directory entries, so no sensor runs on them. This query answers the opposite question: which REAL managed hosts already carry a DESKTOP- plus eight uppercase hex name? Use the list to separate genuine machines from rogue Entra registrations when you audit the device list (Q5), so a legitimate host is not deleted by mistake. FP: imaged or renamed fleets that legitimately use this pattern.
// HUNT: Managed hosts with a DESKTOP- plus eight uppercase hex name (reconcile against rogue Entra devices) // MITRE: T1098.005 | CONF: low FP: medium COST: medium // REQUIRES: Falcon sensor process telemetry (ProcessRollup2) // FALSE POSITIVES: fleets imaged or renamed to this pattern // TUNING: this is an inventory reconciliation aid, not a detection; compare its output with Q5 and the Entra device list before deleting anything #event_simpleName = ProcessRollup2 | ComputerName = /^DESKTOP-[0-9A-F]{8}$/ | groupBy([ComputerName, aid], function=[count(as=Processes), max(@timestamp, as=LastSeen)], limit=500) | sort(LastSeen, order=desc, limit=200)
CrowdStrike Custom IOA Recommendations
| IOA name | Logic | Promotes | Benign Exclusions | Deployment Path |
|---|---|---|---|---|
| Entra sign-in from token-replay range | Any sign-in from 193.26.115.0/24 or the named hosts | Promote Q1, Q2 | None expected; confirm tenant ownership first | Next-Gen SIEM scheduled search, alert on match (Detect) |
| Scripted token replay fingerprint | Office or Broker client, node or bare Mozilla/5.0, Exchange Online or Device Registration | Promote Q3 | Known Node.js automation egress | Next-Gen SIEM scheduled search after a 14-day baseline (Detect) |
| Rogue Entra device registered | Name DESKTOP-/Device-/Work Pc plus eight hex | Promote Q5 | Confirmed fleet naming | Next-Gen SIEM scheduled search, alert on match (Detect) |
| MFA wiped then re-registered | Security info deleted and registered for one user inside 3 hours | Promote Q6 | Helpdesk reset accounts | Next-Gen SIEM correlation rule (Detect) |
| Campaign inbox rules | Exact rule names or hidden-folder destinations | Promote Q7, Q8 | None for the folders; terse-name users for Q7 | Next-Gen SIEM scheduled search, alert on match (Detect) |
| Endpoint connection to relay or replay address | Network connection rule on remote IPv4/IPv6 addresses from section 10 | Promote Q13 | None expected | Custom IOA (network connection), action Detect; revisit only with a second source |
Machine-Readable IOC Appendix
Grouped IOC Quick-Copy
Every atomic value here appears verbatim (defanged forms refanged) in a saved source snapshot. Everything ships detect: no prevent row exists because no indicator has two independent sources, and exy.sh and rdp.sh are legitimate commercial services. Expirations are three months out because the operator rotated addresses across four phases.
type,value,action,severity,expiration,description,tags ipv4,193.26.115.221,detect,high,2027-01-26,Build A token replay and rogue device join host (Phase 1),technique:token-spraying ipv4,193.26.115.199,detect,high,2027-01-26,Build B browser session replay host (Phase 1),technique:token-spraying ipv4,193.26.115.16,detect,high,2027-01-26,Build A token replay and device-code phishing host (Phase 2),technique:token-spraying ipv4,193.26.115.21,detect,high,2027-01-26,Build A token replay host with bare Mozilla/5.0 (Phase 2),technique:token-spraying ipv4,193.26.115.72,detect,high,2027-01-26,Build A token replay host (Phase 3 burst),technique:token-spraying ipv4,193.26.115.210,detect,high,2027-01-26,Build B session replay host (Phase 4),technique:token-spraying ipv4,124.198.132.33,detect,medium,2027-01-26,1337 Services address that continued a hijacked session,technique:token-spraying ipv4,45.83.31.44,detect,medium,2027-01-26,1337 Services address with a failed replay of a revoked session,technique:token-spraying ipv4,192.159.99.172,detect,medium,2027-01-26,1337 Services address with a failed replay of a revoked session,technique:token-spraying ipv4,185.238.2.195,detect,medium,2027-01-26,AiTM relay that captured the 2026-07-21 session,technique:token-spraying ipv6,2400:8d60:2::1:841d:5dd,detect,medium,2027-01-26,AiTM relay at Evoxt Sdn. Bhd. (Phase 4),technique:token-spraying domain,exy.sh,detect,low,2027-01-26,Commercial RDP rental service (enrichment only; legitimate-service domain; never prevent),technique:token-spraying domain,rdp.sh,detect,low,2027-01-26,Commercial RDP rental service (enrichment only; legitimate-service domain; never prevent),technique:token-spraying
# Not importable as single-address IOCs. Use cidr() in Q1 or a REPORT-ONLY Conditional Access named location (playbook 2). 193.26.115.0/24 # 1337 Services GmbH block; six replay hosts used one after another (Huntress). Shared provider space: detect, do not block without a tenant decision AS210558 # 1337 Services GmbH, Hamburg (Huntress, Krebs on Security). Enrichment only # Hosts outside the /24 are in the CSV (124.198.132.33, 45.83.31.44, 192.159.99.172). # Pivot only (shared hosting, not in the CSV): 167.148.41.96 # Interserver; created payroll-hiding inbox rules in Phase 3
# Microsoft first-party identifiers Huntress lists for hunting. These are Microsoft constants, NOT campaign indicators. d3590ed6-52b3-4102-aeff-aad2292ab01c Microsoft Office (Build A client) 29d9ed98-a469-4536-ade2-f981bc1d605e Microsoft Authentication Broker (Build A client) 4765445b-32c6-49b0-83e6-1d93765276ca OfficeHome (Build B client) 00000003-0000-0000-c000-000000000000 Microsoft Graph (resource) 00000002-0000-0ff1-ce00-000000000000 Office 365 Exchange Online (resource) 00000003-0000-0ff1-ce00-000000000000 SharePoint Online (resource) 01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9 Device Registration Service (resource) # Request types: OAuth2:Token (Build A), Login:reprocess and OAuth2:Authorize (Build B), Cmsi:Cmsi (device-code and broker) # Errors: 53003 BlockedByConditionalAccess (valid credential), 50133 SsoArtifactRevoked (replay of a dead session) # User agents: node; bare Mozilla/5.0; current Chrome or Edge on Windows (Build B, looks normal) # Rogue device names: DESKTOP- + 8 hex (DESKTOP-54521F46, DESKTOP-BAF88EF4, DESKTOP-CA85907C); also Device- or Work Pc + 8 hex # Authenticator label: iPhone 13 | token: apns2-d8a3fedb1a3cd1287ca47c6851a49e6052ab6116b43666e674765ca892d798ce # Inbox rule names (case sensitive): . sss ss iT ,. ........ s d # Rule destination folders: RSS Feeds, RSS Subscriptions, Conversation History
Sign-in from 193.26.115.0/24 or a named replay host, success or failure (Q1, Q2) Office or Authentication Broker client with a node or bare Mozilla/5.0 user agent against Exchange Online or the Device Registration Service (Q3) Device Registration Service sign-in from hosting or VPN address space (Q4) Entra device added or registered as DESKTOP-/Device-/Work Pc plus eight hex characters (Q5) Security info deleted and a new method registered for one user within three hours (Q6) Authenticator method labelled "iPhone 13" or carrying the reported apns2- token (Q11) Inbox rule with a campaign name, a 1-3 character or punctuation-only name, a hidden system-folder destination, or payroll/bank criteria that also hide mail (Q7-Q10) Conditional Access block 53003 followed by a sign-in from a second address, or a 50133 replay of a revoked session (Q12) Managed host connecting to a relay address (Q13) Inbox rules never come from 1337 Services space: do not use the source address of the rule to scope Q7-Q10
# Containment and audit checklist (per suspect account). Order matters. 1. Preserve evidence first: export sign-in, audit and Exchange audit logs for the user (90+ days). 2. Revoke sessions: Revoke-MgUserSignInSession. A password reset alone does not invalidate the access token. 3. Reset the password (after the revoke). 4. Audit and remove unrecognised MFA methods: Get-MgUserAuthenticationMethod. A reset alone leaves attacker methods in place. 5. Audit Entra devices registered by or for the user; look for DESKTOP-/Device-/Work Pc plus 8 hex. Confirm before deleting; the pattern is rare but not unique. 6. Enumerate inbox rules including hidden ones: Get-InboxRule -IncludeHidden. Remove campaign rules; export first. 7. Review sent items for payroll mail to outside parties and bulk outside spam. 8. Review enterprise applications and consent grants (Huntress advises; no consent abuse was reported in this campaign). 9. Confirm Conditional Access: MFA or block for All Users, All Cloud Apps, All Client App types; device-code flow restricted. 10. Treat 53003 blocks as a sign of a valid credential, not as a save.
Detection Validation Gates
| Gate | Check | Pass Criterion |
|---|---|---|
| Telemetry ready | Entra sign-in logs, interactive AND non-interactive (Microsoft Sentinel: SigninLogs, AADNonInteractiveUserSignInLogs), Entra audit logs, and Exchange audit with mailbox auditing, all in Next-Gen SIEM or Sentinel; endpoint network and DNS telemetry (CrowdStrike: NetworkConnectIP4/6, DnsRequest. SentinelOne: IP Connect, DNS Resolved. Defender: DeviceNetworkEvents, DeviceEvents) | Rows visible for each stream; retention of 90 days or more reaches 2026-07-14 |
| Parser validated | Run each Q1-Q12 against one known row; confirm ipAddress, appId, userAgent, resourceId, the error-code field, activityDisplayName, the flattened targetResources fields, and the normalized event.action, user.email and source.ip (raw UAL names Operation, UserId, ClientIP if your parser keeps them) resolve; these names depend on your M365 parser and connector version | Fields populated; the audit activity strings in Q5 and Q6 match your tenant's spelling |
| Benign baseline | Inventory Node.js automation, device-join and Autopilot egress ranges, helpdesk MFA reset accounts, finance mailboxes with payroll rules | Exclusions built before promoting Q3, Q4, Q6, Q7, Q9, Q10 |
| Positive test | Create a test rule named sss in a lab mailbox; register a device named DESKTOP-00000000 in a lab tenant; delete and re-add a test user's authenticator | Q7, Q5 and Q6 fire |
| Promotion | Promote Q1, Q2, Q5, Q6, Q8 to Detect alerts; keep Q3, Q4, Q7, Q9-Q12 as tuned hunts; Q13 as Detect IOA; Q14 and Q15 stay hunts | Alerts have documented exclusions; nothing promotes to prevent |
Hardening: Tiered & Deployable
Blocking the replay range does not fix this. The operator rotated across six addresses in four phases, used addresses outside the /24, and captures sessions at relays on other providers. The durable fix makes a stolen post-MFA session less useful and makes the persistence artifacts hard to plant. Authority: MITRE M-numbers plus Microsoft Learn (snapshots 07 to 09) and Huntress; items without a snapshot citation are marked best-practice.
- Conditional Access: require MFA (or block) for All Users, All Cloud Apps and All Client App types, unconditionally. M1032 / M1036. Huntress guidance; closes the app, group and location scoping gaps.
- Block or tightly scope the device-code flow with a Conditional Access authentication-flows policy. M1032 / M1036. Huntress and Microsoft Learn both say to restrict it; Microsoft says to get as close to a unilateral block as possible. Start report-only. Playbook 1.
- Enable mailbox auditing and export all four Entra/Exchange streams (interactive and non-interactive sign-ins, audit, Unified Audit Log) for 90+ days. M1047. Without them Q1-Q12 are blind. Best-practice, no formal benchmark.
- Run the account containment sequence on every user with a campaign-range sign-in: revoke sessions, then reset, then remove attacker MFA methods, devices and rules. M1018. Huntress and Microsoft Learn. Playbook 3.
- Token Protection Conditional Access policy for Exchange Online, SharePoint Online and Teams on supported Windows, macOS and iOS native clients. M1032. Microsoft Learn: binds sign-in sessions to the device. Limit: browser-based support is narrow, so it does not stop Build B browser replay. Report-only first. Playbook 5.
- Require a compliant or hybrid-joined device for Microsoft 365 access. M1032 / M1036. A rogue Entra-joined device is not Intune-compliant, and a stolen token replayed from a rented host is not on a managed device. Best-practice; CIS Microsoft 365 Foundations / CIS Entra ID Benchmark cover the family. Pilot, because it affects BYOD.
- Require MFA to register security information and to join or register devices; restrict who may join devices. M1032 / M1018. Targets the MFA-replacement and rogue-device persistence steps. Best-practice; Microsoft Entra device settings and the Register security information user action.
- Entra ID Protection risk-based Conditional Access (sign-in risk and user risk). M1036. Entra ID Protection rated the spam mailbox high risk and flagged four replay addresses, per Huntress.
- Alert on hidden-folder and junk-name inbox rules and on MFA-wipe-and-replace (Q6-Q9). M1047.
- Disable standard-user consent to new applications. M1018. Huntress advises it; no consent abuse was reported in this campaign. Playbook 6.
- Phishing-resistant MFA (FIDO2 or passkeys) through Conditional Access authentication strengths for all users, starting with finance, payroll and admins. M1032. An AiTM relay cannot replay a FIDO2 assertion bound to the real origin. Best-practice.
- Move to Continuous Access Evaluation and shorter-lived sessions for high-value roles. M1036. Microsoft Learn notes access tokens last one hour by default and that CAE lets admins revoke CAE-capable sessions faster.
- Outbound spam and external-forwarding limits in Exchange Online. M1021 / M1047. One mailbox sent spam to hundreds of outside recipients before it was flagged. Best-practice.
- Standing credential-validity triage, not spray-volume triage. M1047. Huntress: ranking tenants by failed attempts points responders at the four organizations where Conditional Access already worked.
Deployable Playbooks
REPLACE_WITH_ placeholder. Test in a lab tenant first.1 · Restrict the device-code flow (report-only, then enforce)
Prerequisites: Conditional Access Administrator; Microsoft.Graph PowerShell; break-glass account object ID. Reboot required: No. Corroboration: Huntress and Microsoft Learn (snapshot 07), two independent sources, so enforcing after the report-only review is justified. Rollback: set the policy state to disabled or delete it (commands below).
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess","Policy.Read.All"
$body = @{
displayName = "CA - Block device code flow (report-only)"
state = "enabledForReportingButNotEnforced"
conditions = @{
users = @{ includeUsers = @("All"); excludeUsers = @("REPLACE_WITH_BREAK_GLASS_OBJECT_ID") }
applications = @{ includeApplications = @("All") }
authenticationFlows = @{ transferMethods = "deviceCodeFlow" }
}
grantControls = @{ operator = "OR"; builtInControls = @("block") }
}
$p = New-MgIdentityConditionalAccessPolicy -BodyParameter $body
# Review Entra sign-in logs > Conditional Access for 7 to 14 days, add exclusions for any documented device-code use, then:
# Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $p.Id -State "enabled"
# ROLLBACK:
# Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $p.Id -State "disabled"
# Remove-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $p.Id
# Confirm the transferMethods value in the portal (Conditions > Authentication flows) if the call is rejected.
2 · Watch the replay range in report-only (named location, no block)
Prerequisites: Conditional Access Administrator. Reboot required: No. Posture: report-only only. The /24 is shared provider space and the indicator is single-source, so this is a detection aid. Flip to enforced only after you confirm no legitimate user or vendor sign-ins from the range for 14+ days. Rollback: delete the policy, then the named location.
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess"
$loc = New-MgIdentityConditionalAccessNamedLocation -BodyParameter @{
"@odata.type" = "#microsoft.graph.ipNamedLocation"
displayName = "Token replay range 193.26.115.0/24 (watch)"
isTrusted = $false
ipRanges = @(@{ "@odata.type" = "#microsoft.graph.iPv4CidrRange"; cidrAddress = "193.26.115.0/24" })
}
$p = New-MgIdentityConditionalAccessPolicy -BodyParameter @{
displayName = "CA - Block sign-in from token replay range (report-only)"
state = "enabledForReportingButNotEnforced"
conditions = @{
users = @{ includeUsers = @("All"); excludeUsers = @("REPLACE_WITH_BREAK_GLASS_OBJECT_ID") }
applications = @{ includeApplications = @("All") }
locations = @{ includeLocations = @($loc.Id) }
}
grantControls = @{ operator = "OR"; builtInControls = @("block") }
}
# ROLLBACK:
# Remove-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $p.Id
# Remove-MgIdentityConditionalAccessNamedLocation -NamedLocationId $loc.Id
3 · Contain one suspect account (revoke, reset, remove attacker methods and devices)
Prerequisites: User Administrator and Authentication Administrator (or Privileged Authentication Administrator for admin accounts); Cloud Device Administrator; export evidence first. Reboot required: No. Rollback: none for the session revoke (the user simply signs in again) and none for the password reset; method and device removal is not reversible, so export the lists first and confirm with the owner.
Connect-MgGraph -Scopes "User.ReadWrite.All","UserAuthenticationMethod.ReadWrite.All","Device.ReadWrite.All"
$upn = "REPLACE_WITH_SUSPECT_UPN"
# 1. Evidence
Get-MgUserAuthenticationMethod -UserId $upn | Export-Clixml ".\methods-$upn.xml"
Get-MgUserRegisteredDevice -UserId $upn | Export-Clixml ".\devices-$upn.xml"
# 2. Revoke sessions FIRST (a reset alone leaves the access token valid until expiry; Huntress cites about 90 minutes)
Revoke-MgUserSignInSession -UserId $upn
# 3. Force a password change
Update-MgUser -UserId $upn -PasswordProfile @{ ForceChangePasswordNextSignIn = $true }
# 4. Review authenticator methods; remove any the user does not recognise
Get-MgUserAuthenticationMicrosoftAuthenticatorMethod -UserId $upn | Select-Object Id, DisplayName, DeviceTag, CreatedDateTime
# Remove-MgUserAuthenticationMicrosoftAuthenticatorMethod -UserId $upn -MicrosoftAuthenticatorAuthenticationMethodId "<id from the list>"
# 5. Review registered devices; delete only rogue ones confirmed with the owner
# Remove-MgDevice -DeviceId "<object id of the rogue device>"
4 · Audit and remove hidden inbox rules
Prerequisites: Exchange Online PowerShell module; Exchange administrator or Organization Management. Reboot required: No. Rollback: the first command exports every rule; a removed rule can be recreated from the CSV with New-InboxRule.
Connect-ExchangeOnline
$mbx = "REPLACE_WITH_SUSPECT_MAILBOX"
# 1. Export all rules, including hidden ones, before touching anything
Get-InboxRule -Mailbox $mbx -IncludeHidden |
Select-Object Name, Enabled, Priority, Description, MoveToFolder, DeleteMessage, MarkAsRead, SubjectOrBodyContainsWords, From |
Export-Csv ".\rules-$mbx.csv" -NoTypeInformation
# 2. Review, then remove only the campaign rules
Get-InboxRule -Mailbox $mbx -IncludeHidden | Where-Object { $_.Name -match '^(\.|,\.|\.{8}|s{1,3}|iT|d)$' -or $_.MoveToFolder -match 'RSS|Conversation History' } |
Select-Object Name, MoveToFolder, Description
# Remove-InboxRule -Mailbox $mbx -Identity "<rule name or Identity>" -Confirm:$false
5 · Token Protection session control for Exchange Online and SharePoint Online (report-only)
Prerequisites: Microsoft Entra ID P1 or higher; supported Windows native clients (Microsoft Learn, snapshot 08). Reboot required: No. Limit: browser sessions are not broadly covered, so this does not stop Build B. Rollback: disable or delete the policy.
Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess"
$p = New-MgIdentityConditionalAccessPolicy -BodyParameter @{
displayName = "CA - Token protection, Exchange and SharePoint Online, Windows native clients (report-only)"
state = "enabledForReportingButNotEnforced"
conditions = @{
users = @{ includeUsers = @("All"); excludeUsers = @("REPLACE_WITH_BREAK_GLASS_OBJECT_ID") }
applications = @{ includeApplications = @("00000002-0000-0ff1-ce00-000000000000","00000003-0000-0ff1-ce00-000000000000") }
clientAppTypes = @("mobileAppsAndDesktopClients")
platforms = @{ includePlatforms = @("windows") }
}
sessionControls = @{ secureSignInSession = @{ isEnabled = $true } }
}
# Read the sign-in log Conditional Access tab for failures from unsupported clients before enforcing.
# ROLLBACK: Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $p.Id -State "disabled"
6 · Disable standard-user consent to new applications
Prerequisites: Global Administrator or Privileged Role Administrator. Reboot required: No. Corroboration: Huntress only, and no consent abuse was reported in this campaign, so treat as best-practice hardening. Rollback: restore the value you record in step 1.
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
# 1. Record the current value
(Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions.PermissionGrantPoliciesAssigned
# 2. Remove user consent (users then request admin approval)
Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ PermissionGrantPoliciesAssigned = @() }
# ROLLBACK: Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ PermissionGrantPoliciesAssigned = @("<value recorded in step 1>") }
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Revoke sessions for every user with a sign-in from the campaign range, THEN reset the password (a reset alone leaves the access token valid until it expires). Do not wait for the endpoint: there is no malware on it. Preserve sign-in, audit and Exchange logs before changing anything | SOC L2 / IAM | Revocation records; exported logs |
| Triage | Run Q1-Q12 for the 90 days from 2026-07-14. Rank accounts by credential validity (success after a campaign-range sign-in, 53003 blocks, persistence artifacts), not by failed-attempt volume. Check whether the first sign-in preceded the MFA wipe by under three hours | IR | Compromised-account list; timeline per account |
| Eradicate | Remove attacker authenticator methods, rogue devices (confirm with the owner) and hidden inbox rules; review sent items for payroll mail and outside spam; reset passwords after the revoke. Playbooks 3 and 4 | IR + IAM + Messaging | Removed-method, device and rule exports |
| Recover | Re-register MFA with the user in a trusted setting; watch for the operator returning from a new address (they did, after one to two weeks of quiet, in every phase). Notify any outside party who received a payroll email and any recipient of spam | IR + IT + Finance | Re-registration tickets; notification log |
| Harden | Section 12 immediate and near-term controls; promote the detections in section 9; stage the report-only policies in playbooks 1, 2 and 5 | Detection Eng + IAM | Policy state; alert inventory |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | Coverage |
|---|---|---|---|---|
| T1557 | AiTM relay capture (token theft) | Q13 | Q13 IOA | GAP capture is outside endpoint telemetry; relay hostnames unpublished, address-only match on the victim host |
| T1566.002 | Device-code and social-engineering token capture | - | - | Partial native N7 only; posture check, not campaign-specific |
| T1550.001 | Token replay from the /24 and named hosts | Q1, Q2 | Q1, Q2 | Good needs ingestion; hosts rotate, so a clean result does not clear the tenant |
| T1550.001 | Build A scripted replay fingerprint | Q3 | Q3 | Partial needs ingestion; Build B uses a normal browser user agent and is address-only |
| T1550.001 | Valid credential stopped (53003) or revoked replay (50133) | Q12 | - | Partial needs ingestion; noisy, a triage aid |
| T1098.005 | Rogue Entra device registration | Q4, Q5, Q15 | Q5 | Good needs Entra audit ingestion; name pattern is rare but not unique; endpoint sensor cannot see a fake device |
| T1556.006 | MFA wiped and replaced; "iPhone 13" authenticator | Q6, Q11 | Q6 | Good needs Entra audit ingestion; activity strings must be validated |
| T1564.008 | Hidden or junk-named inbox rules | Q7, Q8, Q9, Q10 | Q7, Q8 | Good needs Exchange audit ingestion and mailbox auditing; REST-API and Outlook-client rules may be recorded differently |
| T1090.003 | Rented hosting, VDI tunnels, consumer VPNs | Q1, Q2, Q14 | - | Partial enrichment only; VPN mailbox-action addresses are not published as a list |
| - | BEC email and outside spam sent from the mailbox | - | - | GAP no CQL built; hunt Exchange send activity natively and use Entra ID Protection risk |
| - | New enterprise applications or consent (Huntress advice) | - | - | Not observed no consent abuse was reported in this campaign; hardening in playbook 6 only |
Validation gates: confirm all streams are ingested and retained back to 2026-07-14; validate each parser field and the Q5/Q6 activity strings on a real row; build the benign baseline; run the lab positive tests; promote Q1, Q2, Q5, Q6 and Q8 first. Indicators are perishable and single-source: a clean Q1, Q2 or Q13 result does not clear a tenant, whereas a hit on Q5, Q6 or Q8 is high value regardless of source address.
Hunt Summary Ticket
TITLE: Hunt: Microsoft 365 Token Spraying (stolen-session replay), Huntress report 2026-10-22
SEVERITY: High (account takeover and BEC set-up; 79 organizations, 497 successful sign-ins; 48 rogue Entra devices)
SCOPE: Microsoft Entra ID sign-ins, Entra devices and MFA methods, Exchange Online inbox rules. Campaign unnamed; operator unattributed.
HYPOTHESIS: An AiTM relay captures post-MFA sessions; within minutes the token is replayed from 193.26.115.0/24 (1337 Services GmbH);
the operator then wipes and replaces MFA, registers fake Entra devices and plants hidden inbox rules to divert payroll mail.
QUERIES RUN: Native N1-N9 (sign-in, audit, Exchange, PowerShell)
CQL Q1 replay range | Q2 named hosts and relays | Q3 Build A fingerprint | Q4 Device Registration sign-ins |
Q5 rogue device names | Q6 MFA wipe and replace | Q7 campaign rule names | Q8 hidden folders | Q9 junk-name rules |
Q10 payroll/bank rules | Q11 iPhone 13 authenticator | Q12 53003/50133 | Q13 endpoint egress | Q14 RDP-rental DNS | Q15 hostname reconcile
DO FIRST: Confirm Entra sign-in (incl. non-interactive), Entra audit and Exchange audit ingestion; then Q1, Q5, Q6, Q8 over 90+ days.
FINDINGS: <pending analyst execution>
GAPS: Token theft at the relay and the BEC email itself are outside endpoint telemetry; relay hostnames unpublished; replay
hosts rotate; all indicators are single-source (Huntress) so nothing ships prevent; Falcon Identity Protection coverage unverified.
ACTIONS: Revoke sessions before reset; remove attacker MFA methods, rogue devices and hidden rules; block or restrict device-code flow
(report-only first); MFA for all users, apps and client types; stage Token Protection and compliant-device policies.
OWNER: HuntPack
VERSION: v0.1 - 2026-10-26
Changelog
References
HuntPack v0.1 · M365 Token Spraying and Token Replay (Huntress) · Generated 2026-10-26 · Defensive use only, no offensive code. This is an identity and cloud-session hunt: the evidence lives in Entra sign-in, Entra audit and Exchange audit logs. The campaign is unnamed and the operator unattributed in every source. Validate parser field names in your tenant before promoting any query.