Storm-3168: Automated Azure Resource Destruction and Recovery Inhibition
A compromised service principal is executing an automated sequence of Azure resource discovery, mass deletion, and credential collection to facilitate a cloud-native ransomware operation.
Based on research by Microsoft 2026-09-26 11 steps · 4 queries T1046 T1087.004 T1485 T1486 T1490 T1528 T1580
Brief
Why This Hunt Matters
Microsoft recently detailed Storm-3168 (JADEPUFFER) in Storm-3168: Agentic-driven cloud attacks using compromised service principals. This adversary uses automated scripts to move from initial access to full resource destruction in minutes. Traditional detection rules often trigger on single resource deletions, leading to high noise. This hunt focuses on the speed and volume of the attack chain to distinguish malicious automation from legitimate administrative tasks.
How the Hunt Flows
The first step scopes the investigation using hb_auth_signin logs. The query identifies service principals authenticating with the specific Python user agent observed in the Storm-3168 campaign. This narrows the field to principals running scripts rather than standard management tools.
The hunt then branches into a parallel analysis of reconnaissance behavior. One path calculates the volume of read operations across subscriptions, while the other stacks user agent prevalence to identify outliers. Automated discovery by an agentic script typically generates a significantly higher volume of API calls than a human administrator.
An automated triage agent evaluates these reconnaissance patterns. If the agent finds evidence of scripted discovery, the hunt pivots to hb_cloud_api_activity to search for destructive actions. This phase looks for a dense sequence of storage account and database deletions alongside ListKeys operations for storage accounts. These actions together confirm the ransomware objective.
If the hunt confirms malicious destruction, it routes to response actions. The workflow provides instructions to revoke the service principal's identity and begins an impact assessment to identify which resources require restoration from backups.
What This Hunt Cannot See
Cloud audit logs do not always provide the full request metadata. If the logs lack specific API version details, the analyst cannot always distinguish between a failed SQL deletion caused by the actor's script and one caused by existing resource locks. Additionally, while the hunt identifies when an actor retrieves storage keys, it does not see if the actor used those keys to access and encrypt data within the storage containers without additional, high-fidelity storage logs.
In this series
Steps
-
Identify suspicious service principal sign-ins
Query · scopingScope the hunt to Azure service principals using the campaign's specific Python user agent.
reads hb_auth_signinsqlSELECT actor_user_name, src_endpoint_ip, user_agent, COUNT(*) as login_count, MIN(time) as first_seen FROM hb_auth_signin WHERE provider = 'azure' AND instr(',' || '{{campaign_uas}}' || ',', ',' || user_agent || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY actor_user_name, src_endpoint_ip, user_agentWhat a hit looks like. A list of service principal names that have authenticated using the suspicious Python library. Silence suggests the actor's toolkit is not present.
-
Analyze discovery operation volume
Query · baselineFind service principals performing a high volume of read operations, typical of automated discovery.
reads hb_cloud_api_activitysqlSELECT actor_user_name, api_operation, api_service_name, COUNT(*) as op_count FROM hb_cloud_api_activity WHERE activity_id = 2 AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY actor_user_name, api_operation, api_service_name HAVING op_count > 50 ORDER BY op_count DESCWhat a hit looks like. Service principals with hundreds of successful read/list operations across multiple subscriptions or resource groups.
-
Analyze user agent prevalence
Query · baselineStack-count user agents associated with Azure API activity to see if the campaign UA is an outlier.
reads hb_cloud_api_activitysqlSELECT user_agent, COUNT(DISTINCT actor_user_name) as principal_count, COUNT(*) as call_count FROM hb_cloud_api_activity WHERE time >= datetime('now', '-{{lookback_days}} days') GROUP BY user_agent ORDER BY principal_count ASCWhat a hit looks like. The Campaign user agent (python-requests/2.34.2) appearing for very few service principals, confirming its rarity.
-
Assess reconnaissance evidence
Agent triageAn agent evaluates whether the identified principals exhibit automated reconnaissance patterns linked to the Storm-3168 campaign.
-
Detect automated destruction and key retrieval
Query · detection candidateSearch for mass resource deletion and credential collection following the reconnaissance phase.
reads hb_cloud_api_activitysqlSELECT time, actor_user_name, api_operation, resource_name, status, error_code FROM hb_cloud_api_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || actor_user_name || ',') > 0) AND (activity_id = 4 OR LOWER(api_operation) LIKE '%delete%' OR LOWER(api_operation) LIKE '%listkeys%') AND time >= datetime('now', '-{{lookback_days}} days') ORDER BY time DESCWhat a hit looks like. A dense sequence of deletion operations for storage accounts and key vaults. Failed SQL deletions with API errors and multiple ListKeys operations confirm the ransomware objective.
-
Analyze destruction and recovery inhibition
Agent triageAn agent synthesizes the entire attack chain to determine if the environment has suffered an automated destruction event.
-
Route on malicious activity
DecisionDirect the workflow based on the agent's final assessment of automated destruction.
-
Revoke compromised service principal
Response actionImmediately disable the compromised identity to prevent further resource destruction.
-
Assess destruction and initiate recovery
Analyst taskAn analyst reviews the extent of the damage and begins restoring resources from backup.
-
Close hunt and update detections
Analyst taskFinal close-out of the hunt and transition to detection engineering.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| Rapid Azure resource enumeration T1087.004 · T1046 · T1580 |
Yes | identify-suspicious-principals, recon-volume, ua-prevalence |
| Automated resource destruction T1485 · T1486 |
Yes | automated-destruction |
| Removal of recovery protections T1490 |
Yes | automated-destruction |
| Storage account key collection T1528 |
Yes | automated-destruction |
| Service principal credential leak T1552.001 |
Out of scope | Belongs to another part of the 'Storm-3168: Agentic-driven cloud attacks using compromised service principals' series. |
| Web application vulnerability probing T1595.002 |
Out of scope | Belongs to another part of the 'Storm-3168: Agentic-driven cloud attacks using compromised service principals' series. |
Blind spots
- Needs Cloud audit logs with full request metadata. If the logs do not record the API version used, the analyst might mistake a failed deletion attempt for a simple configuration error. It would answer What specifically caused the SQL deletion failures?.
- Needs hb_account_change for cloud secrets. We can see the retrieval but not if the actor successfully applied the keys to access data without high-fidelity storage logs. It would answer Were the retrieved storage keys rotated by the actor?.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
campaign_uas | list[string] | python-requests/2.34.2 | User agents observed in Storm-3168 activity. |
lookback_days | number | 14 | Days of cloud API activity to examine. |
scope_hosts | list[host] | — | List of suspicious Service Principal names identified in the scoping phase. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Identity / sign-in telemetry | identity | identity |
Source
---
analysis: A single resource deletion rule is noisy. This hunt correlates a specific
Python user-agent with a rapid discovery phase and a subsequent multi-service destruction
sequence, capturing the behavior of automated cloud scripts.
blind_spots:
- id: lack-of-api-version-detail
question: What specifically caused the SQL deletion failures?
requires: Cloud audit logs with full request metadata
risk: If the logs do not record the API version used, the analyst might mistake
a failed deletion attempt for a simple configuration error.
stage: impact-resource-deletion
- id: secret-rotation-visibility
question: Were the retrieved storage keys rotated by the actor?
requires: hb_account_change for cloud secrets
risk: We can see the retrieval but not if the actor successfully applied the keys
to access data without high-fidelity storage logs.
stage: credential-access-storage-keys
coverage:
- stage: reconnaissance-cloud-discovery
status: covered
steps:
- identify-suspicious-principals
- recon-volume
- ua-prevalence
- stage: impact-resource-deletion
status: covered
steps:
- automated-destruction
- stage: inhibit-recovery-lock-removal
status: covered
steps:
- automated-destruction
- stage: credential-access-storage-keys
status: covered
steps:
- automated-destruction
- reason: 'Belongs to another part of the ''Storm-3168: Agentic-driven cloud attacks
using compromised service principals'' series.'
stage: initial-access-exposed-secret
status: out_of_scope
- reason: 'Belongs to another part of the ''Storm-3168: Agentic-driven cloud attacks
using compromised service principals'' series.'
stage: reconnaissance-application-probing
status: out_of_scope
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: promote-to-detection
justification: The shift toward agentic cloud attacks allows adversaries to destroy
infrastructure in minutes; identifying the reconnaissance phase provides the only
window to prevent catastrophic resource loss.
methodology: model-assisted
trigger: intel-report
hypothesis: A compromised service principal is executing an automated sequence of
Azure resource discovery, mass deletion, and credential collection to facilitate
a cloud-native ransomware operation.
labels:
- hunt
- attack.t1087.004
- attack.t1046
- attack.t1580
- attack.t1485
- attack.t1486
- attack.t1490
- attack.t1528
name: 'Storm-3168: Automated Azure Resource Destruction and Recovery Inhibition'
parameters:
campaign_uas:
default:
- python-requests/2.34.2
description: User agents observed in Storm-3168 activity.
from:
kind: article
observed: '2026-09-25'
ref: https://www.microsoft.com/en-us/security/blog/2026/09/25/storm-3168-agentic-driven-cloud-attacks-using-compromised-service-principals/
type: list[string]
lookback_days:
default: '14'
description: Days of cloud API activity to examine.
type: number
scope_hosts:
default: []
description: List of suspicious Service Principal names identified in the scoping
phase.
type: list[host]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.microsoft.com/en-us/security/blog/2026/09/25/storm-3168-agentic-driven-cloud-attacks-using-compromised-service-principals/
gates:
- dry-run
- lint
- critic
model: hb_google/gemini-3-flash-preview
rationale: Focus on service principals using Python-based request libraries. The hunt
starts with Azure authentication logs and pivots to API activity to identify the
breadth of the impact.
references:
- name: 'Storm-3168: Agentic-driven cloud attacks using compromised service principals'
url: https://www.microsoft.com/en-us/security/blog/2026/09/25/storm-3168-agentic-driven-cloud-attacks-using-compromised-service-principals/
related:
- hunt: exposed-secrets-github-history
reason: Initial access via secrets in GitHub history requires a repository-focused
hunt.
relation: out-of-scope-alternative
scenario:
stages:
- name: Service principal credential leak
observables:
- plaintext client_id, client_secret, and tenant_id in public GitHub issue history
slug: initial-access-exposed-secret
tactic: initial-access
techniques:
- T1552.001
- name: Rapid Azure resource enumeration
observables:
- python-requests/2.34.2
- 300+ successful read operations
- enumeration of Azure VMs, subscriptions, and resource groups
- enumeration of App Service configuration stores
- probing for Azure OpenSearch resources
slug: reconnaissance-cloud-discovery
tactic: discovery
techniques:
- T1087.004
- T1046
- T1580
- name: Automated resource destruction
observables:
- 100+ storage account deletion attempts
- deletion of Azure Key Vault
- deletion of Azure Function App
- deletion of Azure App service plan
- failed SQL database deletions due to unsupported API version
slug: impact-resource-deletion
tactic: impact
techniques:
- T1485
- T1486
- name: Removal of recovery protections
observables:
- attempts to delete Azure Site Recovery locks
- attempts to delete Azure Backup protection locks
slug: inhibit-recovery-lock-removal
tactic: impact
techniques:
- T1490
- name: Storage account key collection
observables:
- ListKeys requests against Azure Storage Accounts
- 30+ successful key retrieval operations
slug: credential-access-storage-keys
tactic: credential-access
techniques:
- T1528
- name: Web application vulnerability probing
observables:
- GET /api/v1/validate/code
- WordPress administration path probing
- PHP-CGI path probing
slug: reconnaissance-application-probing
tactic: discovery
techniques:
- T1595.002
summary: Storm-3168 (JADEPUFFER) leverages Azure service principal credentials exposed
in public GitHub issue histories to perform automated cloud resource destruction.
The actor executes rapid reconnaissance before bulk-deleting storage accounts,
key vaults, and databases while attempting to remove backup and recovery locks
to inhibit restoration.
series:
index: 1
slug: storm-3168-agentic-driven-cloud-attacks-using-compromised-service-principals
title: 'Storm-3168: Agentic-driven cloud attacks using compromised service principals'
total: 2
severity: high
targets:
analyst:
name: Tier-2 analyst
role: analyst
endpoint:
category: endpoint
name: Endpoint telemetry (hb_ surfaces)
telemetry:
- endpoint
hunter:
agent: true
name: Hunt agent
identity:
category: identity
name: Identity / sign-in telemetry
telemetry:
- identity
tlp: clear
type: investigation
---
# Storm-3168: Automated Azure Resource Destruction and Recovery Inhibition
This hunt targets the activity of Storm-3168 (JADEPUFFER), an actor using agentic automation to conduct rapid cloud attacks. We analyze Azure control-plane logs for an initial phase of broad resource reconnaissance, followed by a dense sequence of storage, database, and identity resource deletions. The hunt also searches for attempts to remove recovery protection locks and retrieve storage account access keys, identifying the hallmarks of a cloud-native ransomware operation.
## identify-suspicious-principals
<!-- Identify suspicious service principal sign-ins -->
Scope the hunt to Azure service principals using the campaign's specific Python user agent.
```sqlite target=identity role=scoping params=(campaign_uas=campaign_uas, lookback_days=lookback_days)
~~~yaml
expected: A list of service principal names that have authenticated using the suspicious
Python library. Silence suggests the actor's toolkit is not present.
reads:
- actor_user_name
- src_endpoint_ip
- user_agent
- time
- provider
silence: not_evidence_of_absence
source: hb_auth_signin
verified: dry-run
verified_at: '2026-09-26'
~~~
SELECT actor_user_name, src_endpoint_ip, user_agent, COUNT(*) as login_count, MIN(time) as first_seen FROM hb_auth_signin WHERE provider = 'azure' AND instr(',' || '{{campaign_uas}}' || ',', ',' || user_agent || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY actor_user_name, src_endpoint_ip, user_agent
```
## parallel-recon-check
<!-- Examine reconnaissance breadth and volume -->
parallel:
- → recon-volume
- → ua-prevalence
join: → triage-reconnaissance
## recon-volume
<!-- Analyze discovery operation volume -->
Find service principals performing a high volume of read operations, typical of automated discovery.
```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: Service principals with hundreds of successful read/list operations across
multiple subscriptions or resource groups.
prevalence:
by: actor_user_name
key:
- api_operation
rare_below: 3
reads:
- actor_user_name
- api_operation
- api_service_name
- activity_id
- time
silence: not_evidence_of_absence
source: hb_cloud_api_activity
verified: dry-run
verified_at: '2026-09-26'
~~~
SELECT actor_user_name, api_operation, api_service_name, COUNT(*) as op_count FROM hb_cloud_api_activity WHERE activity_id = 2 AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY actor_user_name, api_operation, api_service_name HAVING op_count > 50 ORDER BY op_count DESC
```
## ua-prevalence
<!-- Analyze user agent prevalence -->
Stack-count user agents associated with Azure API activity to see if the campaign UA is an outlier.
```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: The Campaign user agent (python-requests/2.34.2) appearing for very few
service principals, confirming its rarity.
prevalence:
by: actor_user_name
key:
- user_agent
rare_below: 5
reads:
- user_agent
- actor_user_name
- time
silence: not_evidence_of_absence
source: hb_cloud_api_activity
verified: dry-run
verified_at: '2026-09-26'
~~~
SELECT user_agent, COUNT(DISTINCT actor_user_name) as principal_count, COUNT(*) as call_count FROM hb_cloud_api_activity WHERE time >= datetime('now', '-{{lookback_days}} days') GROUP BY user_agent ORDER BY principal_count ASC
```
## triage-reconnaissance
<!-- Assess reconnaissance evidence -->
```agent target=hunter
cite: required
context:
- identify-suspicious-principals
- recon-volume
- ua-prevalence
max_iterations: 3
objective: Determine if any service principal exhibits signs of automated reconnaissance
against Azure resources, specifically focusing on those using the campaign user
agent.
success_criteria: A per-principal classification of malicious, suspicious, or benign
based on reconnaissance patterns.
tools:
- endpoint
- identity
```
## automated-destruction
<!-- Detect automated destruction and key retrieval -->
Search for mass resource deletion and credential collection following the reconnaissance phase.
```sqlite target=endpoint role=detection-candidate params=(scope_hosts=scope_hosts, lookback_days=lookback_days)
~~~yaml
expected: A dense sequence of deletion operations for storage accounts and key vaults.
Failed SQL deletions with API errors and multiple ListKeys operations confirm the
ransomware objective.
reads:
- actor_user_name
- api_operation
- resource_name
- status
- error_code
- activity_id
- time
silence: not_evidence_of_absence
source: hb_cloud_api_activity
verified: dry-run
verified_at: '2026-09-26'
~~~
SELECT time, actor_user_name, api_operation, resource_name, status, error_code FROM hb_cloud_api_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || actor_user_name || ',') > 0) AND (activity_id = 4 OR LOWER(api_operation) LIKE '%delete%' OR LOWER(api_operation) LIKE '%listkeys%') AND time >= datetime('now', '-{{lookback_days}} days') ORDER BY time DESC
```
## triage-final-verdict
<!-- Analyze destruction and recovery inhibition -->
```agent target=hunter
cite: required
context:
- triage-reconnaissance
- automated-destruction
max_iterations: 4
objective: Evaluate the combined evidence of discovery, mass resource deletion, failed
SQL deletions, and storage key collection to confirm a ransomware-aligned campaign.
success_criteria: A comprehensive report naming the malicious service principals and
the resources affected.
tools:
- endpoint
- identity
```
## route-on-verdict
<!-- Route on malicious activity -->
if~: "the agent verdict for triage-final-verdict is malicious and confirms automated resource deletion" (confidence: high, judge=hunter)
then: → revoke-compromised-sp
indeterminate: → impact-assessment
unavailable: → impact-assessment (blind_spot: lack-of-api-version-detail)
else: → close-out
## revoke-compromised-sp
<!-- Revoke compromised service principal -->
```action target=identity
~~~yaml
approval: required
~~~
Disable the service principal(s) identified as malicious and revoke all active OAuth tokens to halt the destructive campaign.
```
→ impact-assessment
## impact-assessment
<!-- Assess destruction and initiate recovery -->
```manual target=analyst
Verify the list of deleted storage accounts, SQL databases, and key vaults. Identify resources where deletion was blocked by locks or protection. Initiate restoration from Azure Backup or Site Recovery.
```
→ close-out
## close-out
<!-- Close hunt and update detections -->
```manual target=analyst
Document the hunt findings. If a confirmed intrusion occurred, escalate to IR. If not, record any benign Python-based automation for exclusion tuning.
```
→ end
Run it
Take this hunt into your environment.
Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.
Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, critic, then reviewed by a person.