Pre-Patch Web Runtime Exploitation
An attacker has exploited a zero-day vulnerability in an internet-facing web runtime during the four-day pre-patch window (Oct 17-21, 2026) to gain remote code execution, established botnet C2, and leveraged stolen credentials to persist via remote access services.
Based on research by Sekoia 2026-10-29 12 steps · 5 queries T1133 T1190 T1486
Brief
Why this hunt matters
In the article "PHP2Shell Was Exploited Before the Patch", Sekoia (https://www.sekoia.com/blog/php2shell-was-exploited-before-the-patch-expect-more-of-it) describes a zero-day window between October 17 and October 21, 2026. During these four days, attackers scanned for and exploited vulnerabilities in web runtimes like PHP and Node.js. Because exploitation happened before a fix existed, standard patching cycles do not address systems that adversaries already compromised. This hunt focuses on identifying those initial entries and the subsequent activity.
How the hunt flows
The first phase identifies the attack surface. The hunt uses the hb_software_inventory surface to list hosts running PHP, Node.js, or Bun. This scoping step ensures the investigation focuses on systems where the specific vulnerabilities could exist.
Next, the hunt runs parallel queries to find evidence of the exploit. One branch examines hb_http_activity for 500-series server errors or high-volume POST requests. Simultaneously, another branch inspects hb_process_activity for web runtime processes—like php-fpm or node—that spawn shells or network tools like curl and wget.
An analyst then triages these results. They look for timing alignment where a specific web error occurs immediately before a shell launch on the same host. This step separates failed exploitation attempts from successful unauthenticated execution.
If a host shows signs of a beachhead, the hunt expands to look for follow-on activity. It queries hb_network_connection for outbound traffic to rare external IPs, looking for Mirai botnet command-and-control patterns. The hunt also checks hb_auth_signin for successful logins to VPN or Citrix gateways originating from the same external IPs used during the initial RCE.
Finally, the analyst investigates impact. They review file activity for signs of mass modification or renaming and check shell history to understand what the attacker did after gaining access.
What the hunt cannot see
The hunt has specific limitations. If an adversary uses a crafted Brotli-compressed stream to trigger memory corruption, the payload remains invisible to hb_http_activity. Furthermore, if the attacker uses different IP addresses for the initial exploit and the subsequent persistence via VPN, the correlation between those two phases will likely fail.
Steps
-
Scope vulnerable runtimes
Query · scopingIdentify hosts running targeted web runtimes that were vulnerable during the October 2026 window.
reads hb_software_inventorysqlSELECT device_hostname, package_name, package_version, install_path FROM hb_software_inventory WHERE (instr(',' || '{{runtime_packages}}' || ',', ',' || LOWER(package_name) || ',') > 0) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)What a hit looks like. A list of hosts and paths where PHP, Node.js or Bun are installed. Silence means these runtimes are not under management.
-
HTTP exploitation attempts
Query · enrichmentIdentify HTTP requests targeting web servers that resulted in server errors or heavy POST activity during the zero-day window.
reads hb_http_activitysqlSELECT device_hostname, src_endpoint_ip, http_method, url_path, status_code, COUNT(*) AS request_count FROM hb_http_activity WHERE (status_code >= 500 OR http_method = 'POST') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (time >= '{{start_date}}' AND time <= '{{end_date}}') GROUP BY device_hostname, src_endpoint_ip, http_method, url_path, status_code ORDER BY request_count DESCWhat a hit looks like. Unusual URIs with 500-series errors or high volumes of POST requests. Silence proves absence only if web server logging is enabled.
-
RCE shell execution
Query · detection candidateDetect instances where a web runtime process spawns a shell or downloader, indicating successful unauthenticated execution.
reads hb_process_activitysqlSELECT device_hostname, process_name, process_cmd_line, parent_process_name, user_name, time FROM hb_process_activity WHERE (LOWER(parent_process_name) LIKE '%/php%' OR LOWER(parent_process_name) LIKE '%/node%' OR LOWER(parent_process_name) LIKE '%/bun%' OR LOWER(parent_process_name) LIKE '%/httpd%' OR LOWER(parent_process_name) LIKE '%/nginx%') AND (LOWER(process_name) LIKE '%/sh' OR LOWER(process_name) LIKE '%/bash' OR LOWER(process_name) LIKE '%/curl' OR LOWER(process_name) LIKE '%/wget') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (time >= '{{start_date}}' AND time <= '{{end_date}}')What a hit looks like. A web runtime parent spawning a shell or network utility. This is highly suspicious during a zero-day window.
-
Triage early exploitation
Agent triageEvaluate whether any host shows signs of successful zero-day compromise based on web errors and process execution.
-
Mirai botnet C2
Query · baselineDetect outbound connections to rare external IPs, indicative of a botnet agent established via the RCE.
reads hb_network_connectionsqlSELECT dst_endpoint_ip, dst_endpoint_port, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_network_connection WHERE direction = 'outbound' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (time >= '{{start_date}}' AND time <= '{{end_date}}') GROUP BY dst_endpoint_ip, dst_endpoint_port HAVING host_count <= 2 ORDER BY host_count ASCWhat a hit looks like. Outbound connections to unique IPs. Mirai often targets non-standard ports (23, 2323) but may use 80/443.
-
Remote access persistence
Query · triageIdentify successful sign-ins to VPN or Citrix gateways from IP addresses involved in the RCE or botnet activity.
reads hb_auth_signinsqlSELECT actor_user_name, src_endpoint_ip, provider, dst_endpoint_name, time FROM hb_auth_signin WHERE (instr(',' || '{{suspicious_ips}}' || ',', ',' || src_endpoint_ip || ',') > 0 OR '{{suspicious_ips}}' = '') AND status_id = 1 AND (time >= '{{start_date}}' AND time <= '{{end_date}}')What a hit looks like. Successful logons to gateways from external IPs identified as suspicious. This indicates credential reuse or theft.
-
Triage intrusion chain
Agent triageSynthesize early RCE evidence with follow-on C2 and persistence findings to confirm a multi-stage intrusion.
-
Route on verdict
DecisionDetermine the response path based on the triage-intrusion results.
-
Isolate and revoke
Response actionContain the compromise by isolating the host and revoking sessions.
-
Investigate impact
Analyst taskDetermine the extent of damage, including potential data encryption or unauthorized file access.
-
Close out
Analyst taskVerify patching status and document the zero-day incident.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| Exploitation of Web Runtimes T1190 |
Yes | scope-vulnerable-runtimes, http-exploitation-attempts |
| RCE via Web Server T1190 |
Yes | rce-shell-execution |
| Botnet Communication T1190 |
Yes | mirai-botnet-c2 |
| Persistence via Remote Services T1133 |
Yes | remote-access-persistence |
| Data Encryption for Impact T1486 |
Yes | investigate-impact |
Blind spots
- Needs Full HTTP request body inspection. Exploit payloads inside valid compressed streams are invisible to hb_http_activity. It would answer Was a specifically crafted Brotli-compressed stream used to trigger memory corruption?.
- Needs Device-to-Identity IP correlation logs. An attacker using different IPs for RCE and persistence may evade correlation if src_endpoint_ip doesn't match. It would answer Is the external IP used for a VPN sign-in definitely the same as the attacker's beachhead IP?.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
end_date | string | 2026-10-21 23:59:59 | The date the official patch was released. |
runtime_packages | list[string] | php, node, nodejs, bun | Package names representing the targeted web runtimes. |
scope_hosts | list[host] | — | Hostnames to focus the investigation; leave empty to hunt across the entire estate. |
start_date | string | 2026-10-17 00:00:00 | The date active zero-day exploitation was first observed. |
suspicious_ips | list[ip] | — | IP addresses identified as suspicious in the first phase; leave empty to search all authentication attempts during the window. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Identity / sign-in telemetry | identity | identity |
| Network telemetry | network | network |
| Web server / proxy logs | siem | network |
Source
---
analysis: A single rule on shell execution from a web server is useful but lacks the
historical context of the zero-day window and the correlation with HTTP status codes
and follow-on botnet C2 that this phased hunt provides.
blind_spots:
- id: brotli-stream-blindspot
question: Was a specifically crafted Brotli-compressed stream used to trigger memory
corruption?
requires: Full HTTP request body inspection
risk: Exploit payloads inside valid compressed streams are invisible to hb_http_activity.
stage: initial-access-vuln-exploitation
- id: no-identity-ip-correlation
question: Is the external IP used for a VPN sign-in definitely the same as the attacker's
beachhead IP?
requires: Device-to-Identity IP correlation logs
risk: An attacker using different IPs for RCE and persistence may evade correlation
if src_endpoint_ip doesn't match.
stage: external-remote-persistence
coverage:
- stage: initial-access-vuln-exploitation
status: covered
steps:
- scope-vulnerable-runtimes
- http-exploitation-attempts
- stage: unauthenticated-remote-code-execution
status: covered
steps:
- rce-shell-execution
- stage: botnet-command-and-control
status: covered
steps:
- mirai-botnet-c2
- stage: external-remote-persistence
status: covered
steps:
- remote-access-persistence
- stage: data-encryption-impact
status: covered
steps:
- investigate-impact
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: keep-as-periodic-hunt
justification: Threat actors exploited critical web runtime vulnerabilities four
days before patches existed. Identifying these breaches requires investigating
activity during the October zero-day window across several telemetry surfaces.
methodology: model-assisted
trigger: intel-report
hypothesis: An attacker has exploited a zero-day vulnerability in an internet-facing
web runtime during the four-day pre-patch window (Oct 17-21, 2026) to gain remote
code execution, established botnet C2, and leveraged stolen credentials to persist
via remote access services.
labels:
- hunt
- attack.t1190
- attack.t1133
- attack.t1486
- command and control
- execution
- impact
- initial access
- persistence
name: Pre-Patch Web Runtime Exploitation
parameters:
end_date:
default: '2026-10-21 23:59:59'
description: The date the official patch was released.
from:
kind: article
observed: '2026-10-21'
ref: sekoia-php2shell
type: string
runtime_packages:
default:
- php
- node
- nodejs
- bun
description: Package names representing the targeted web runtimes.
type: list[string]
scope_hosts:
default: []
description: Hostnames to focus the investigation; leave empty to hunt across
the entire estate.
type: list[host]
start_date:
default: '2026-10-17 00:00:00'
description: The date active zero-day exploitation was first observed.
from:
kind: article
observed: '2026-10-17'
ref: sekoia-php2shell
type: string
suspicious_ips:
default: []
description: IP addresses identified as suspicious in the first phase; leave empty
to search all authentication attempts during the window.
from:
kind: article
observed: '2026-10-27'
ref: https://www.sekoia.com/blog/php2shell-was-exploited-before-the-patch-expect-more-of-it
type: list[ip]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.sekoia.com/blog/php2shell-was-exploited-before-the-patch-expect-more-of-it
gates:
- dry-run
- lint
model: hb_google/gemini-3-flash-preview
rationale: Focus on internet-facing servers running PHP or Node.js. Narrow the search
to the specific window from Oct 17 to Oct 21, 2026.
references:
- name: PHP2Shell Was Exploited Before the Patch
url: https://www.sekoia.com/blog/php2shell-was-exploited-before-the-patch-expect-more-of-it
related:
- hunt: mirai-botnet-persistence-patterns
reason: This hunt focuses on the initial zero-day web vector; general Mirai persistence
is handled separately.
relation: out-of-scope-alternative
scenario:
stages:
- name: Exploitation of Web Runtimes
observables:
- CVE-2026-111497
- Brotli-compressed streams
- Scanning for PHP 8.1 or older
- Requests targeting Node.js, Bun, or PHP instances
- Attempts captured on 2026-10-17
slug: initial-access-vuln-exploitation
tactic: initial-access
techniques:
- T1190
- name: RCE via Web Server
observables:
- PHP or Node.js processes spawning shells
- /bin/sh
- /bin/bash
- curl or wget used for downloader scripts
- One-line root fix scripts from GitHub
slug: unauthenticated-remote-code-execution
tactic: execution
techniques:
- T1190
- name: Botnet Communication
observables:
- Mirai botnet C2 traffic
- Outbound network connections from web server to known botnet IPs
slug: botnet-command-and-control
tactic: command-and-control
techniques:
- T1190
- name: Persistence via Remote Services
observables:
- Anomalous sign-ins to VPN gateways
- Citrix appliance access patterns following web server compromise
slug: external-remote-persistence
tactic: persistence
techniques:
- T1133
- name: Data Encryption for Impact
observables:
- Ransomware encryption on local drives
- Mass file modification on web servers
slug: data-encryption-impact
tactic: impact
techniques:
- T1486
summary: Threat actors exploited critical PHP and Brotli vulnerabilities (CVE-2026-111497)
during a 'negative window' before official patches were released. The campaign
involved mass scanning of internet-facing web runtimes to achieve unauthenticated
remote code execution, primarily for botnet recruitment and potential ransomware
impact.
severity: critical
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
network:
category: network
name: Network telemetry
telemetry:
- network
web:
category: siem
name: Web server / proxy logs
telemetry:
- network
tlp: clear
type: investigation
---
# Pre-Patch Web Runtime Exploitation
This hunt examines the negative window between the start of active exploitation for vulnerabilities like PHP2Shell and Node2Shell (CVE-2026-111497) and the release of patches on October 21, 2026. Threat actors were observed scanning and exploiting these systems days before a fix existed, meaning patching alone is insufficient for response. The hunt uses a phased approach: first, it identifies exposed runtimes and correlates 500-series web errors with suspicious shell interpreters spawned by web processes. Second, it searches for follow-on botnet communication and anomalous logins to VPN or Citrix gateways from IPs involved in the initial RCE.
## scope-vulnerable-runtimes
<!-- Scope vulnerable runtimes -->
Identify hosts running targeted web runtimes that were vulnerable during the October 2026 window.
```sqlite target=endpoint role=scoping params=(runtime_packages=runtime_packages, scope_hosts=scope_hosts)
~~~yaml
expected: A list of hosts and paths where PHP, Node.js or Bun are installed. Silence
means these runtimes are not under management.
reads:
- device_hostname
- package_name
- package_version
- install_path
silence: not_evidence_of_absence
source: hb_software_inventory
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT device_hostname, package_name, package_version, install_path FROM hb_software_inventory WHERE (instr(',' || '{{runtime_packages}}' || ',', ',' || LOWER(package_name) || ',') > 0) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)
```
## early-stage-fan-out
<!-- Correlate exploitation and execution -->
parallel:
- → http-exploitation-attempts
- → rce-shell-execution
join: → triage-exploitation
## http-exploitation-attempts
<!-- HTTP exploitation attempts -->
Identify HTTP requests targeting web servers that resulted in server errors or heavy POST activity during the zero-day window.
```sqlite target=web role=enrichment params=(scope_hosts=scope_hosts, start_date=start_date, end_date=end_date)
~~~yaml
expected: Unusual URIs with 500-series errors or high volumes of POST requests. Silence
proves absence only if web server logging is enabled.
reads:
- device_hostname
- src_endpoint_ip
- http_method
- url_path
- status_code
- time
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT device_hostname, src_endpoint_ip, http_method, url_path, status_code, COUNT(*) AS request_count FROM hb_http_activity WHERE (status_code >= 500 OR http_method = 'POST') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (time >= '{{start_date}}' AND time <= '{{end_date}}') GROUP BY device_hostname, src_endpoint_ip, http_method, url_path, status_code ORDER BY request_count DESC
```
## rce-shell-execution
<!-- RCE shell execution -->
Detect instances where a web runtime process spawns a shell or downloader, indicating successful unauthenticated execution.
```sqlite target=endpoint role=detection-candidate params=(scope_hosts=scope_hosts, start_date=start_date, end_date=end_date)
~~~yaml
expected: A web runtime parent spawning a shell or network utility. This is highly
suspicious during a zero-day window.
reads:
- device_hostname
- process_name
- process_cmd_line
- parent_process_name
- user_name
- time
silence: evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT device_hostname, process_name, process_cmd_line, parent_process_name, user_name, time FROM hb_process_activity WHERE (LOWER(parent_process_name) LIKE '%/php%' OR LOWER(parent_process_name) LIKE '%/node%' OR LOWER(parent_process_name) LIKE '%/bun%' OR LOWER(parent_process_name) LIKE '%/httpd%' OR LOWER(parent_process_name) LIKE '%/nginx%') AND (LOWER(process_name) LIKE '%/sh' OR LOWER(process_name) LIKE '%/bash' OR LOWER(process_name) LIKE '%/curl' OR LOWER(process_name) LIKE '%/wget') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (time >= '{{start_date}}' AND time <= '{{end_date}}')
```
## triage-exploitation
<!-- Triage early exploitation -->
```agent target=hunter
cite: required
context:
- scope-vulnerable-runtimes
- http-exploitation-attempts
- rce-shell-execution
max_iterations: 4
objective: Determine if any host with a web runtime shows signs of unauthenticated
remote code execution citing HTTP errors and shell spawning.
success_criteria: Verdicts citing the specific web runtime and the subsequent shell
commands.
tools:
- endpoint
- identity
- network
- web
```
## follow-on-fan-out
<!-- Hunt follow-on activity -->
parallel:
- → mirai-botnet-c2
- → remote-access-persistence
join: → triage-intrusion
## mirai-botnet-c2
<!-- Mirai botnet C2 -->
Detect outbound connections to rare external IPs, indicative of a botnet agent established via the RCE.
```sqlite target=network role=baseline params=(scope_hosts=scope_hosts, start_date=start_date, end_date=end_date)
~~~yaml
baseline:
compare: first_seen
window: 5d
expected: Outbound connections to unique IPs. Mirai often targets non-standard ports
(23, 2323) but may use 80/443.
prevalence:
by: device_hostname
key:
- dst_endpoint_ip
rare_below: 3
reads:
- device_hostname
- dst_endpoint_ip
- dst_endpoint_port
- direction
- time
silence: not_evidence_of_absence
source: hb_network_connection
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT dst_endpoint_ip, dst_endpoint_port, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_network_connection WHERE direction = 'outbound' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (time >= '{{start_date}}' AND time <= '{{end_date}}') GROUP BY dst_endpoint_ip, dst_endpoint_port HAVING host_count <= 2 ORDER BY host_count ASC
```
## remote-access-persistence
<!-- Remote access persistence -->
Identify successful sign-ins to VPN or Citrix gateways from IP addresses involved in the RCE or botnet activity.
```sqlite target=identity role=triage params=(suspicious_ips=suspicious_ips, start_date=start_date, end_date=end_date)
~~~yaml
expected: Successful logons to gateways from external IPs identified as suspicious.
This indicates credential reuse or theft.
reads:
- actor_user_name
- src_endpoint_ip
- provider
- dst_endpoint_name
- time
silence: not_evidence_of_absence
source: hb_auth_signin
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT actor_user_name, src_endpoint_ip, provider, dst_endpoint_name, time FROM hb_auth_signin WHERE (instr(',' || '{{suspicious_ips}}' || ',', ',' || src_endpoint_ip || ',') > 0 OR '{{suspicious_ips}}' = '') AND status_id = 1 AND (time >= '{{start_date}}' AND time <= '{{end_date}}')
```
## triage-intrusion
<!-- Triage intrusion chain -->
```agent target=hunter
cite: required
context:
- triage-exploitation
- mirai-botnet-c2
- remote-access-persistence
max_iterations: 6
objective: Confirm whether the RCE beachhead was used to establish botnet C2 or pivot
to external remote services.
success_criteria: A final verdict identifying compromised hosts, IPs, and accounts
with high confidence.
tools:
- endpoint
- identity
- network
- web
```
## route-on-verdict
<!-- Route on verdict -->
if~: "the triage-intrusion verdict is malicious for at least one host" (confidence: high, judge=hunter)
then: → isolate-and-revoke
indeterminate: → investigate-impact
unavailable: → investigate-impact (blind_spot: brotli-stream-blindspot)
else: → close-out
## isolate-and-revoke
<!-- Isolate and revoke -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the compromised web server from the network and revoke any active VPN or Citrix sessions associated with the user accounts involved in the suspicious sign-ins.
```
→ investigate-impact
## investigate-impact
<!-- Investigate impact -->
```manual target=analyst
Review hb_file_activity on the compromised host for mass modification or renaming (ransomware indicators) and check shell_history for commands executed by the attacker.
```
→ close-out
## close-out
<!-- Close out -->
```manual target=analyst
Confirm that all web runtimes identified in the scoping step have been patched to versions released on or after October 21, 2026. Document any gaps in log visibility found during the triage.
```
→ 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.