← All hunts high TLP:CLEAR Part 1 of 2

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

  1. Identify suspicious service principal sign-ins

    Query · scoping

    Scope the hunt to Azure service principals using the campaign's specific Python user agent.

    reads hb_auth_signinsql
    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

    What 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.

  2. Analyze discovery operation volume

    Query · baseline

    Find service principals performing a high volume of read operations, typical of automated discovery.

    reads hb_cloud_api_activitysql
    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

    What a hit looks like. Service principals with hundreds of successful read/list operations across multiple subscriptions or resource groups.

  3. Analyze user agent prevalence

    Query · baseline

    Stack-count user agents associated with Azure API activity to see if the campaign UA is an outlier.

    reads hb_cloud_api_activitysql
    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

    What a hit looks like. The Campaign user agent (python-requests/2.34.2) appearing for very few service principals, confirming its rarity.

  4. Assess reconnaissance evidence

    Agent triage

    An agent evaluates whether the identified principals exhibit automated reconnaissance patterns linked to the Storm-3168 campaign.

  5. Detect automated destruction and key retrieval

    Query · detection candidate

    Search for mass resource deletion and credential collection following the reconnaissance phase.

    reads hb_cloud_api_activitysql
    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

    What 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.

  6. Analyze destruction and recovery inhibition

    Agent triage

    An agent synthesizes the entire attack chain to determine if the environment has suffered an automated destruction event.

  7. Route on malicious activity

    Decision

    Direct the workflow based on the agent's final assessment of automated destruction.

  8. Revoke compromised service principal

    Response action

    Immediately disable the compromised identity to prevent further resource destruction.

  9. Assess destruction and initiate recovery

    Analyst task

    An analyst reviews the extent of the damage and begins restoring resources from backup.

  10. Close hunt and update detections

    Analyst task

    Final close-out of the hunt and transition to detection engineering.

Coverage

Scenario coverage

StageCoveredHow, 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

ParameterTypeDefaultWhat it is
campaign_uaslist[string]python-requests/2.34.2User agents observed in Storm-3168 activity.
lookback_daysnumber14Days of cloud API activity to examine.
scope_hostslist[host]—List of suspicious Service Principal names identified in the scoping phase.

Telemetry

SourceCategoryTelemetry
Endpoint telemetry (hb_ surfaces)endpointendpoint
Identity / sign-in telemetryidentityidentity

Source

Download hunt.md Definition (JSON) An open hunt.md file; it runs anywhere that reads the format.
---
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.