ARTEX: AI agents cut attack time to hours

$ cat blog/ai-agent-attack-speed-artex.md
✓ incident
✓ AI security
✓ agents
✓ authentication
6 min read
$ 

Microsoft says a China state-aligned group it tracks as Storm-0940 used ARTEX, an agentic AI penetration testing framework, to automate reconnaissance, password spraying and OAuth application abuse against hundreds of organizations. In some cases the group reached privileged cloud access in under three hours from first targeting. The Hacker News covered the report in October 2026.

What happened

According to Microsoft, as reported by The Hacker News, ARTEX is "a Go-based agentic AI penetration testing framework." It was made closed-source after it was abused against South Korean financial institutions, and several groups now weaponize it.

Microsoft attributes the compromise of the ConnectWise Cloud Backup application to Storm-0940, which used ARTEX for scanning and reconnaissance, password spraying and OAuth application abuse against downstream victims. The reported sequence:

  • From October 10 to 14, 2026, from 144.172.64[.]99, Microsoft saw "an automated cross-tenant password-spray campaign targeting 327 accounts across eight organizations," with more than 151,000 failed logons and no successful sign-in in that window.
  • On October 15, the agents gained access to the identities of multiple ConnectWise cloud apps and used them to authenticate to customer environments.
  • The automated activity included password spraying at up to 2,900 sign-in attempts per minute, sign-ins to more than 2,250 tenants, Microsoft Graph and directory operations, app credential modifications for persistence, and identity, group and privileged role changes.
  • In some cases the group reached privileged cloud access in under three hours from initial targeting. Microsoft says it has targeted "hundreds of organizations across the world" since September 2026.

The article does not list specific recommendations from Microsoft. The checklist further down is ours.

Why it worked

None of the individual techniques is new. Password spraying, abusing a trusted vendor's app identity, and adding credentials to an app for persistence are all well known. What changed is speed and scale: an agent can try thousands of sign-ins a minute across thousands of tenants and chain the next step as soon as one works.

That shifts the maths for defenders in two ways:

  • Anything an attacker can automate, assume they will. Sign-in endpoints without per-account and per-source limits, app identities with broad directory roles, and credentials nobody audits all become cheap to exploit.
  • The time between a flaw shipping and being exploited shrinks. If an attacker can go from first probe to privileged access in hours, a weakness found in next quarter's review is found too late. It has to be caught before it ships, in the pull request.

A small example of the first point: a sign-in handler with no throttle lets an automated spray run at full speed. This is an illustrative example, not any product's code:

# Illustrative only: no limit on failed sign-ins (vulnerable)
def sign_in(email: str, password: str, ip: str) -> Session | None:
    user = users.find(email)
    if user and verify_password(user.password_hash, password):
        return create_session(user)
    return None
# Illustrative only: limit failures per account and per source (fixed)
def sign_in(email: str, password: str, ip: str) -> Session | None:
    if limiter.over(f"fail:acct:{email.lower()}", limit=10, window_s=900) \
            or limiter.over(f"fail:ip:{ip}", limit=100, window_s=900):
        raise TooManyAttempts()  # same response whether or not the account exists
    user = users.find(email)
    if user and verify_password(user.password_hash, password):
        return create_session(user)
    limiter.hit(f"fail:acct:{email.lower()}")
    limiter.hit(f"fail:ip:{ip}")
    return None

A spray tries one or two passwords per account across many accounts, so the per-source limit matters as much as the per-account one. Limits don't replace MFA; they buy time and create a signal you can alert on.

Your own agents are identities too

A related argument from a Hacker News expert insight (October 19) is worth applying to your own systems: an AI agent's permissions should be bounded by the least trustworthy input it processes. An agent that reads issues, web pages or email can be steered by whoever wrote them. If it also holds a broad cloud role or can run any shell command, that author effectively holds them too.

In practice this means scoping agent tools the way you'd scope an app identity. An illustrative Claude Code settings file, not from any real project:

{
  "permissions": {
    "allow": ["Read", "Grep", "Bash(npm test)", "Bash(npm run lint)"],
    "deny": ["Read(./.env*)", "Bash(curl:*)", "Bash(git push:*)"],
    "defaultMode": "default"
  }
}

The risky version allows Bash(*), sets bypassPermissions, and keeps a cloud admin key in the environment the agent runs in.

attack, as reported where OpenRouting checks 1 Agent-driven scanning and reconnot a runtime monitor 2 Automated cross-tenant password spray✓ Auth agent: sign-in limits 3 Takes over vendor cloud app identitiesvendor app, not your repo 4 Signs in to customer tenantsidentity provider logs 5 Adds app credentials for persistence✓ Blast radius: least privilege 6 Privileged access in under 3 hours✓ PR review before release
The attack as reported, and where an OpenRouting check applies. Illustrative.

What to do now

  1. Require MFA for every account, and prefer phishing-resistant methods (passkeys, FIDO2 keys) for admins.
  2. Put rate limits and lockouts on sign-in, per account and per source, and alert on spray patterns: many accounts, few passwords each.
  3. Audit OAuth and enterprise app credentials. Remove unused secrets and certificates, and alert whenever a credential is added to an app.
  4. Give app identities the least privilege they need. Avoid directory-wide or privileged roles for integrations, and review which ones can change roles or groups.
  5. Review third-party and vendor app access to your tenant. A vendor's app identity is part of your attack surface; know what each can do and remove what you no longer use.
  6. Apply the same rules to your AI agents: scoped tools, no long-lived admin credentials in their environment, human approval for writes, and the least trusted input in mind.

How OpenRouting helps

OpenRouting can't see into your identity provider. What it can do is shorten the time between a weakness being written and being found, and check the code and agent configuration that decide how much an attacker gets.

  • PR review checks the files a pull request changes, so a new sign-in route without limits, or a new over-broad token, is reviewed before it ships rather than after.
  • The authentication agent reviews sign-in, session and token code for missing throttling, weak checks and tokens handled in plain text.
  • The agent setup review reads AI-agent setups in your repository (CLAUDE.md, AGENTS.md, .claude settings, hooks, MCP configs) for over-broad tools, auto-approve hooks and secrets in agent config.
  • The blast radius agent looks at how far a single compromised credential or identity could reach, which is the question this incident turns on.
  • API collection rules flag endpoints with no authentication in imported Postman, OpenAPI or HAR files, without sending traffic.

AI triage, by Claude, via Amazon Bedrock, adds a verdict, a plain-English explanation and a suggested fix to each finding, with secret values redacted before any AI call. For more on scoping agents, see least privilege for AI agents.

What it won't catch

  • OpenRouting does not detect password spraying or monitor your identity provider or tenant. It is not a runtime monitoring or SIEM product; use your identity provider's sign-in logs and alerts for that.
  • It can't secure a vendor's application. If a vendor's app identity is compromised, the fix is on their side and in what you allow that app to do.
  • It reviews your repositories and, passively, your websites. Your tenant's app registrations and role assignments live outside both.
  • Findings appear when a scan runs, from the dashboard or on a pull request review.

Sources

Questions

What happened in the Storm-0940 ARTEX campaign?

Microsoft says Storm-0940, a China state-aligned group, used ARTEX, an agentic AI penetration testing framework, to automate reconnaissance, password spraying and OAuth application abuse against hundreds of organizations. Microsoft attributes the compromise of the ConnectWise Cloud Backup application to the group, which used the identities of ConnectWise cloud apps to authenticate to customer environments.

How fast did the ARTEX attacks reach privileged access?

In some cases the group reached privileged cloud access in under three hours from initial targeting. The automated activity included password spraying at up to 2,900 sign-in attempts per minute and sign-ins to more than 2,250 tenants.

How do I defend against automated password spraying and OAuth app abuse?

Require MFA for every account, with phishing-resistant methods for admins, and rate-limit sign-in per account and per source. Audit OAuth and enterprise app credentials, alert whenever a credential is added to an app, and give app identities the least privilege they need. This checklist is ours; the article lists no specific recommendations from Microsoft.

Can OpenRouting detect password spraying?

No. OpenRouting does not detect password spraying or monitor your identity provider or tenant. It reviews the code and AI-agent setups in your repositories, such as a new sign-in route without limits, before they ship.

Keep reading