← All hunts high TLP:CLEAR Part 1 of 2

PHP2Shell: Web-to-Shell Staging

An attacker is exploiting PHP core RCE vulnerabilities (CVE-2026-109618/19) to force web server processes to spawn shells or interpreters, which then perform system discovery and payload staging.

Based on research by Elastic Security Labs 2026-10-22 11 steps · 4 queries T1059 T1082 T1087 T1105 T1190 T1505.003

Brief

Why hunt for PHP2Shell

Recent research from Elastic Security Labs, titled php2shell: Assessing Elastic detection coverage relevant to CVE-2026-109618 and CVE-2026-109619, details critical vulnerabilities in the PHP core. These flaws allow unauthenticated attackers to execute arbitrary code. While static detection rules often catch known web shells, this hunt focuses on the broader behavioral footprint of a compromised web worker. We want to find the transition from a web server process to a shell or interpreter, even if the specific exploit payload is new or obfuscated.

How the Hunt Flows

The hunt begins by identifying vulnerable assets. The first query searches the vulnerability surface to find hosts where the target CVEs remain unpatched. This scoping step ensures analysts prioritize high-risk servers, though the hunt can also run across the entire estate to find undocumented or shadow PHP deployments.

The second phase analyzes early execution in two parallel paths. One path looks for direct shell launches from web server processes like nginx, apache2, or php-fpm. The second path performs a frequency analysis of all children spawned by web processes. By stack-counting these relationships across the fleet, the hunt highlights rare pivots—such as a single web server launching an unusual binary—that traditional rules might miss due to their low volume or lack of a known-bad signature.

An analyst then triages these execution hits. They evaluate the command lines to distinguish legitimate administrative scripts from malicious activity. Once a suspicious host is identified, the hunt searches for follow-on staging behavior. This query looks for discovery tools and downloaders like wget, python, or hostname on the targeted systems. The hunt concludes by correlating the initial shell launch with these staging actions to provide a final verdict.

Why this is a hunt, not a detection

Detection rules for web-to-shell activity often generate high noise in environments where administrators run maintenance scripts or backups directly through web worker contexts. This hunt uses fleet-wide prevalence and human triage to separate that administrative noise from an actual exploit. It looks for the convergence of a vulnerability, a rare process pivot, and follow-on discovery, rather than just a single process event.

Blind Spots and Limitations

This hunt relies heavily on process telemetry. If a web server is not enrolled in endpoint monitoring, it remains a complete blind spot. Additionally, sophisticated attackers might execute code entirely within the PHP worker process memory without spawning a child process. Such memory-only exploitation is invisible to the process-launch telemetry used here and requires specialized engine-level tracing to detect.

In this series

Steps

  1. Identify vulnerable PHP assets

    Query · scoping

    Locate hosts with active vulnerability findings for the PHP2Shell CVEs to focus the hunt.

    reads hb_vulnerability_findingsql
    SELECT resource_uid, device_uid, cve_uid, severity, status FROM hb_vulnerability_finding WHERE instr(',' || '{{cve_ids}}' || ',', ',' || cve_uid || ',') > 0 AND status != 'suppressed'

    What a hit looks like. Rows identify specific device UIDs and resources that are unpatched for the target CVEs. Silence suggests a lower exposure, but is not evidence of absence if scans are not current.

  2. Web workers spawning shells

    Query · detection candidate

    Detect the most direct indicator of RCE: a web worker process spawning a shell.

    reads hb_process_activitysql
    SELECT device_hostname, parent_process_name, process_name, process_cmd_line, time FROM hb_process_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (instr(',' || '{{web_server_parents}}' || ',', ',' || LOWER(parent_process_name) || ',') > 0 OR LOWER(parent_process_name) LIKE 'php-fpm%' OR LOWER(parent_process_name) LIKE 'lsphp%') AND instr(',' || '{{shell_names}}' || ',', ',' || LOWER(process_name) || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days')

    What a hit looks like. A shell running as a child of a web server. Benign hits include maintenance scripts or scheduled backups.

  3. Rare web server children

    Query · baseline

    Stack-count children of web processes to identify rare pivots across the fleet that behavior rules might miss.

    reads hb_process_activitysql
    SELECT LOWER(parent_process_name) AS parent, LOWER(process_name) AS child, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_process_activity WHERE (instr(',' || '{{web_server_parents}}' || ',', ',' || LOWER(parent_process_name) || ',') > 0 OR LOWER(parent_process_name) LIKE 'php-fpm%' OR LOWER(parent_process_name) LIKE 'lsphp%') AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY 1, 2 HAVING hosts <= 2

    What a hit looks like. One or two hosts where a web process spawns an unusual child. Silence means the web tier behaves uniformly across the fleet.

  4. Triage early execution

    Agent triage

    Evaluate whether the shells or rare children found represent legitimate administrative tasks or high-risk post-exploitation.

  5. Follow-on discovery and staging

    Query · triage

    Search for follow-on actions (enumeration and downloading) on the hosts identified in the early triage.

    reads hb_process_activitysql
    SELECT device_hostname, parent_process_name, process_name, process_cmd_line, time FROM hb_process_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (instr(',' || '{{staging_tools}}' || ',', ',' || LOWER(process_name) || ',') > 0 OR LOWER(process_cmd_line) LIKE '%/etc/os-release%' OR LOWER(process_cmd_line) LIKE '%/etc/shadow%') AND time >= datetime('now', '-{{lookback_days}} days')

    What a hit looks like. Discovery commands like whoami or downloaders like curl running on the same hosts that showed suspicious early execution.

  6. Correlate end-to-end execution

    Agent triage

    Combine the early execution patterns with follow-on staging evidence to reach a final verdict.

  7. Route on final verdict

    Decision

    Direct confirmed compromises to immediate containment.

  8. Isolate compromised host

    Response action

    Sever the attacker's staging path by isolating the host.

  9. Manual forensic review

    Analyst task

    Verify the entry point and assess the extent of the staging.

  10. Patch and remediate

    Analyst task

    Apply long-term fixes to prevent re-exploitation.

Coverage

Scenario coverage

StageCoveredHow, or why not
PHP core RCE exploitation
T1190
Yes vulnerable-php-assets
Web worker spawning shell
T1059
Yes web-worker-shell, rare-web-children
System and identity enumeration
T1087 · T1082
Yes follow-on-staging
Payload staging and interpreter pivot
T1059 · T1105
Yes follow-on-staging
Reverse shell communication
T1071.001
Out of scope Belongs to another part of the 'php2shell: Assessing Elastic detection coverage relevant to CVE-2026-109618 and CVE-2026-109619' series.
Credential and identity harvesting
T1552.001 · T1552.005
Out of scope Belongs to another part of the 'php2shell: Assessing Elastic detection coverage relevant to CVE-2026-109618 and CVE-2026-109619' series.
Webshell and system persistence
T1505.003 · T1133 · T1053.003
Out of scope Belongs to another part of the 'php2shell: Assessing Elastic detection coverage relevant to CVE-2026-109618 and CVE-2026-109619' series.

Blind spots

  • Needs Complete endpoint agent coverage on all web-facing assets.. A host without an agent provides no process telemetry, making it a complete blind spot for behavioral execution hunting. It would answer Are there web servers in the estate that are not enrolled in endpoint monitoring?.
  • Needs PHP engine-level tracing or APM instrumentation.. Sophisticated exploits staying within the process address space are invisible to process-launch telemetry. It would answer Did the attacker execute malicious code entirely within the PHP worker process memory without spawning a child?.

Parameters & data

Parameters

ParameterTypeDefaultWhat it is
cve_idslist[string]CVE-2026-109618, CVE-2026-109619The PHP core RCE identifiers.
lookback_daysnumber14Days of history to examine.
scope_hostslist[host]—Optional list of hostnames to narrow the search; leave empty to hunt across the entire estate.
shell_nameslist[string]bash, dash, sh, zsh, kshStandard Unix shells used for post-exploitation.
staging_toolslist[string]curl, wget, python, python3, perl, ruby, node, php, id, whoami, uname, hostnameInterpreters, downloaders, and discovery tools used for staging.
web_server_parentslist[string]apache2, httpd, nginx, php-cgiCommon PHP-driving web server process names.

Telemetry

SourceCategoryTelemetry
Endpoint telemetry (hb_ surfaces)endpointendpoint

Source

Download hunt.md Definition (JSON) An open hunt.md file; it runs anywhere that reads the format.
---
analysis: While static rules catch common shells, this hunt adds context by stack-counting
  rare children across the fleet and correlating vulnerability finding data with staging
  tools, distinguishing true compromises from administrative noise.
blind_spots:
- id: no-agent-coverage
  question: Are there web servers in the estate that are not enrolled in endpoint
    monitoring?
  requires: Complete endpoint agent coverage on all web-facing assets.
  risk: A host without an agent provides no process telemetry, making it a complete
    blind spot for behavioral execution hunting.
  stage: execution-web-worker-shell
- id: memory-only-exploitation
  question: Did the attacker execute malicious code entirely within the PHP worker
    process memory without spawning a child?
  requires: PHP engine-level tracing or APM instrumentation.
  risk: Sophisticated exploits staying within the process address space are invisible
    to process-launch telemetry.
  stage: initial-access-php-rce
coverage:
- stage: initial-access-php-rce
  status: covered
  steps:
  - vulnerable-php-assets
- stage: execution-web-worker-shell
  status: covered
  steps:
  - web-worker-shell
  - rare-web-children
- stage: discovery-system-enumeration
  status: covered
  steps:
  - follow-on-staging
- stage: execution-payload-staging
  status: covered
  steps:
  - follow-on-staging
- reason: 'Belongs to another part of the ''php2shell: Assessing Elastic detection
    coverage relevant to CVE-2026-109618 and CVE-2026-109619'' series.'
  stage: c2-reverse-shell-primitives
  status: out_of_scope
- reason: 'Belongs to another part of the ''php2shell: Assessing Elastic detection
    coverage relevant to CVE-2026-109618 and CVE-2026-109619'' series.'
  stage: credential-access-harvesting
  status: out_of_scope
- reason: 'Belongs to another part of the ''php2shell: Assessing Elastic detection
    coverage relevant to CVE-2026-109618 and CVE-2026-109619'' series.'
  stage: persistence-webshell-and-system
  status: out_of_scope
guardrails:
  claims: no_unsupported
  evidence: citation_required
  missing_data: not_benign
  telemetry: untrusted
hunt:
  applicability: campaign-specific
  handoff: keep-as-periodic-hunt
  justification: PHP core RCE flaws represent a massive cross-platform exposure; confirming
    the absence of the convergent 'web-to-shell' execution pattern across the estate
    is a high-value negative result for the business.
  methodology: model-assisted
  trigger: intel-report
hypothesis: An attacker is exploiting PHP core RCE vulnerabilities (CVE-2026-109618/19)
  to force web server processes to spawn shells or interpreters, which then perform
  system discovery and payload staging.
labels:
- hunt
- attack.t1059
- attack.t1190
- attack.t1505.003
- attack.t1087
- attack.t1082
- attack.t1105
- command and control
- credential access
- discovery
- execution
- initial access
- persistence
name: 'PHP2Shell: Web-to-Shell Staging'
parameters:
  cve_ids:
    default:
    - CVE-2026-109618
    - CVE-2026-109619
    description: The PHP core RCE identifiers.
    from:
      kind: article
      observed: '2026-10-21'
      ref: https://www.elastic.co/security-labs/threat-command/php2shell-php-rce-detection
    type: list[string]
  lookback_days:
    default: '14'
    description: Days of history to examine.
    type: number
  scope_hosts:
    default: []
    description: Optional list of hostnames to narrow the search; leave empty to hunt
      across the entire estate.
    type: list[host]
  shell_names:
    default:
    - bash
    - dash
    - sh
    - zsh
    - ksh
    description: Standard Unix shells used for post-exploitation.
    type: list[string]
  staging_tools:
    default:
    - curl
    - wget
    - python
    - python3
    - perl
    - ruby
    - node
    - php
    - id
    - whoami
    - uname
    - hostname
    description: Interpreters, downloaders, and discovery tools used for staging.
    type: list[string]
  web_server_parents:
    default:
    - apache2
    - httpd
    - nginx
    - php-cgi
    description: Common PHP-driving web server process names.
    type: list[string]
provenance:
  authors:
  - name: Huntbase hunt generation
    org: huntbase.io
  generated:
    by: huntbase-hunt-generation
    from: https://www.elastic.co/security-labs/threat-command/php2shell-php-rce-detection
    gates:
    - dry-run
    - lint
    model: hb_google/gemini-3-flash-preview
rationale: Focus on internet-facing assets running common PHP platforms like WordPress,
  Magento, or custom web applications. Priority should be given to servers running
  mod_php or PHP-FPM without container isolation.
references:
- name: "Elastic Security Labs \u2014 php2shell: Assessing Elastic detection coverage\
    \ relevant to CVE-2026-109618 and CVE-2026-109619"
  url: https://www.elastic.co/security-labs/threat-command/php2shell-php-rce-detection
related:
- hunt: php2shell-persistence-and-c2
  reason: This hunt focuses on the beachhead; a follow-on hunt is required for reverse
    shells and persistence mechanisms.
  relation: follows
scenario:
  stages:
  - name: PHP core RCE exploitation
    observables:
    - CVE-2026-109618
    - CVE-2026-109619
    - unauthenticated HTTP request targeting PHP worker (php-fpm, mod_php, php-cgi)
    slug: initial-access-php-rce
    tactic: initial-access
    techniques:
    - T1190
  - name: Web worker spawning shell
    observables:
    - parent_process_name in ('apache2', 'httpd', 'nginx', 'php-cgi', 'php-fpm', 'lsphp')
    - process_name in ('bash', 'dash', 'sh', 'zsh', 'ksh')
    - process_args in ('-c', '-cl', '-lc')
    slug: execution-web-worker-shell
    tactic: execution
    techniques:
    - T1059
  - name: System and identity enumeration
    observables:
    - id
    - whoami
    - uname -a
    - hostname
    - cat /etc/os-release
    - sudo -l
    - find / -perm -u=s -type f
    - getcap -r /
    - reading /etc/passwd
    - reading /etc/shadow
    slug: discovery-system-enumeration
    tactic: discovery
    techniques:
    - T1087
    - T1082
  - name: Payload staging and interpreter pivot
    observables:
    - python3 -c
    - perl -e
    - ruby -e
    - node -e
    - php -r
    - curl -O
    - wget -O
    - echo <b64> | base64 -d | sh
    - openssl enc -d
    - xxd -r
    slug: execution-payload-staging
    tactic: execution
    techniques:
    - T1059
    - T1105
  - name: Reverse shell communication
    observables:
    - /dev/tcp/
    - mkfifo
    - nc
    - socat
    - pty.spawn
    - fsockopen
    slug: c2-reverse-shell-primitives
    tactic: command-and-control
    techniques:
    - T1071.001
  - name: Credential and identity harvesting
    observables:
    - reading .env files
    - reading ~/.aws/credentials
    - accessing /var/run/secrets/kubernetes.io/
    - HTTP request to 169.254.169.254 (IMDS)
    slug: credential-access-harvesting
    tactic: credential-access
    techniques:
    - T1552.001
    - T1552.005
  - name: Webshell and system persistence
    observables:
    - writing .php or .phtml to web root
    - avatar.php.jpg (polyglot/double extension)
    - adding cron entries
    - creating systemd units
    - modifying ~/.ssh/authorized_keys
    slug: persistence-webshell-and-system
    tactic: persistence
    techniques:
    - T1505.003
    - T1133
    - T1053.003
  summary: Unauthenticated remote code execution vulnerabilities in PHP core (CVE-2026-109618
    and CVE-2026-109619) allow attackers to gain code execution in the context of
    web worker processes. Post-exploitation involves web servers spawning shells for
    system discovery, staging second-stage payloads via interpreters, and establishing
    persistence through webshells or system configuration.
series:
  index: 1
  slug: php2shell-assessing-elastic-detection-coverage-relevant-to-cve-2026-109618-and-cve-2026-109619
  title: 'php2shell: Assessing Elastic detection coverage relevant to CVE-2026-109618
    and CVE-2026-109619'
  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
tlp: clear
type: investigation
---


# PHP2Shell: Web-to-Shell Staging

This hunt targets the convergent post-exploitation footprint of PHP core vulnerabilities, where unauthenticated requests trigger a PHP worker to execute arbitrary code. The hunt begins by identifying vulnerable assets and then uses a phased approach to detect the transition from web server processes (like php-fpm or nginx) to shells and language interpreters. By stack-counting unusual parent-child relationships and corroborating them with discovery commands and staging tools, the hunt identifies active compromises where traditional static rules might be bypassed.

## vulnerable-php-assets
<!-- Identify vulnerable PHP assets -->
Locate hosts with active vulnerability findings for the PHP2Shell CVEs to focus the hunt.

```sqlite target=endpoint role=scoping params=(cve_ids=cve_ids)
~~~yaml
expected: Rows identify specific device UIDs and resources that are unpatched for
  the target CVEs. Silence suggests a lower exposure, but is not evidence of absence
  if scans are not current.
reads:
- resource_uid
- device_uid
- cve_uid
- severity
- status
silence: not_evidence_of_absence
source: hb_vulnerability_finding
verified: dry-run
verified_at: '2026-10-22'
~~~
SELECT resource_uid, device_uid, cve_uid, severity, status FROM hb_vulnerability_finding WHERE instr(',' || '{{cve_ids}}' || ',', ',' || cve_uid || ',') > 0 AND status != 'suppressed'
```

## early-execution-parallel
<!-- Early execution analysis -->
parallel:
- → web-worker-shell
- → rare-web-children
join: → agent-triage-early

## web-worker-shell
<!-- Web workers spawning shells -->
Detect the most direct indicator of RCE: a web worker process spawning a shell.

```sqlite target=endpoint role=detection-candidate params=(scope_hosts=scope_hosts, web_server_parents=web_server_parents, shell_names=shell_names, lookback_days=lookback_days)
~~~yaml
expected: A shell running as a child of a web server. Benign hits include maintenance
  scripts or scheduled backups.
reads:
- device_hostname
- parent_process_name
- process_name
- process_cmd_line
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-10-22'
~~~
SELECT device_hostname, parent_process_name, process_name, process_cmd_line, time FROM hb_process_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (instr(',' || '{{web_server_parents}}' || ',', ',' || LOWER(parent_process_name) || ',') > 0 OR LOWER(parent_process_name) LIKE 'php-fpm%' OR LOWER(parent_process_name) LIKE 'lsphp%') AND instr(',' || '{{shell_names}}' || ',', ',' || LOWER(process_name) || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days')
```

## rare-web-children
<!-- Rare web server children -->
Stack-count children of web processes to identify rare pivots across the fleet that behavior rules might miss.

```sqlite target=endpoint role=baseline params=(web_server_parents=web_server_parents, lookback_days=lookback_days)
~~~yaml
baseline:
  compare: first_seen
  window: '{{lookback_days}}d'
expected: One or two hosts where a web process spawns an unusual child. Silence means
  the web tier behaves uniformly across the fleet.
prevalence:
  by: device_hostname
  key:
  - parent_process_name
  - process_name
  rare_below: 3
reads:
- device_hostname
- parent_process_name
- process_name
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-10-22'
~~~
SELECT LOWER(parent_process_name) AS parent, LOWER(process_name) AS child, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_process_activity WHERE (instr(',' || '{{web_server_parents}}' || ',', ',' || LOWER(parent_process_name) || ',') > 0 OR LOWER(parent_process_name) LIKE 'php-fpm%' OR LOWER(parent_process_name) LIKE 'lsphp%') AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY 1, 2 HAVING hosts <= 2
```

## agent-triage-early
<!-- Triage early execution -->
```agent target=hunter
cite: required
context:
- vulnerable-php-assets
- web-worker-shell
- rare-web-children
max_iterations: 4
objective: Identify hosts where the transition from a web process to a child (shell
  or rare binary) is suspicious, citing specific rows and process command lines.
success_criteria: A list of hosts and processes that warrant follow-on hunting for
  staging behavior.
tools:
- endpoint
```

## follow-on-staging
<!-- Follow-on discovery and staging -->
Search for follow-on actions (enumeration and downloading) on the hosts identified in the early triage.

```sqlite target=endpoint role=triage params=(scope_hosts=scope_hosts, staging_tools=staging_tools, lookback_days=lookback_days)
~~~yaml
expected: Discovery commands like whoami or downloaders like curl running on the same
  hosts that showed suspicious early execution.
reads:
- device_hostname
- parent_process_name
- process_name
- process_cmd_line
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-10-22'
~~~
SELECT device_hostname, parent_process_name, process_name, process_cmd_line, time FROM hb_process_activity WHERE ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND (instr(',' || '{{staging_tools}}' || ',', ',' || LOWER(process_name) || ',') > 0 OR LOWER(process_cmd_line) LIKE '%/etc/os-release%' OR LOWER(process_cmd_line) LIKE '%/etc/shadow%') AND time >= datetime('now', '-{{lookback_days}} days')
```

## agent-final-verdict
<!-- Correlate end-to-end execution -->
```agent target=hunter
cite: required
context:
- agent-triage-early
- follow-on-staging
max_iterations: 4
objective: Determine if the suspicious initial execution and subsequent staging activity
  on a host indicate a successful PHP RCE compromise. Prioritize hosts where both
  stages are present.
success_criteria: A final verdict of malicious | suspicious | benign for each examined
  host, citing the complete process chain.
tools:
- endpoint
```

## decision-route
<!-- Route on final verdict -->
if~: "the verdict of agent-final-verdict is malicious for at least one host" (confidence: high, judge=hunter)
then: → action-isolate
indeterminate: → task-manual-review
unavailable: → task-manual-review (blind_spot: no-agent-coverage)
else: → task-manual-review

## action-isolate
<!-- Isolate compromised host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the host and preserve memory for forensic analysis to identify the specific PHP script that was targeted.
```
→ task-manual-review

## task-manual-review
<!-- Manual forensic review -->
```manual target=analyst
Review the hb_process_activity and hb_linux_audit logs for the isolated host. Cross-reference the shell launch time with hb_http_activity to find the unauthenticated request that triggered the RCE.
```
→ task-remediation

## task-remediation
<!-- Patch and remediate -->
```manual target=analyst
Update PHP to versions 8.5.12, 8.4.27, 8.3.36, or 8.2.35 across all identified assets.
```
→ 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.