← All hunts critical TLP:CLEAR

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

  1. Scope vulnerable runtimes

    Query · scoping

    Identify hosts running targeted web runtimes that were vulnerable during the October 2026 window.

    reads hb_software_inventorysql
    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)

    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.

  2. HTTP exploitation attempts

    Query · enrichment

    Identify HTTP requests targeting web servers that resulted in server errors or heavy POST activity during the zero-day window.

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

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

  3. RCE shell execution

    Query · detection candidate

    Detect instances where a web runtime process spawns a shell or downloader, indicating successful unauthenticated execution.

    reads hb_process_activitysql
    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}}')

    What a hit looks like. A web runtime parent spawning a shell or network utility. This is highly suspicious during a zero-day window.

  4. Triage early exploitation

    Agent triage

    Evaluate whether any host shows signs of successful zero-day compromise based on web errors and process execution.

  5. Mirai botnet C2

    Query · baseline

    Detect outbound connections to rare external IPs, indicative of a botnet agent established via the RCE.

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

    What a hit looks like. Outbound connections to unique IPs. Mirai often targets non-standard ports (23, 2323) but may use 80/443.

  6. Remote access persistence

    Query · triage

    Identify successful sign-ins to VPN or Citrix gateways from IP addresses involved in the RCE or botnet activity.

    reads hb_auth_signinsql
    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}}')

    What a hit looks like. Successful logons to gateways from external IPs identified as suspicious. This indicates credential reuse or theft.

  7. Triage intrusion chain

    Agent triage

    Synthesize early RCE evidence with follow-on C2 and persistence findings to confirm a multi-stage intrusion.

  8. Route on verdict

    Decision

    Determine the response path based on the triage-intrusion results.

  9. Isolate and revoke

    Response action

    Contain the compromise by isolating the host and revoking sessions.

  10. Investigate impact

    Analyst task

    Determine the extent of damage, including potential data encryption or unauthorized file access.

  11. Close out

    Analyst task

    Verify patching status and document the zero-day incident.

Coverage

Scenario coverage

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

ParameterTypeDefaultWhat it is
end_datestring2026-10-21 23:59:59The date the official patch was released.
runtime_packageslist[string]php, node, nodejs, bunPackage names representing the targeted web runtimes.
scope_hostslist[host]—Hostnames to focus the investigation; leave empty to hunt across the entire estate.
start_datestring2026-10-17 00:00:00The date active zero-day exploitation was first observed.
suspicious_ipslist[ip]—IP addresses identified as suspicious in the first phase; leave empty to search all authentication attempts during the window.

Telemetry

SourceCategoryTelemetry
Endpoint telemetry (hb_ surfaces)endpointendpoint
Identity / sign-in telemetryidentityidentity
Network telemetrynetworknetwork
Web server / proxy logssiemnetwork

Source

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

Locked pack

Sign up to unlock

Pack contents are available to Huntbase users.