Storm-3168 Web Application Probing
An adversary is conducting automated web reconnaissance by probing for specific administrative and AI-related paths to identify vulnerable entry points for a subsequent cloud-focused intrusion.
Based on research by Microsoft 2026-09-26 9 steps · 3 queries T1595.002
Brief
Why this hunt
Microsoft recently detailed Storm-3168, also known as JADEPUFFER, and their use of agentic ransomware in cloud environments. Their operations often begin with automated web reconnaissance to find a foothold. By the time they reach cloud resources, the window for prevention is narrow. We focus on the initial probing phase to identify these actors while they are still testing the perimeter. The full report is available at Storm-3168: Agentic-driven cloud attacks using compromised service principals (https://www.microsoft.com/en-us/security/blog/2026/09/25/storm-3168-agentic-driven-cloud-attacks-using-compromised-service-principals/).
Hunt Flow
The first phase focuses on the 'python-requests/2.34.2' user agent and specific paths like /php-cgi/ and /api/v1/validate/code. These are known signals for JADEPUFFER activity. By filtering for these specific markers, the hunt narrows millions of logs down to a manageable set of potential leads. The hunt then evaluates the prevalence of the source IPs. In the prevalence check, the logic identifies source IPs that target more than one hostname. This behavior is typical of automated scripts that spray a range of assets rather than a single accidental visitor. The hunt calculates the 'host_targets' count to surface the most aggressive scanners. Simultaneously, the hunt checks internal asset inventory to see if the targeted hosts actually run the software being probed. It uses the hb_exposed_assets surface to identify which hosts in your environment run WordPress, PHP, or LangFlow. This ensures that an analyst does not waste time on probes hitting systems that aren't even running the targeted service. A match between a probe for /wp-admin/ and a host confirmed to run WordPress is a high-confidence signal. Finally, an analyst or automated agent reviews the combined telemetry. They verify if the source IP successfully reached a sensitive endpoint and determine if the activity aligns with known Storm-3168 behavior before initiating a block at the network perimeter.
Blind Spots
This hunt relies on HTTP telemetry. It cannot see probes against unmanaged or shadow IT assets that do not report logs to a central system. Additionally, without TLS inspection or application-level payload logs, we cannot see the contents of POST requests. A successful response status might indicate a successful exploit, but the hunt can only confirm the reconnaissance attempt.
In this series
Steps
-
HTTP probing for sensitive paths
Query · detection candidateIdentify HTTP requests matching known actor user agents or targeted vulnerability paths.
reads hb_http_activitysqlSELECT src_endpoint_ip, url_hostname, url_path, user_agent, status_code, http_method, time FROM hb_http_activity WHERE (LOWER(url_path) = LOWER('{{langflow_path}}') OR instr(',' || '{{probing_paths}}' || ',', ',' || LOWER(url_path) || ',') > 0 OR user_agent = '{{storm_user_agent}}') AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Requests matching the actor user agent or specific vulnerability paths. Hits on LangFlow or administrative paths with status 200 are high-interest.
-
Source IP scanning prevalence
Query · baselineStack-count source IPs to determine if they have targeted multiple distinct hostnames in the environment.
reads hb_http_activitysqlSELECT src_endpoint_ip, COUNT(DISTINCT url_hostname) AS host_targets, COUNT(*) AS request_hits, MIN(time) AS first_seen FROM hb_http_activity WHERE time >= datetime('now', '-{{lookback_days}} days') GROUP BY src_endpoint_ip HAVING host_targets > 1 ORDER BY host_targets DESCWhat a hit looks like. IPs targeting multiple hosts. Automated scanning is identified when host_targets exceeds normal per-IP limits.
-
Vulnerable asset inventory
Query · enrichmentIdentify hosts known to run WordPress, PHP, or LangFlow to confirm the relevance of the probes.
reads hb_exposed_assetssqlSELECT domain_or_ip, product, version, port FROM hb_exposed_assets WHERE product IS NOT NULL AND (LOWER(product) LIKE '%wordpress%' OR LOWER(product) LIKE '%php%' OR LOWER(product) LIKE '%langflow%')What a hit looks like. A list of hosts running products the actor is actively probing. Probes against these specific hosts indicate targeted intent.
-
Evaluate probing activity
Agent triageSynthesize the HTTP hits, scanning prevalence, and asset inventory to determine the maliciousness of source IPs.
-
Route on verdict
DecisionBranch the hunt based on the agent's confidence in the malicious probing activity.
-
Block attacker IP
Response actionImmediately mitigate the threat by blocking identified actor infrastructure at the network perimeter.
-
Analyst confirmation
Analyst taskVerify the agent's findings and assess whether probes resulted in successful exploitation attempts.
-
Hunt close-out
Analyst taskDocument the findings and recommend standing detections.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| Web application vulnerability probing T1595.002 |
Yes | http-probing-lead, ip-scanning-prevalence, vulnerable-asset-inventory |
| Service principal credential leak T1552.001 |
Out of scope | Belongs to a separate hunt focusing on GitHub audit logs and secret exposures. |
| Rapid Azure resource enumeration T1087.004 · T1046 · T1580 |
Out of scope | Belongs to a separate hunt focusing on Azure Resource Manager enumeration via hb_cloud_api_activity. |
| Automated resource destruction T1485 · T1486 |
Out of scope | Belongs to another part of the 'Storm-3168: Agentic-driven cloud attacks using compromised service principals' series. |
| Removal of recovery protections T1490 |
Out of scope | Belongs to another part of the 'Storm-3168: Agentic-driven cloud attacks using compromised service principals' series. |
| Storage account key collection T1528 |
Out of scope | Belongs to another part of the 'Storm-3168: Agentic-driven cloud attacks using compromised service principals' series. |
Blind spots
- Needs hb_http_activity from all internet-facing servers. Probes against systems not reporting to central logging will be missed. It would answer whether probes targeted unmanaged or shadow IT assets.
- Needs TLS inspection or application-level logs. A successful probe (200 OK) might be an exploit attempt; without payload visibility, the hunt cannot confirm if code was actually executed. It would answer the specific content of POST request payloads.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
langflow_path | path | /api/v1/validate/code | The specific LangFlow code validation path. |
lookback_days | number | 14 | Days of history to examine. |
probing_paths | list[path] | /wp-admin/, /wp-login.php, /php-cgi/ | Sensitive paths targeted by Storm-3168 probing. |
storm_user_agent | string | python-requests/2.34.2 | The specific User Agent observed during Storm-3168 activity. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Network telemetry | network | network |
| Web server / proxy logs | siem | network |
Source
---
analysis: A standard detection rule alerts on the User Agent or sensitive paths. This
hunt correlates those hits with wide scanning prevalence (targeting multiple hosts)
and matches them against an organization's specific vulnerable asset inventory to
reduce false positives and prioritize high-intent actors.
blind_spots:
- id: no-http-telemetry
question: whether probes targeted unmanaged or shadow IT assets
requires: hb_http_activity from all internet-facing servers
risk: Probes against systems not reporting to central logging will be missed.
stage: reconnaissance-application-probing
- id: encrypted-traffic
question: the specific content of POST request payloads
requires: TLS inspection or application-level logs
risk: A successful probe (200 OK) might be an exploit attempt; without payload visibility,
the hunt cannot confirm if code was actually executed.
stage: reconnaissance-application-probing
coverage:
- stage: reconnaissance-application-probing
status: covered
steps:
- http-probing-lead
- ip-scanning-prevalence
- vulnerable-asset-inventory
- reason: Belongs to a separate hunt focusing on GitHub audit logs and secret exposures.
stage: initial-access-exposed-secret
status: out_of_scope
- reason: Belongs to a separate hunt focusing on Azure Resource Manager enumeration
via hb_cloud_api_activity.
stage: reconnaissance-cloud-discovery
status: out_of_scope
- reason: 'Belongs to another part of the ''Storm-3168: Agentic-driven cloud attacks
using compromised service principals'' series.'
stage: impact-resource-deletion
status: out_of_scope
- reason: 'Belongs to another part of the ''Storm-3168: Agentic-driven cloud attacks
using compromised service principals'' series.'
stage: inhibit-recovery-lock-removal
status: out_of_scope
- reason: 'Belongs to another part of the ''Storm-3168: Agentic-driven cloud attacks
using compromised service principals'' series.'
stage: credential-access-storage-keys
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: Storm-3168 conducts extensive web reconnaissance before moving to
destructive cloud operations; identifying these signals early allows for infrastructure
blocking before initial access.
methodology: model-assisted
trigger: intel-report
hypothesis: An adversary is conducting automated web reconnaissance by probing for
specific administrative and AI-related paths to identify vulnerable entry points
for a subsequent cloud-focused intrusion.
labels:
- hunt
- attack.t1595.002
name: Storm-3168 Web Application Probing
parameters:
langflow_path:
default: /api/v1/validate/code
description: The specific LangFlow code validation path.
from:
kind: article
observed: '2026-09-25'
ref: storm-3168-msrc
type: path
lookback_days:
default: '14'
description: Days of history to examine.
from:
kind: manual
observed: '2026-09-25'
ref: hunt-standard-lookback
type: number
probing_paths:
default:
- /wp-admin/
- /wp-login.php
- /php-cgi/
description: Sensitive paths targeted by Storm-3168 probing.
from:
kind: article
observed: '2026-09-25'
ref: storm-3168-msrc
type: list[path]
storm_user_agent:
default: python-requests/2.34.2
description: The specific User Agent observed during Storm-3168 activity.
from:
kind: article
observed: '2026-09-25'
ref: storm-3168-msrc
type: string
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
model: hb_google/gemini-3-flash-preview
rationale: Focus on internet-facing web servers, WAFs, and application gateways. Priority
is given to hosts already identified in hb_exposed_assets as running WordPress or
AI-related tooling.
references:
- name: "MSRC \u2014 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: storm-3168-cloud-resource-destruction
reason: Reconnaissance precedes the impact stage; that hunt monitors for resource
deletion events.
relation: follows
- hunt: storm-3168-azure-resource-destruction
relation: follows
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: 2
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
network:
category: network
name: Network telemetry
telemetry:
- network
web:
category: siem
name: Web server / proxy logs
telemetry:
- network
tlp: clear
type: investigation
---
# Storm-3168 Web Application Probing
This hunt identifies reconnaissance activity linked to Storm-3168 (JADEPUFFER), an actor known for agentic ransomware operations. The actor uses automated scripts to identify web-based entry points by probing for WordPress administrative paths, PHP-CGI vulnerabilities, and LangFlow code validation endpoints. The hunt filters HTTP telemetry for specific user agents and URI paths, then cross-references findings with source IP scanning prevalence and exposed asset context to distinguish directed actor activity from general internet background noise.
## http-probing-lead
<!-- HTTP probing for sensitive paths -->
Identify HTTP requests matching known actor user agents or targeted vulnerability paths.
```sqlite target=web role=detection-candidate params=(probing_paths=probing_paths, langflow_path=langflow_path, storm_user_agent=storm_user_agent, lookback_days=lookback_days)
~~~yaml
expected: Requests matching the actor user agent or specific vulnerability paths.
Hits on LangFlow or administrative paths with status 200 are high-interest.
reads:
- src_endpoint_ip
- url_hostname
- url_path
- user_agent
- status_code
- http_method
- time
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-09-26'
~~~
SELECT src_endpoint_ip, url_hostname, url_path, user_agent, status_code, http_method, time FROM hb_http_activity WHERE (LOWER(url_path) = LOWER('{{langflow_path}}') OR instr(',' || '{{probing_paths}}' || ',', ',' || LOWER(url_path) || ',') > 0 OR user_agent = '{{storm_user_agent}}') AND time >= datetime('now', '-{{lookback_days}} days')
```
## parallel-corroboration
<!-- Corroborate with prevalence and asset context -->
parallel:
- → ip-scanning-prevalence
- → vulnerable-asset-inventory
join: → agent-triage
## ip-scanning-prevalence
<!-- Source IP scanning prevalence -->
Stack-count source IPs to determine if they have targeted multiple distinct hostnames in the environment.
```sqlite target=web role=baseline params=(lookback_days=lookback_days)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: IPs targeting multiple hosts. Automated scanning is identified when host_targets
exceeds normal per-IP limits.
prevalence:
by: url_hostname
key:
- src_endpoint_ip
rare_below: 2
reads:
- src_endpoint_ip
- url_hostname
- time
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-09-26'
~~~
SELECT src_endpoint_ip, COUNT(DISTINCT url_hostname) AS host_targets, COUNT(*) AS request_hits, MIN(time) AS first_seen FROM hb_http_activity WHERE time >= datetime('now', '-{{lookback_days}} days') GROUP BY src_endpoint_ip HAVING host_targets > 1 ORDER BY host_targets DESC
```
## vulnerable-asset-inventory
<!-- Vulnerable asset inventory -->
Identify hosts known to run WordPress, PHP, or LangFlow to confirm the relevance of the probes.
```sqlite target=endpoint role=enrichment
~~~yaml
expected: A list of hosts running products the actor is actively probing. Probes against
these specific hosts indicate targeted intent.
reads:
- domain_or_ip
- product
- version
- port
silence: not_evidence_of_absence
source: hb_exposed_assets
verified: dry-run
verified_at: '2026-09-26'
~~~
SELECT domain_or_ip, product, version, port FROM hb_exposed_assets WHERE product IS NOT NULL AND (LOWER(product) LIKE '%wordpress%' OR LOWER(product) LIKE '%php%' OR LOWER(product) LIKE '%langflow%')
```
## agent-triage
<!-- Evaluate probing activity -->
```agent target=hunter
cite: required
context:
- http-probing-lead
- ip-scanning-prevalence
- vulnerable-asset-inventory
max_iterations: 4
objective: Determine if source IPs are conducting targeted Storm-3168 application
probing based on user agent, paths, status codes, scanning prevalence, and the presence
of vulnerable software on the targets.
success_criteria: Verdicts for each suspect IP, identifying those that hit vulnerable
assets with the Storm-3168 user agent or paths.
tools:
- endpoint
- web
```
## route-on-verdict
<!-- Route on verdict -->
if~: "the agent-triage verdict is malicious for at least one source IP conducting automated probes against WordPress or LangFlow assets" (confidence: high, judge=hunter)
then: → block-attacker-ip
indeterminate: → analyst-confirmation
unavailable: → analyst-confirmation (blind_spot: no-http-telemetry)
else: → analyst-confirmation
## block-attacker-ip
<!-- Block attacker IP -->
```action target=network
~~~yaml
approval: required
~~~
Block the identified source IPs in the WAF, perimeter firewall, or application gateway to prevent further reconnaissance or exploitation.
```
→ analyst-confirmation
## analyst-confirmation
<!-- Analyst confirmation -->
```manual target=analyst
Review the full HTTP request logs for the identified IPs. Check for subsequent POST requests to administrative or code-validation paths that might indicate successful exploitation. Validate the product version on the target hosts.
```
→ hunt-close-out
## hunt-close-out
<!-- Hunt close-out -->
```manual target=analyst
Record the identified IPs and the targeted paths. If successful probes were found against vulnerable versions of WordPress or LangFlow, ensure those systems are patched and credentials rotated.
```
→ 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, then reviewed by a person.