Storm-0940 is a Chinese threat actor active since at least 2021. Its earlier operations combined password spraying, compromised credentials, and attacks against internet-facing systems. In October 2026, Microsoft attributed the ConnectWise cloud application compromise to the group and identified ARTEX as an automation framework used in its attacks.
Three reports show ARTEX adoption during September–October 2026. They describe separate campaigns, not three confirmed Storm-0940 operations.
| Campaign | Period | Public finding |
|---|---|---|
| South Korean financial sector | Late September–early October | ThreatLabz reported theft of approximately 67,000 individuals’ records. |
| Japanese companies | Reported October 19 | GMO identified attack infrastructure targeting approximately 20 companies. |
| ConnectWise customer tenants | October | Microsoft attributed application OAuth abuse and targeting of more than 2,250 tenants to Storm-0940. |
What ARTEX is, and how it works
ARTEX is an open-source, multi-agent penetration-testing framework: a Go backend with a Next.js console. It is not a model – it is the harness that drives one. It is model-agnostic (observed running on Claude, GPT, DeepSeek, GLM and Grok). The public repo went private on 8 October 2026, but forks still circulate.
The design is a planner/worker loop over a shared graph. The human sets a target; the agents chain the rest together on their own.
The framework maintains two linked graphs:
- Asset graph: domains, IP addresses, services, applications, and endpoints.
- Exploration graph: goals, proposed actions, observations, and findings, linked to the assets they concern.
A planner reads this state and creates work items. Concurrent workers claim items, run tools, and record results. New observations trigger another planning round. HTTP traffic recording provides evidence for findings.
How ARTEX operates: a multi-agent loop where the model, not the human, selects each action after every scan.
ARTEX supplies orchestration; access still depends on exploitable software, usable credentials, and granted permissions.
The ConnectWise compromise: OAuth abuse at machine speed
Microsoft attributes the ConnectWise compromise to Storm-0940 and dates the Microsoft 365 activity from September 2026.
Phase 1 — the loud failure (no foothold)
From 10–14 October, ARTEX-driven infrastructure at 144.172.64.99 sprayed 327 accounts across eight organizations: 151,000+ failed logons, peaking at 2,900 per minute, with account lockouts making up roughly 98% of the activity. Microsoft observed no successful sign-in, no token issuance, and no Graph call.
Phase 2 — the quiet success (initial access)
Initial access did not come from a guessed password.
Storm-0940 used compromised ConnectWise application credentials (the client secrets/certificates) of ConnectWise’s multi-tenant OAuth apps (Cloud Backup and related Teams/Graph apps) to authenticate to customer tenants through permissions already granted to those applications. The original exact credential-exposure mechanism remains unknown.
A multi-tenant app is registered once at the vendor. When a customer installs it, Entra provisions a service principal inside the customer tenant carrying the app’s admin-consented Graph permissions. That app normally authenticates per-tenant via the OAuth2 client-credentials flow: present app ID + secret to the token endpoint, receive an app-only access token — no user, no interactive sign-in, no MFA. With the stolen secrets, ARTEX requested those tokens against every tenant that had the app installed, riding consent the customers had already granted.
ARTEX reached privileged cloud access in under three hours in multiple cases. Inside each tenant, the agents used Graph for reconnaissance, added their own credentials to applications for persistence that survives rotation of the original ConnectWise secret, had one ConnectWise management app self-assign Global Administrator, modified users, groups and privileged roles, and in Azure performed bulk Key Vault secret reads.
Incident timeline
| Date/time | Observation |
|---|---|
| September | Storm-0940 adopts ARTEX; M365 targeting begins across aerospace, defense, finance, government and tech. |
| October 10–14 | Cross-tenant password spray from 144.172.64.99. 151k+ failed logons, 98% lockouts, zero successful sign-ins. |
| October 15 | Downstream M365 access via ConnectWise apps surfaces publicly — same 144.172.64.x range. |
| October 16 | ConnectWise advisory: unauthorized access associated with Cloud Backup. |
| October 21 | Microsoft attributes the campaign to Storm-0940 and names ARTEX. |
Affected applications and indicators
| Artifact | Value | Type |
|---|---|---|
| ConnectWise Cloud Backup | 506e08f9-6e9a-4da4-942b-a099a03644ae | OAuth app ID |
| ConnectWise – MS Teams | 24205314-2b89-4e07-a2a4-579e898a5640 | OAuth app ID |
| ConnectWise – MS Graph | 0de14c77-b9b0-4137-9360-34878a929827 | OAuth app ID |
| Operator infrastructure | 144.172.64.99 / .60 / .128 | IP |
| Automation user agents | python-urllib/3.12, python-requests/2.31.0, curl/7.88.1, azure-toolkit/1.0 | User-Agent |
ATT&CK mapping
| Stage | Technique | Observable |
|---|---|---|
| Reconnaissance | T1595 | ARTEX scans cloud resources |
| Initial Access | T1195 · T1078.004 · T1199 | App-only auth via trusted multi-tenant app |
| Credential Access | T1528 · T1555.006 | Stolen app secrets; bulk Key Vault reads |
| Privilege Escalation | T1098.003 | App self-assigns Global Administrator |
| Persistence | T1098.001 | New secret added to service principal |
| Discovery | T1087.004 · T1580 | Directory + cloud enumeration via Graph |
The numbers
| Figure | Source | Attempts vs. successes |
|---|---|---|
| 2,250+ tenants | Microsoft | Sign-in attempts |
| ~1,000 Entra accounts | Huntress | Showed suspicious activity |
| 300+ organizations | Huntress | Successful intrusions |
Where SlashID fits
The failed spray and the successful path point at the same conclusion: this was an identity attack. No malware, no exploit – a trusted non-human identity, a vendor’s OAuth app, holding standing admin-consented access, abused at machine speed. That is the layer SlashID operates on.
| Attack step | SlashID control | Signal / action |
|---|---|---|
| Over-permissioned, pre-consented vendor app | Access Graph / AI Identity Governance | Discovers the OAuth grant, maps scopes → reachable resources → authorizing human; flags the toxic standing grant |
| App-only auth from attacker infra | ITDR behavioral detection | Service-principal sign-in from unusual ISP/IP |
| App self-assigns Global Admin | Cross-identity detection | Privileged role assigned to a service principal → high-severity alert |
| Attacker adds app credentials | NHI posture + detection | New secret/cert on a service principal → surfaces the persistence backdoor |
| Bulk Key Vault reads · lateral movement | Graph-correlated anomaly detection | Anomalous NHI behavior |
| Containment | Automated remediation | Revoke tokens/sessions, rotate creds, remove grants/owners/roles, enforce MFA |
ARTEX made the attack faster, but the path into customer tenants was already there: trusted ConnectWise applications with existing permissions. Once the attacker had the vendor credentials, it could request access across thousands of tenants without a user signing in.
Discover it, baseline it, and be able to revoke it in minutes — before the agent reaches the 2,250th tenant.
Do you know which OAuth-connected apps hold admin consent in your tenant?
Storm-0940 never needed a user to sign in. It took a stolen third-party app secret and rode permissions customers had granted long ago. SlashID maps every OAuth grant to what it can reach, flags service principals that sign in from new infrastructure or assign themselves Global Admin, and can revoke the access before a trusted app becomes a persistent admin foothold for attackers.
References
[1] Microsoft Security Blog, October 21, 2026. https://www.microsoft.com/en-us/security/blog/2026/10/21/storm-0940-uses-artex-agentic-ai-connectwise-supply-chain-compromise/
[2] Huntress Blog. https://www.huntress.com/blog/vendor-credential-enables-unauthorized-access
