Storm-3168: How One Compromised Service Principal Can Put an Entire Cloud Environment at Risk
Storm-3168 used one leaked service principal secret to destroy Azure workloads in 7 minutes. Here's how to detect it and reduce your blast radius.
If one of my workload identities was compromised right now, what would I check first?
That question is the entire article. Not the threat actor. Not the timeline. Not the press release.
Storm-3168 is a reminder that the most dangerous identity in your cloud isn't a human with a password. It's a service principal nobody watches, holding permissions nobody reviewed, using a secret somebody pasted into GitHub.
Here's what happened, and here's what I'd do about it.
What Happened in Storm-3168
In June 2026, Microsoft tracked a campaign it calls Storm-3168 (linked to JADEPUFFER). Two compromised Azure service principals from the same tenant executed a devastating sequence: 15.5 hours of quiet reconnaissance, a 7-minute destruction window, and a credential-harvesting phase that followed .
The numbers matter because they show intent and automation:
· 300+ successful read operations during reconnaissance
· 100+ storage account deletion attempts
· 30+ successful ListKeys requests after the destruction
The first service principal spent over fifteen hours mapping subscriptions, VMs, and resource groups. The second enumerated two subscriptions in five seconds, then began deleting storage accounts, Key Vaults, Function Apps, and App Service plans. It also tried to remove Azure Site Recovery and Backup protection locks—not random vandalism, but a deliberate attempt to disable recovery .
The initial access vector? A client ID, client secret, and tenant ID posted in plaintext in a public GitHub issue. The issue was later edited to remove the secret, but the edit history preserved it .
That's it. That's the whole entry point.
How Service Principal Credentials Get Compromised
Service principals are the workhorses of cloud automation. They don't have MFA. They don't have a device posture. They often don't have a human owner who notices when something's wrong .
The compromise paths are boringly predictable:
Public code exposure. The Storm-3168 vector. A secret pasted into an issue, a commit, a gist. The edit history is forever.
Over-permissioned app owners. Anyone who can add a credential to an application can authenticate as that application. If the app holds Directory.ReadWrite.All, the attacker just became a directory admin without touching a password .
Standing privilege with no expiry. A service principal created for a migration project in 2024 still holds Owner on the subscription in 2026. The attacker's window equals the credential's lifetime .
Unmonitored sign-in logs. Service principal sign-ins often bypass the alerting that human sign-ins receive. An attacker with a valid secret looks exactly like your CI/CD pipeline—until they don't.
How Excessive Azure RBAC Permissions Increase the Blast Radius
Here's the uncomfortable truth: Storm-3168 didn't exploit a vulnerability. It used the permissions the service principals already had .
The reconnaissance identity could enumerate the entire tenant. The destructive identity could delete storage accounts, Key Vaults, and Function Apps. That's not a misconfiguration in the traditional sense. That's the default outcome of granting broad roles because "it's just an automation account" .
Blast radius isn't abstract. It's the answer to: If this identity's secret leaked right now, what's the worst an attacker could do before anyone notices?
For a service principal with Owner at subscription scope, the answer is: everything in that subscription. For a principal with Contributor plus ListKeys permissions on storage, the answer is: delete the data, then harvest the keys to whatever survives .
The Storm-3168 attackers specifically targeted backup and Site Recovery controls. They understood that destroying production is loud, but destroying recovery options is what makes the damage permanent .
How Attackers Misuse Legitimate Cloud APIs
There's no malware in Storm-3168. No endpoint compromise. No lateral movement through VMs.
Just python-requests/2.34.2 calling Azure Resource Manager APIs .
The API calls themselves are documented, supported, and expected:
· ListKeys on storage accounts is how applications retrieve access keys. The attackers used it 30+ times to harvest credentials after destruction .
· Resource deletion APIs are how Terraform cleans up. The attackers used them 100+ times in seven minutes .
· Enumeration APIs are how inventory tools work. The attackers used them for 15 hours before anyone noticed .
This is the core lesson of cloud-native attacks: the API doesn't know your intent. It only knows your token. If the token is valid and the permission is present, the action succeeds.
The one thing that broke the attack wasn't a security control. It was a version mismatch—the attackers used an unsupported API version for SQL deletion, and those attempts failed . Don't count on that.
How to Detect Suspicious Activity Using Entra ID and Azure Activity Logs
If I suspect a workload identity is compromised, I'm not opening a dashboard. I'm running queries.
Entra ID sign-in logs:
Look for service principal sign-ins from IPs not seen in the last 30 days. The Elastic detection rule for rare service principal activity from multiple IPs is a good starting point: it surfaces principals performing low-frequency actions from multiple source IPs, which often indicates stolen credentials or token replay .
Azure Activity Logs:
Filter for Delete operations initiated by service principals. Then look for ListKeys calls in the same time window. The Storm-3168 pattern—destruction followed by key harvesting—is detectable if you correlate the two.
A practical KQL approach for Activity Logs:
AzureActivity | where OperationNameValue has_any ("DELETE", "LISTKEYS") | where Caller contains "service principal name or object ID" | project TimeGenerated, OperationNameValue, ResourceGroup, Caller, CallerIpAddress | sort by TimeGenerated desc The one detection that would have caught Storm-3168 early: A service principal performing ListKeys on a storage account it has never touched before, from an IP that isn't your build agent's egress range. That's not automation. That's reconnaissance or harvesting .
For credential additions specifically, alert on Update application – Certificates and secrets management outside your secrets rotation pipeline. If a human added a secret to a high-privilege app at 3 AM, that's worth a page .
AWS IAM Roles, Least Privilege, and CloudTrail: A Useful Mirror
I don't write about AWS to pad word count. I write about it because AWS's model exposes what Azure teams often miss.
In AWS, long-term access keys are the exception, not the default. IAM roles with temporary credentials via STS are the norm. A compromised role session expires. A leaked Azure service principal secret—unless you built rotation—doesn't .
CloudTrail's strength isn't that it's magic. It's that the events are structured around who assumed what role, from where, and what did they do with it. AssumeRole chains create an audit trail that links a session back to its origin. Azure's equivalent—service principal sign-ins correlated with Activity Log operations—is possible, but fewer teams build the correlation .
The least-privilege comparison is equally sharp. AWS permission boundaries and IAM Access Analyzer can surface escalation paths before an attacker finds them. In Azure, the equivalent discipline is scoped RBAC assignments plus Privileged Identity Management for anything that doesn't need to be always-on .
The gap isn't technical. It's cultural. AWS teams grew up with temporary credentials. Many Azure teams grew up with a secret in a config file.
Five Practical Lessons for Cloud Security and DevOps Engineers
Audit every service principal secret. Right now.
Search your GitHub organization's issue and commit history for client_secret, AZURE_CLIENT_SECRET, and tenant IDs. Rotate anything you find. The Storm-3168 entry vector was an edited issue—the secret remained in the edit history .Treat ListKeys as a sensitive operation.
Storage account key retrieval is credential access. Alert when a service principal that normally runs Get or List operations suddenly calls ListKeys, especially after a burst of deletions .Scope RBAC to the resource, not the subscription.
If a service principal only needs to manage one storage account, give it Storage Account Contributor on that account. Not Contributor on the resource group. Not Owner on the subscription. The blast radius shrinks with every scope reduction .Use PIM for workload identities that don't need standing access.
Most automation needs persistent permissions. Some doesn't. For the service principals that only run during deployments, use just-in-time activation. The secret, if leaked, is useless outside the activation window .Correlate identity logs with resource logs.
The Storm-3168 detection window exists because two log sources tell one story. Entra ID tells you who authenticated. Azure Activity Logs tell you what they did. If you're only watching one, you're watching half the attack .
The ghost of Storm-3168 isn't a sophisticated adversary. It's a forgotten secret in a public edit history and a service principal with permissions nobody questioned.
What would I check first?
The service principal secrets my team hasn't rotated. The RBAC assignments nobody can justify. The ListKeys calls in the Activity Log from an IP we don't recognize.
Start there. Then fix what you find.






