Microsoft has published the first detailed look at an Azure environment hit by JADEPUFFER, the actor Sysdig reported in July 2026 as the first documented agentic ransomware operation. Microsoft tracks the activity as Storm-3168. Two compromised service principals in a single tenant enumerated the environment, then deleted Azure resources at a speed no human operator would work at.
The destructive sequence lasted about seven minutes. In that window the attacker attempted to delete more than 100 storage accounts and succeeded against most of them, then went looking for storage keys thirty minutes later. Two controls held where identity controls did not: Azure resource locks and storage account-level deletion protection. And the credential that opened the door was a client secret posted in plaintext in a public GitHub issue, edited afterwards, and never revoked. Redacting a secret does not invalidate it.
Key takeaways:
- Deleting 100+ storage accounts took about seven minutes. Recovery controls that do not depend on the compromised identity are what limited the damage.
- Azure SQL deletions all failed because the attacker used an unsupported API version. That was luck, not a control.
- The Service Principal had broad Contributor rights, so least privilege alone would not have stopped the storage deletions.
- Microsoft’s own framing is “automated or scripted execution.” There is no evidence in the public report of a language model in the loop.
What happened, in order
Two service principals in the same tenant were compromised. One was used for reconnaissance. The other handled destruction and credential collection. Both used the same network fingerprint and the user agent python-requests/2.34.2.
| Phase | What occurred |
|---|---|
| Discovery | One principal enumerated VMs, subscriptions, resource groups and resources for roughly 15 hours 30 minutes with 300+ successful reads. |
| Fast follow-up | Roughly 90 minutes later, the second principal enumerated VMs and resource groups across two subscriptions in five seconds. |
| Credential hunting | 16 hours on, it enumerated Azure App Service configuration stores and unsuccessfully probed for Azure OpenSearch resources. |
| Destruction | Less than one second after a failed ListKeys call on a non-existent storage account, it began deleting. 150+ destructive or credential-collection operations in 35 minutes; the destructive sequence itself ran about seven minutes. |
| Key collection | About 30 minutes after the last destructive action, it inventoried storage accounts and sent 30+ successful ListKeys requests, including for Site Recovery related accounts. |
Why the seven minutes matter more than the AI label
Inside the destructive sequence the attacker issued 100+ storage account deletion attempts and most succeeded. It also deleted a Key Vault, a Function App and an App Service plan, all in the same resource group. Azure SQL databases were targeted in parallel, and every one of those deletions failed because the request used an unsupported API version for that resource type.
That failure is worth pausing on. A SQL database deletion was not prevented by a security control, a deny rule or an architectural decision. It was prevented by an API version mismatch in the attacker’s own tooling. Any article that tells you this attack was contained by good security posture would be reading the wrong lesson into it.
What actually stopped the damage
Microsoft reports that Azure resource locks and storage account-level deletion protection blocked deletion attempts for some of the targeted storage accounts, and that there were multiple unsuccessful attempts to delete Azure Site Recovery locks and an Azure Backup protection lock.
This is the single most useful finding in the report. The compromised identity carried broad permissions: a group-granted Storage Account Contributor role authorized the storage deletions, and direct Contributor access authorized the application-resource deletions. At that permission level, RBAC tuning would not have saved those accounts. What saved them was a control enforced outside the identity’s own reach.
If you take one action from this incident, make it independent recovery protection on storage accounts that hold backups or terraform state — not on the same identity that can delete application resources.
The initial access problem: a secret nobody revoked
Microsoft could not confirm how the service principals were first compromised. It did establish that the client ID, client secret and tenant ID had been posted in plaintext in a public GitHub issue by an employee of the affected organization. The issue was later edited to remove the secret. The secret remained retrievable through the issue’s public edit history.
The rule Microsoft states is blunt: removing or redacting an exposed secret does not invalidate it. Treat any credential that has touched a public location as compromised and revoke or rotate it immediately. Copies persist in edit history, caches, archives, forks and logs — not just in the place you deleted it from.
If you use long-lived service principal secrets in CI pipelines, this is the failure mode to design against. Microsoft points to Microsoft Entra Workload ID and Defender for Cloud secrets scanning to reduce reliance on them. The practical version: run a secret scanner in pre-commit and in CI, and treat every hit as an incident, not a ticket.
Was an AI agent actually driving this?
Sysdig’s July 2026 report described JADEPUFFER as the first documented agentic ransomware operation. Microsoft’s Azure analysis is more careful. It says the division of work across two principals, the overlapping token streams, and the timing between operations “strongly indicates automated or scripted execution.”
Microsoft observed five unique tokens issued to the destructive principal. Four supported deletion; the fifth handled storage inventory and key retrieval. Two of the deletion tokens were active during the same 70-second window, one focused on storage and the other on a mix of storage and SQL.
Concurrent tokens and sub-second reactions are consistent with an orchestrated agent loop, but they are equally consistent with a well-written script and a thread pool. Nothing in the public Azure evidence shows model output, tool calls or reasoning. Treat “agentic” as the vendor’s characterisation of speed and coordination, not as a demonstrated capability.
Check your own tenant
Start by finding out what the compromised identities could actually do, including anything inherited:
# Find the service principal and its object id
az ad sp list --filter "appId eq '<client-id>'"
--query "[].{name:displayName,appId:appId,objectId:id}" -o table
# Effective permissions, including group-inherited assignments
az role assignment list --all
--assignee <service-principal-object-id>
--include-inherited
--query "[].{role:roleDefinitionName,scope:scope}" -o table
# Recovery protection that does not depend on that identity
az lock list -o table
Then look for bulk deletes in the Azure Activity Log. Note that deletions are frequently recorded under a resource’s write operation rather than a delete verb, so filtering on /delete alone will miss a lot of them:
az monitor activity-log list
--start-time 2026-06-01T00:00:00Z
--query "[].{time:eventTimestamp,op:operationName.value,status:status.value,caller:identity.name}"
-o table
In Log Analytics, the same hunt aggregated by caller makes a scripted burst obvious:
AzureActivity
| where TimeGenerated > ago(30d)
| where OperationNameValue in ("microsoft.storage/storageaccounts/write",
"microsoft.keyvault/vaults/write",
"microsoft.web/sites/write")
| summarize Ops = count() by Caller, ResourceGroup, bin(TimeGenerated, 1h)
| order by Ops desc
Indicators of compromise
Microsoft published three IPv4 addresses associated with Storm-3168 activity:
45.131.66[.]106— App Service probing and malicious Azure Resource Manager requests34.153.223[.]102— App Service probing64.20.53[.]230— App Service probing
The same infrastructure was seen probing Azure App Service deployments across multiple customers since the start of 2026, targeting WordPress administration paths, PHP-CGI, and the LangFlow code validation endpoint at /api/v1/validate/code. Those targets did not overlap with the affected subscriptions.
Key takeaways
- Revoke exposed credentials. Editing the post, closing the issue or deleting the message changes nothing about the secret’s validity.
- Put deletion protection on storage accounts that hold backups and recovery state. It held here against an identity with broad Contributor rights.
- Assume a compromised workload identity is powerful enough to do damage under its current role. Design controls that do not run through that identity.
- Alert on volume, not on individual operations. Seven minutes and 150 calls is not a shape any human produces.
- Enable the Defender for Cloud plans that cover the resources you actually run: Resource Manager, Storage, Key Vault, App Service and Databases.
Related reading on DevStackIO
If workload identity hygiene is your next step, start with detecting expiring IAM access keys with a Bash audit script. For the wider problem of credentials and tokens reaching places they should never be, read how to protect Git repositories from AI agent abuse. And for what happens when an automated process ends up with more authority than its task requires, see what happens when an AI agent gets root access.
References
- Storm-3168: Agentic-driven cloud attacks using compromised service principals — Microsoft Security Research, September 25, 2026
- JADEPUFFER: Agentic ransomware for automated database extortion — Sysdig, July 2026
- Lock resources to prevent accidental deletion — Microsoft Learn
- Best practices for Azure RBAC — Microsoft Learn
- MITRE ATT&CK T1485 — Data Destruction and T1490 — Inhibit System Recovery
Related Tools
Try these companion tools on DevStackIO Tools:
- API token generator — generate secure service principal tokens for Azure and cloud authentication.