name: Web Server or PHP Worker Spawning Suspicious Command Process Analytic
signatureid: EDR-NIX5-RUN
category: 'Execution'
threatname: 'Command & Scripting Interpreter: Unix Shell'
functionality: 'Endpoint Management Systems'
lifecycle: experimental
version: 20261022.5
description: |
Detects a web server daemon or PHP-FPM worker spawning a shell with execution-oriented
arguments, a network transfer or tunneling utility, or a general-purpose interpreter with
suspicious inline-code, remote-resource, or temporary-path arguments. These process-tree
shapes are durable post-exploitation evidence for server-side remote code execution and are
not tied to a particular exploit payload.
This Delta includes coverage for the unauthenticated PHP core RCE vulnerabilities
CVE-2026-109618 and CVE-2026-109619 (php2shell). The PHP project confirmed exploitation can
execute code as the web worker account across PHP web SAPIs, with the highest demonstrated
risk under Apache mod_php. CISA specifically recommends monitoring PHP workers launching
shells or downloading tools. It also retains the web-server reverse-shell behavior originally
covered for XWiki CVE-2025-24893.
Sysmon for Linux Event ID 1 or equivalent process-creation telemetry is required. PHP-FPM is
matched with a substring because common Linux packages use version-suffixed binaries such as
/usr/sbin/php-fpm8.3. Shells are not matched on process name alone: their command lines must
show command execution, interactive use, network redirection, transfer, decoding, or temporary
staging. Transfer/tunneling tools are high-signal direct children of web-facing workers.
The parent condition can also be satisfied by the web service account the worker runs as.
That branch exists because Sysmon for Linux does not RELIABLY resolve ParentImage for a
forked web worker: whether it resolves depends on whether that worker PID has already
produced an instrumented event of its own. See the measured behavior in
master_meta.technical_details before deploying.
reference:
- https://www.php.net/archive/2026.php#2026-10-21-5
- https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
- https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
- https://www.cisa.gov/news-events/alerts/2026/10/21/critical-vulnerabilities-php-require-immediate-patching
- https://www.vulncheck.com/blog/xwiki-under-increased-attack
- https://www.vulncheck.com/blog/xwiki-cve-2025-24893-eitw
- https://github.com/bobby-tablez/FT-Sysmon-Config
labels:
- attack.execution
- attack.t1059
- attack.t1059.004
- attack.command_and_control
- attack.t1105
- attack.t1190
- CVE_2026_109618
- CVE_2026_109619
- CVE_2025_24893
- PHP
- PHP_FPM
- Post_Exploitation
logsource:
category: process_creation
product: linux
detection:
selection_webserver_parent_image:
ParentImage|endswith:
- '/java'
- '/java.bin'
- '/httpd'
- '/apache2'
- '/nginx'
- '/tomcat'
- 'catalina.sh'
selection_phpfpm_parent_image:
ParentImage|contains: '/php-fpm'
selection_webserver_parent_cmdline:
ParentProcessCommandLine|contains:
- 'httpd'
- 'apache2'
- 'nginx'
- 'tomcat'
- 'catalina'
- 'php-fpm'
selection_web_service_account:
AccountName:
- 'www-data'
- 'apache'
- 'apache2'
- 'httpd'
- 'nginx'
- 'http'
selection_shell:
Image|endswith:
- '/bash'
- '/sh'
- '/zsh'
- '/dash'
- '/ksh'
selection_shell_suspicious_arguments:
ChildProcessCommandLine|contains:
- ' -c '
- ' -i'
- '/dev/tcp/'
- '/dev/udp/'
- '>&'
- 'curl '
- 'wget '
- 'base64'
- '/tmp/'
- '/var/tmp/'
- '/dev/shm/'
selection_interpreter:
Image|endswith:
- '/python'
- '/python3'
- '/perl'
- '/ruby'
- '/php'
selection_interpreter_suspicious_arguments:
ChildProcessCommandLine|contains:
- ' -c '
- ' -e '
- 'base64'
- 'exec('
- 'http://'
- 'https://'
- '/tmp/'
- '/var/tmp/'
- '/dev/shm/'
selection_transfer_or_tunnel_tool:
Image|endswith:
- '/curl'
- '/wget'
- '/nc'
- '/ncat'
- '/netcat'
- '/socat'
condition: (selection_webserver_parent_image or selection_phpfpm_parent_image or selection_webserver_parent_cmdline or selection_web_service_account) and ((selection_shell and selection_shell_suspicious_arguments) or (selection_interpreter and selection_interpreter_suspicious_arguments) or selection_transfer_or_tunnel_tool)
criticality: High
saveasthreat: true
violation_summary:
grouping_attribute: 'AccountName'
level2_attribute: 'DeviceHostName'
level2_metadata_attributes:
metadata_attributes:TECHNICAL DETAILS
COVERAGE INTENT
---------------
This is a behavior-level post-exploitation analytic. It does not attempt to identify the
multipart heap-shaping requests used by CVE-2026-109618 or the producer/consumer request
sequence used by CVE-2026-109619. Those request mechanics require multipart-part and worker-
affinity fields that ordinary web access logs do not expose reliably. Instead, this rule
detects the durable endpoint consequence: a web-facing worker launching tooling associated
with command execution, payload retrieval, tunneling, or staged interpreter execution.
PHP PARENT PROCESS HANDLING
---------------------------
Apache mod_php executes PHP inside the Apache worker, so the resulting process parent
resolves to httpd or apache2. That was an expectation up to version 20261022.3 and is a
measurement from 20261022.4: see APACHE MOD_PHP PREFORK - MEASURED 2026-10-23 below. On
Ubuntu the binary is /usr/sbin/apache2 and the shipped ParentImage is exactly that, so the
match is carried by the '/apache2' value of selection_webserver_parent_image; the '/httpd'
value covers the RHEL-family path and remains unexercised. PHP-FPM uses a separate worker
binary. Distribution packages commonly suffix that binary with the PHP version, for example
php-fpm8.3, so the PHP-FPM selector uses ParentImage|contains: '/php-fpm' rather than a
fixed |endswith value. ParentProcessCommandLine is retained as a fallback where the parser
populates the parent command line but not ParentImage.
SYSMON FOR LINUX PARENT RESOLUTION - MEASURED BEHAVIOR
------------------------------------------------------
Emulated on range AURORA01 on 2026-10-22 and captured through the collector; the trace is
cited in master_meta.ATD. A php-fpm8.3 pool worker on Ubuntu 24.04 executed one fixed inert
child in response to an HTTP request routed through an nginx/ModSecurity WAF. Sysmon for
Linux Event ID 1 shipped the following to the collector:
Image /usr/bin/dash
CommandLine sh -c -- true
User www-data
ParentProcessId 15828 (the php-fpm pool worker)
CurrentDirectory /var/www/petalstem
ParentImage -
ParentCommandLine -
ParentImage and ParentCommandLine are the literal string '-', not the worker path. Sysmon
for Linux resolves parent image and parent command line from an internal per-PID process
cache. PHP-FPM pool workers and nginx worker processes are created by fork(2) from their
master process and never call execve(2), so a worker that has not produced an instrumented
event of its own is absent from that cache and its children are shipped with no parent
identity at all.
Measured on the same host: only three Event ID 1 records exist with
Image /usr/sbin/php-fpm8.3, and all three are the MASTER process (ParentProcessId 1, started
by systemd). Pool workers 15827 and 15828, and 55327 and 55328 after a deliberate service
restart, have no process-creation record whatsoever. Across the capture window, 4135 of the
4226 process-creation records carrying a ParentImage element shipped it as '-', or 97.8
percent.
CORRECTION AT VERSION 20261022.3 - RESOLUTION IS CONDITIONAL, NOT IMPOSSIBLE
---------------------------------------------------------------------------
Versions up to and including 20261022.2 concluded from the measurements above that
selection_webserver_parent_image, selection_phpfpm_parent_image and
selection_webserver_parent_cmdline are all "unsatisfiable for a PHP-FPM, nginx-worker or
Apache-prefork parent on this telemetry". That conclusion was too strong and is retracted
here. The 97.8 percent measurement itself is reproduced and stands; the absolute inference
drawn from it does not.
A controlled experiment on range AURORA01 on 2026-10-22, cited under SUPPORTING TRACES,
isolated the variable. A temporary one-worker static pool (pm = static, pm.max_children = 1)
was configured so that worker identity is a measured fact rather than an inference, and
php8.3-fpm was restarted to obtain exactly one fresh worker, PID 9351, with no prior
instrumented activity of any kind. Three requests were then driven through the WAF, eight
seconds apart, every one of them served by PID 9351:
1. an inert /bin/echo child -> ParentProcessId 9351, ParentImage '-'
2. a warm action performed by the worker itself: one EID 3 NetworkConnect to a host-local
address, plus an EID 11 FileCreate and EID 23 FileDelete pair on a /tmp marker, all
under PID 9351. These are the ONLY instrumented events for that PID between steps 1
and 3.
3. a second inert /bin/echo child -> ParentProcessId 9351,
ParentImage /usr/sbin/php-fpm8.3
Same worker PID, same host, same pool, same Sysmon for Linux v1.5.2. The only intervening
change was that the worker PID had by then produced instrumented events of its own.
Sysmon for Linux therefore enriches ParentImage from a per-PID cache populated by that PID's
own earlier instrumented activity of ANY event type. fork-without-execve explains why a
pool worker is usually absent from that cache, but it is not a permanent disqualification.
This confirms the mechanism asserted by EDR-NIX29-RUN and EDR-NIX31-RUN. EDR-NIX31-RUN
recorded that mechanism as an operational inference that was "strong, but not proven from
Sysmon for Linux's source"; it is now proven by direct measurement, and no correction is
owed to either of those Deltas. A cross-check against the CITY01 archive closes the
remaining ambiguity there as well: a grep that pins the element name immediately before the
value (the literal text ParentImage followed by /usr/sbin/php-fpm8.3) returns one match on
line 1 of EDR-NIX29-RUN_evidence.txt, Hostname WEB01, 2026-10-01T15:40:59. A prior audit
could only establish that the php-fpm path sat inside some XML Data element; the element is
now read directly as ParentImage.
Consequence for this Delta, stated precisely: the three original parent branches ARE
satisfiable and are not dead code, but they are UNRELIABLE for a forked web worker. They
fire only when the worker PID happens to have produced an earlier instrumented event. In
ordinary operation a pool worker that merely serves HTTP produces no Sysmon event of its
own, which is exactly why 97.8 percent of records shipped '-'. Both measurements are true
at once: the branches work when the cache is warm for that PID and are silent when it is
not. Which case a given event is in is not stated in the record, but it is predictable from
the SAPI's accept transport - see the Apache measurement below, where the worker accepts TCP
itself and is therefore always warm. '/java' and '/tomcat' are unaffected either way,
because a JVM is exec'd rather than forked, which is why the java parent anchor in
EDR-NIX27-RUN never depended on this behavior.
selection_web_service_account is therefore added as a fourth, parent-image-independent way
to satisfy the parent condition, keyed on the service account the web worker runs as. It is
purely additive - where a parser does populate ParentImage, the original three branches
continue to work exactly as before. This correction strengthens rather than weakens its
justification: a parent-image-independent branch is what keeps the Delta working across both
cache states.
CurrentDirectory is also present in the raw record and would discriminate a web-application
parent context well, but the Endpoint Management Systems functionality publishes no
CurrentDirectory attribute, so it cannot be used in a selector for this datasource.
APACHE MOD_PHP PREFORK - MEASURED 2026-10-23
--------------------------------------------
Run php2shell-apacheprefork-capture-20261023 on range AURORA01 closed the one branch left
unexercised at version 20261022.3. Host aurora01-web02 was given Apache 2.4.58 with
mpm_prefork_module and libapache2-mod-php8.3 8.3.6-0ubuntu0.24.04.13 on port 8081, SAPI
apache2handler, alongside the unchanged nginx/PHP-FPM stack. A dedicated vhost
/var/www/aurora-prefork was configured for exactly one persistent worker (StartServers,
MinSpareServers, MaxSpareServers and MaxRequestWorkers all 1, MaxConnectionsPerChild 0), so
worker identity is a measured fact. A token-gated non-parameterized endpoint spawned one
fixed direct child with the PHP proc_open array form, which execs without /bin/sh.
Two children were spawned by the same worker PID 3610, seventeen seconds apart, with a
deliberate warming action by that worker in between. Both shipped identically:
Image /usr/bin/dash
CommandLine /bin/sh -c -- true P2SAPACHE20261023
User www-data
ParentProcessId 3610 (the Apache prefork worker)
CurrentDirectory /var/www/aurora-prefork
ParentImage /usr/sbin/apache2
ParentCommandLine /usr/sbin/apache2 -k start
ParentUser www-data
So selection_webserver_parent_image is satisfied by shipped data through its '/apache2'
value, and selection_webserver_parent_cmdline and selection_web_service_account are
satisfied on the same records. No selector change is owed: the Apache expectation recorded
since 20261022.1 is confirmed as written.
THE PREDICTED COLD CASE DOES NOT EXIST UNDER MOD_PHP, AND THAT IS THE REAL FINDING
----------------------------------------------------------------------------------
The run was designed to catch an unwarmed Apache worker, expecting ParentImage '-' on the
first child and /usr/sbin/apache2 on a second child after a warming action, mirroring the
PHP-FPM experiment above. That prediction is FALSE, for a structural reason worth recording.
An Apache mpm_prefork worker accepts the inbound TCP connection ITSELF, and Sysmon for Linux
logs that accept as an Event ID 3 with the worker as subject process and Initiated=false.
The worker therefore produces an instrumented event of its own on every single request it
serves, including its first, so its per-PID cache entry exists before PHP ever executes.
Measured: 11 inbound-accept records in the window carry Image /usr/sbin/apache2 and
Initiated=false, and the replacement worker 3610 produced its first one 1.0 second after it
was forked, on its first ever request. Exactly 1 record carries Initiated=true, which is the
deliberate warm action.
A PHP-FPM pool worker accepts on a UNIX socket because nginx terminates the TCP connection,
which produces no NetworkConnect event at all. The operative difference between the two
SAPIs is therefore the transport the worker accepts on, not fork-versus-exec alone. The
practical consequence for a rule author is the opposite of the PHP-FPM case: under Apache
mod_php on a TCP listener, the parent branches are the reliable path and the account branch
is redundant insurance rather than the load-bearing one.
One residual unknown is recorded honestly. Worker 3610 had already accepted five connections
before the first child was spawned, so this run cannot separate "warmed by its own accept in
the same request" from "warmed by an earlier accept". The accept for the child-spawning
request preceded that child by 7 milliseconds, so same-request warming is fast enough in
principle. Settling it requires a worker whose first ever accepted request is the
child-spawning one, which needs a second controlled worker termination and was outside the
one-termination limit set for this run.
NEGATIVE AND SPECIFICITY CONTROLS, 2026-10-23
---------------------------------------------
Exactly three records in the window carry the fixed child command line, and they are the two
Apache children plus one control. The absence proof is that count: a stock static-page
request, an invalid-token request that still carried the valid action keyword (HTTP 403), and
a valid-token request with no action keyword (HTTP 200, no action) each spawned nothing.
The specificity control is the same child run as www-data from sudo with no inbound HTTP
request. It ships ParentImage /usr/bin/sudo and ParentCommandLine 'sudo', so the
parent-image branch does not match it, while selection_web_service_account does. This is the
exposure recorded under FALSE-POSITIVE CONTROL reproduced on Apache, and it is also the
reason the account branch needs baselining wherever ParentImage is reliable.
ABNORMAL WORKER TERMINATION IS NOT A DELTA SIGNAL - RESEARCH, NOT COVERAGE
-------------------------------------------------------------------------
The same run produced one real but controlled abnormal termination: SIGABRT to the sole
Apache worker 1901 from privileged guest control. The master survived with an unchanged start
time, Apache forked a replacement about 7.2 seconds later, and the first client request was
satisfied 12.1 seconds after the signal. This is recorded here only to state that it yields
NO detection content for this or any Delta on this telemetry path, and no selector was added
for it:
- Sysmon Event ID 5 ProcessTerminate is disabled by the host config, which has
<ProcessTerminate onmatch="include"> with zero rules; include with no rules logs nothing.
- Apache's own AH00051 'child pid 1901 exit signal Abort (6)' notice goes only to the
GLOBAL error log, not the vhost ErrorLog, and the host's nxlog reads neither.
- nxlog holds read file descriptors on /var/log/syslog and /var/log/auth.log only, and
drops every non-Sysmon syslog line at the input, so kernel and systemd messages do not
ship either.
- The only shipped witness anywhere in the collector is the sudo authorization record for
the signal itself, in the linuxauth source. That records the cause, not the effect.
Two incidental findings from the same run are worth carrying forward. apache2.service runs
with PrivateTmp=yes, so a /tmp write by a mod_php worker ships an EID 11 FileCreate whose
TargetFilename carries the systemd-private host path
/tmp/systemd-private-<id>-apache2.service-<id>/tmp/... while the matching EID 23 FileDelete
shows the in-namespace path; a /tmp selector aimed at mod_php must expect both shapes. And on
this collector path the flat file is NOT raw Sysmon XML: nxlog runs
parse_windows_eventlog_xml then to_json, so the archive holds JSON with the XML in a Message
field and entities decoded. A grep that pins an element name can therefore match a probe
process whose own command line contained the search text, which inflates naive counts; every
count above was taken with a pattern no probe carried and cross-checked against the guest.
FALSE-POSITIVE CONTROL
----------------------
Web applications sometimes invoke shells or language runtimes for legitimate maintenance,
media conversion, backup, deployment, or integration tasks. A shell or interpreter name is
therefore insufficient by itself. Shell children must also contain execution, interactive,
network-redirection, transfer, decoding, or temporary-staging arguments. Interpreter children
require inline-code, remote-resource, decoding, or temporary-path arguments. Direct curl,
wget, netcat, or socat children remain intentionally high signal because payload retrieval or
tunneling from a web worker is uncommon and is the behavior CISA recommends monitoring.
Environments with known server-side automation should tune exact application, script, account,
and maintenance-window combinations rather than excluding the parent web worker globally.
selection_web_service_account widens that exposure and was measured doing so. A deliberate
negative control ran '/bin/sh -c true' as www-data from a non-web parent - sudo, with no
inbound HTTP request. It carries AccountName www-data, Image /usr/bin/dash and ' -c ', so it
satisfies the new branch and the shell branch together. Any administrative
'sudo -u www-data sh -c ...' and any cron job running as the web service account behaves the
same way. Sites enabling this branch should baseline and filter known maintenance command
lines before deployment. The three original parent branches do not carry this exposure, so an
environment whose parser populates ParentImage correctly may prefer to omit the account branch
- subject to the reliability caveat in the CORRECTION section above, which the 2026-10-23
Apache measurement narrows to the UNIX-socket SAPIs.
RELATIONSHIP TO OTHER DELTAS
----------------------------
EDR-NIX7-RUN is narrower and remains useful for the specific base64-decode-to-shell pipeline.
EDR-NIX20-RUN is restricted to cPanel cpsrvd/cpdavd parents. EDR-NIX29-RUN requires the
SLAPSHOT-specific Python wrapper and .uxdport/.uxdlock artifacts. EDR-NIX31-RUN requires a
chmod operation granting setuid to /bin/sh. EDR-NIX27-RUN covers a Windchill Method Server
JVM parent and is unaffected by the fork-versus-exec behavior described above. This update
generalizes EDR-NIX5-RUN rather than adding a parallel Delta for the same
web-worker-to-command-execution telemetry layer.
EDR-SYM974-RUN is the Windows counterpart to this Delta, added 2026-10-22. It mirrors this
three-tier child structure for process_creation/windows, anchored on php-cgi.exe and w3wp.exe
among other Windows web workers. It deliberately carries NO account branch, because under IIS
FastCGI php-cgi.exe is created by CreateProcess rather than forked and Windows Sysmon
resolves ParentImage for its children correctly and consistently - the asymmetry with this
Delta is a measured difference between the two platforms, not a stylistic one.
VALIDATION STATUS
-----------------
L1. Emulation was performed and is cited in master_meta.ATD, and the one open mapping question
that previously held this Delta at L0 has been confirmed by STR parser review on 2026-10-22.
PROVEN against the captured collector archive:
- A PHP-FPM worker spawning a shell child is captured by Sysmon for Linux and reaches the
collector, stamped with the correct hostname aurora01-web02.
- Image and ChildProcessCommandLine carry the deciding values '/usr/bin/dash' and
'sh -c -- true', so selection_shell and selection_shell_suspicious_arguments (on ' -c ')
are both satisfied by shipped data.
- The negative controls behaved. A normal PHP request that spawns no child, a request with
an invalid token, and the same multipart body sent to a static route produced no shell
child anywhere in the window: exactly one 'sh -c -- true' record exists among 4296
shipped records, at the timestamp of the single positive request.
- ParentImage and ParentCommandLine were '-' on that event. Per the CORRECTION section
above, that reflects the unwarmed-cache state for that worker PID; it is the common case
for a UNIX-socket SAPI but not an absolute property of the telemetry.
- Apache mod_php, added 2026-10-23: ParentImage /usr/sbin/apache2 and ParentCommandLine
'/usr/sbin/apache2 -k start' on both captured children, proven against the archive by
element-pinned greps. selection_webserver_parent_image is live on this SAPI.
ACCOUNT MAPPING - CONFIRMED
---------------------------
selection_web_service_account selects on AccountName. The archive proves the raw Sysmon for
Linux element User equals 'www-data'. The mapped attribute is not visible in that archive,
which holds collector-side raw Sysmon output rather than parsed data, so the mapping is
established by review rather than read off the capture. STR parser review on 2026-10-22
confirmed that the Sysmon for Linux User element is parsed into the published attribute
AccountName, so the shipped User 'www-data' reaches the rule as AccountName 'www-data' and
selection_web_service_account is satisfied by the captured positive event. SourceUserName
is not the mapping and no selector change is owed. This is a confirmed parser mapping
rather than the corpus convention it was described as at version 20261022.1, and it is
what allows promotion to L1.
NOW EXERCISED AT VERSION 20261022.3
-----------------------------------
The interpreter branch and the transfer/tunnel branch were recorded as unexercised at
20261022.2. Both were exercised on range AURORA01 on 2026-10-22 against the stock dynamic
pool on aurora01-web02, through the same WAF, from the same kind of token-gated
non-parameterized endpoint, and the trace is cited under SUPPORTING TRACES:
- selection_interpreter: ChildProcessCommandLine 'python3 -c pass', User www-data. The
interpreter branch requires ' -c ', which this command line carries.
- selection_transfer_or_tunnel_tool: ChildProcessCommandLine 'wget --version', the
ungated transfer-tool branch. wget was used rather than curl because curl is not
installed on that host.
- Both children are DIRECT children of the pool worker. The endpoint used the PHP 7.4+
proc_open array form, which execs without /bin/sh, so unlike the 'sh -c -- true' record
above there is no intervening dash process.
- Negative controls behaved again: a stock page request, an invalid-token request carrying
a valid action keyword, and a valid-token request with no action keyword each produced
no child process.
- ParentImage was '-' on both, consistent with the CORRECTION section: these were
ordinary pool workers serving HTTP, so their PIDs were not in Sysmon's cache. Both
events are matched through selection_web_service_account, which is the branch that
exists for exactly this case.
NOW EXERCISED AT VERSION 20261022.4
-----------------------------------
Apache mod_php under mpm_prefork, the last unexercised parent branch and the SAPI with the
highest demonstrated exploitability for CVE-2026-109618. Measured on 2026-10-23 and detailed
under APACHE MOD_PHP PREFORK above; the traces are cited under SUPPORTING TRACES.
selection_webserver_parent_image fires on shipped data through its '/apache2' value.
STILL NOT EXERCISED, which does not block L1:
- The '/httpd' value of selection_webserver_parent_image. Ubuntu names the binary
apache2, so an RHEL-family host with /usr/sbin/httpd has not been captured. The value
is retained on the same reasoning as before, and the Ubuntu measurement makes the
mechanism rather than the path the uncertain part.
- '/java', '/java.bin', '/nginx', '/tomcat' and 'catalina.sh' as parent images.
The confirmed mapping does not reduce the false-positive exposure recorded under
FALSE-POSITIVE CONTROL. It does the opposite: the account branch is now known to be live, so
the measured 'sudo -u www-data sh -c true' negative control is a real firing path and must be
baselined and filtered before deployment.
SUPPORTING TRACES
-----------------
The interpreter-branch, transfer-tool-branch and controlled parent-resolution evidence
described above:
https://bitbucket.org/securonixsnypr/str-atd/src/main/Execution/php2shell_phpfpm_interpreter_transfer_and_parent_resolution_sysmonlinux_20261022.log
The Apache mod_php prefork evidence added at 20261022.4. The first is the child lineage,
including the non-web-parent specificity control; the second is the Apache worker's own
instrumented events, which is what establishes why the parent cache is warm on this SAPI:
https://bitbucket.org/securonixsnypr/str-atd/src/main/Execution/php2shell_apache_modphp_prefork_child_lineage_sysmonlinux_20261023.log
https://bitbucket.org/securonixsnypr/str-atd/src/main/Execution/php2shell_apache_prefork_worker_self_warming_sysmonlinux_20261023.log
The abnormal-termination result from the same run is deliberately NOT cited as validation
evidence for this Delta, because it produced no detection content. It is recorded separately
so the negative result is not re-investigated:
https://bitbucket.org/securonixsnypr/str-atd/src/main/Execution/php2shell_apache_prefork_worker_sigabrt_privileged_control_linuxauth_20261023.log
The same emulation run also captured a bounded multipart precursor request, the request-shape
half of CVE-2026-109618, against a Windows IIS / PHP 8.3.35 CGI-FastCGI backend through the
same WAF. Those traces are not validation evidence for this Delta's process-creation logic
but they document what the request layer does and does not show:
https://bitbucket.org/securonixsnypr/str-atd/src/main/Initial_Access/php2shell_multipart_precursor_waf_modsec_20261022.log
https://bitbucket.org/securonixsnypr/str-atd/src/main/Initial_Access/php2shell_multipart_precursor_iis_w3c_20261022.log
Two findings there bear on why this Delta is endpoint-based rather than request-based. First,
the backend IIS W3C log records cs-bytes but no Content-Type, so a 51-part multipart request
carrying repeated part headers and empty-filename controls is indistinguishable from a benign
single-file upload of identical size - both logged cs-bytes 4927. Second, CRS v4 at default
paranoia raised no multipart rule on the anomalous request; the only rule that fired was
920350, Host header is a numeric IP address, which is an artifact of addressing the lab by IP.
The discriminating structure survived only inside the full ModSecurity JSON audit record.
Policy building walkthrough can be found in this previous post:
