Huntress tracked ten weeks (Jul 14 – Sep 22) of AiTM relays and device-code phishing capturing post-MFA M365 sessions, then replaying tokens from 193.26.115.0/24 (1337 Services GmbH, AS210558): 497 successful sign-ins at 79 organizations against 200,000+ failures. Endgame: hidden inbox rules for BEC and payroll fraud — finance-sector shaped.
Verdict: nothing to patch — this is a Conditional Access and identity-hygiene test, and the campaign graded it. MFA alone did not stop it: the tokens were stolen after MFA. What did stop it, outright, was Conditional Access enforced for all users, all cloud apps, all client types — four orgs blocked the replay phase cold. The work is two-sided: hunt the four behavior families (replayed sign-ins, rogue device registrations, MFA wipe-and-replace, hidden inbox rules) and close the CA gaps the campaign selects for. Expect the pattern to recur from new infrastructure — a near-identical June campaign ran from different hosting (LSHIY LLC), so key detections on behavior, not the IP block.
Phase 1: AiTM relays and device-code phishing harvest live sessions. Phase 2: token replay from the 1337 Services /24 — sign-ins show OAuth2:Token / Cmsi:Cmsi with node or bare Mozilla/5.0 user agents from hosting space. Phase 3: persistence — at least 48 rogue Entra devices named like defaults (DESKTOP- + 8 uppercase hex), MFA methods wiped and re-registered within minutes in 19 orgs, hidden inbox rules with 1–3-character or punctuation-only names moving mail to RSS Feeds / Conversation History, created via the Outlook REST API. Two sign-in error codes are themselves signal: AADSTS53003 (CA blocked — but the credential is valid) and AADSTS50133 (replay of a revoked session).
| Category | Source | Status |
|---|---|---|
| Platform | Microsoft Entra | Partial — Entra ID Protection "verified threat actor IP" detections flagged campaign IPs; no dedicated Microsoft advisory found. CA enforcement is the platform-native block |
| Primary research | Huntress | Full hunt package — six published behavior hunts (sign-in shape, device naming, MFA churn, inbox rules, error-code triage) rendering live below; response guidance: revoke sessions, reset passwords, audit devices/MFA/rules |
| Precedent | Huntress — June campaign | Pattern recurs — near-identical June 2026 campaign from LSHIY LLC infrastructure; detections keyed to the /24 alone will miss the next wave |
| VM / EDR | — | Not applicable — no vulnerability; identity-log hunting and CA policy are the whole game |
DESKTOP-[hex8] device registrations, MFA wipe→re-register inside minutes, short/punctuation-named inbox rulesHuntress's published hunts (live above) come first; these are the generic-SIEM translations over exported Entra logs:
# sign-in logs
auth_protocol IN (OAuth2:Token, Cmsi:Cmsi)
AND user_agent IN ('node') OR user_agent = 'Mozilla/5.0' # bare, no platform suffix
AND src.asn in (hosting providers) → review mailbox + Device Registration Service touches
# audit logs — persistence pair
device registered matching /^(DESKTOP-|Device-|Work Pc)[0-9A-F]{8}$/ from hosting IPs
MFA method deleted AND new method added for same user within 10 minutes
# exchange — hidden rules
inbox rule name matches /^.{1,3}$/ or punctuation-only
AND (moves to 'RSS Feeds'/'RSS Subscriptions'/'Conversation History'
OR conditions contain payroll/banking terms)
Splunk (index=*) / Elastic ES|QL (FROM logs-*) — Splunk over the Azure Monitor diagnostic export shape (category / properties.*) for Entra and the UAL shape for Exchange; Elastic over its Azure Logs and Microsoft 365 integrations (azure.signinlogs.*, azure.auditlogs.*, o365.audit.*); tune to your add-on or integration:
# replayed sessions: successful sign-ins with the campaign's client shape, Jul 14 → now
index=* category=SignInLogs earliest=-110d@d properties.status.errorCode=0
(properties.userAgent IN ("node","Mozilla/5.0") OR properties.authenticationProtocol IN ("deviceCode"))
| rename properties.userPrincipalName as user, properties.ipAddress as src, properties.userAgent as ua,
properties.autonomousSystemNumber as asn, properties.appDisplayName as app
| eval campaign_net=if(cidrmatch("193.26.115.0/24", src), "1337 Services /24", null())
| stats count values(ua) as uas values(asn) as asns values(app) as apps values(campaign_net) as net min(_time) as first by user src
# rogue device registrations from hosting space
index=* category=AuditLogs earliest=-110d@d operationName="Register device"
| rename properties.initiatedBy.user.userPrincipalName as user, properties.initiatedBy.user.ipAddress as src
| regex properties.targetResources{}.displayName="^(DESKTOP-|Device-|Work Pc)[0-9A-F]{8}$"
| stats count values(properties.targetResources{}.displayName) as devices by user src
# MFA wipe-and-replace inside 10 minutes
index=* category=AuditLogs earliest=-110d@d operationName IN ("User deleted security info","User registered security info")
| rename properties.targetResources{}.userPrincipalName as user
| transaction user maxspan=10m | where eventcount>=2 AND searchmatch("deleted") AND searchmatch("registered")
| table _time user duration
# hidden inbox rules (Exchange / UAL)
index=* earliest=-110d@d Operation IN ("New-InboxRule","Set-InboxRule")
| spath Parameters{} output=p | mvexpand p
| rex field=p "\"Name\":\"(?<pname>[^\"]+)\",\"Value\":\"(?<pval>[^\"]*)\""
| eval rule_name=if(pname="Name", pval, null()), folder=if(pname="MoveToFolder", pval, null())
| stats values(rule_name) as names values(folder) as folders by UserId ClientIP
| where match(names, "^.{1,3}$") OR match(names, "^[[:punct:]]+$") OR match(folders, "RSS|Conversation History")
// replayed sessions: successful sign-ins with the campaign's client shape, Jul 14 → now
FROM logs-*
| WHERE @timestamp > NOW() - 110 days AND event.dataset == "azure.signinlogs"
| WHERE azure.signinlogs.properties.status.error_code == 0
AND (user_agent.original IN ("node", "Mozilla/5.0") OR azure.signinlogs.properties.authentication_protocol == "deviceCode")
| EVAL campaign_net = CASE(CIDR_MATCH(source.ip, "193.26.115.0/24"), "1337 Services /24")
| STATS count = COUNT(*), uas = VALUES(user_agent.original), asns = VALUES(source.as.number), apps = VALUES(azure.signinlogs.properties.app_display_name),
net = VALUES(campaign_net), first = MIN(@timestamp) BY user.name, source.ip
// rogue device registrations from hosting space
FROM logs-*
| WHERE @timestamp > NOW() - 110 days AND event.dataset == "azure.auditlogs" AND azure.auditlogs.operation_name == "Register device"
| WHERE azure.auditlogs.properties.target_resources.0.display_name RLIKE "(DESKTOP-|Device-|Work Pc)[0-9A-F]{8}"
| STATS count = COUNT(*), devices = VALUES(azure.auditlogs.properties.target_resources.0.display_name)
BY azure.auditlogs.properties.initiated_by.user.userPrincipalName, azure.auditlogs.properties.initiated_by.user.ipAddress
// MFA wipe-and-replace inside 10 minutes (fixed 10-minute buckets stand in for Splunk's transaction window)
FROM logs-*
| WHERE @timestamp > NOW() - 110 days AND event.dataset == "azure.auditlogs"
AND azure.auditlogs.operation_name IN ("User deleted security info", "User registered security info")
| EVAL user = azure.auditlogs.properties.target_resources.0.user_principal_name, window = DATE_TRUNC(10 minutes, @timestamp)
| STATS ops = COUNT_DISTINCT(azure.auditlogs.operation_name), first = MIN(@timestamp), last = MAX(@timestamp) BY user, window
| WHERE ops == 2
| KEEP first, last, user
// hidden inbox rules (Exchange / UAL)
FROM logs-*
| WHERE @timestamp > NOW() - 110 days AND event.dataset == "o365.audit" AND event.action IN ("New-InboxRule", "Set-InboxRule")
| EVAL rule_name = o365.audit.Parameters.Name, folder = o365.audit.Parameters.MoveToFolder
| WHERE rule_name RLIKE ".{1,3}" OR rule_name RLIKE "[^A-Za-z0-9 ]+" OR folder RLIKE ".*(RSS|Conversation History).*"
| STATS names = VALUES(rule_name), folders = VALUES(folder) BY user.id, source.ip
Huntress — token replay/spray (primary) · Huntress — June 2026 LSHIY campaign · Help Net Security · Infosecurity Magazine