name: Windows IIS Worker or PHP CGI Process Spawning Suspicious Command Process Analytic
signatureid: EDR-SYM974-RUN
category: 'Execution'
threatname: 'Command & Scripting Interpreter: Windows Command Shell'
functionality: 'Endpoint Management Systems'
description: |
Detects a Windows web worker process - the IIS worker w3wp.exe, a PHP CGI/FastCGI worker
php-cgi.exe or php.exe, or a Windows build of another web daemon - spawning a command shell
with execution-oriented arguments, a general-purpose interpreter with inline-code,
remote-resource or temporary-path arguments, or a network transfer or tunneling utility as a
direct child. 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 is the Windows counterpart to EDR-NIX5-RUN and mirrors its three-tier child structure so
the same false-positive discipline applies on both platforms. It 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, and CISA specifically recommends monitoring PHP workers launching shells or
downloading tools.
Under IIS the PHP FastCGI handler runs PHP inside php-cgi.exe, a separate process created by
w3wp.exe. Code executed through a PHP request therefore has ParentImage php-cgi.exe, NOT
w3wp.exe, which is why an IIS-worker-only parent anchor does not see this behavior. Both
parents are included here.
Sysmon Event ID 1 or equivalent process-creation telemetry is required. Shells and
interpreters are not matched on process name alone: their command lines must show command
execution, encoding, download, or temporary staging. Transfer and tunneling tools are
intentionally high signal as direct children of a web-facing worker.
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://attack.mitre.org/techniques/T1059/003/
- https://attack.mitre.org/techniques/T1105/
- https://learn.microsoft.com/en-us/sysinternals/downloads/sysmon
- https://github.com/bobby-tablez/FT-Sysmon-Config
labels:
- attack.execution
- attack.t1059
- attack.t1059.001
- attack.t1059.003
- attack.command_and_control
- attack.t1105
- attack.t1190
- CVE_2026_109618
- CVE_2026_109619
- PHP
- IIS
- FastCGI
- Post_Exploitation
logsource:
category: process_creation
product: windows
detection:
selection_webworker_parent:
ParentImage|endswith:
- '\php-cgi.exe'
- '\php.exe'
- '\w3wp.exe'
- '\httpd.exe'
- '\nginx.exe'
- '\tomcat.exe'
- '\tomcat9.exe'
- '\tomcat10.exe'
selection_shell:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\pwsh.exe'
- '\powershell_ise.exe'
selection_shell_suspicious_arguments:
ChildProcessCommandLine|contains:
- ' /c '
- ' /k '
- ' -enc'
- ' -EncodedCommand'
- ' -nop'
- ' -NoProfile'
- ' -w hidden'
- ' -WindowStyle hidden'
- ' -Command '
- 'DownloadString'
- 'DownloadFile'
- 'Invoke-WebRequest'
- 'Invoke-Expression'
- 'FromBase64String'
- 'Start-BitsTransfer'
- '\Temp\'
- '\AppData\'
selection_interpreter:
Image|endswith:
- '\python.exe'
- '\python3.exe'
- '\perl.exe'
- '\ruby.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\mshta.exe'
selection_interpreter_suspicious_arguments:
ChildProcessCommandLine|contains:
- ' -c '
- ' -e '
- 'base64'
- 'exec('
- 'eval('
- 'http://'
- 'https://'
- 'urllib'
- 'socket'
- '\Temp\'
- '\AppData\'
selection_transfer_or_tunnel_tool:
Image|endswith:
- '\curl.exe'
- '\wget.exe'
- '\certutil.exe'
- '\bitsadmin.exe'
- '\ftp.exe'
- '\nc.exe'
- '\ncat.exe'
- '\netcat.exe'
- '\socat.exe'
condition: selection_webworker_parent 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 and the Windows counterpart to
EDR-NIX5-RUN, following the corpus NIX/SYM counterpart convention (EDR-NIX27/EDR-SYM961,
EDR-NIX28/EDR-SYM962). 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 need multipart-part and worker-affinity fields that web access logs
do not expose. It detects the durable endpoint consequence instead.
WHY php-cgi.exe IS THE PARENT, AND WHY THAT IS THE GAP
-----------------------------------------------------
Under IIS, PHP is normally hosted through the FastCGI handler, which maps *.php to
php-cgi.exe. w3wp.exe creates php-cgi.exe, and PHP user code then runs INSIDE php-cgi.exe.
A process spawned by PHP code is therefore a child of php-cgi.exe, and its ParentImage is
php-cgi.exe - not w3wp.exe. This was measured directly on range AURORA01 (see VALIDATION
STATUS): the IIS handler mapping was confirmed in-guest as PHP_via_FastCGI *.php ->
C:\PHP\php-cgi.exe, and all five captured child processes carried
ParentImage C:\PHP\php-cgi.exe.
A consequence worth stating plainly for the reviewer: an IIS-worker-only parent anchor
(ParentImage|endswith '\w3wp.exe') does not see php2shell post-exploitation at all, because
the PHP child is one process deeper than w3wp.exe.
WINDOWS vs LINUX PARENT RESOLUTION - THE ASYMMETRY MATTERS
---------------------------------------------------------
EDR-NIX5-RUN carries a fourth, parent-image-independent branch keyed on the web service
account (selection_web_service_account on AccountName www-data and friends). That branch
exists only because Sysmon for Linux frequently ships ParentImage as the literal '-' for a
forked PHP-FPM or nginx worker, and it was measured to widen false-positive exposure to any
'sudo -u www-data sh -c ...' maintenance command.
This Delta deliberately does NOT include an equivalent account branch, and the reason is a
measurement rather than a preference. Under IIS FastCGI, php-cgi.exe is created by
CreateProcess - it is exec'd, not forked - so it is a fully-tracked process and Windows
Sysmon resolves ParentImage for its children correctly and consistently. All five captured
events carried a correct, fully-qualified ParentImage. The parent anchor alone is therefore
sufficient on Windows, and adding an 'IIS APPPOOL\...' account branch would import
EDR-NIX5-RUN's false-positive exposure while buying nothing. Sites should not add one.
FALSE-POSITIVE CONTROL
----------------------
IIS-hosted applications do legitimately shell out for media conversion, backup, deployment,
and integration work, and EDR-SYM858-RUN's own technical_details concedes the same for IIS
generally. A shell or interpreter image name is therefore insufficient by itself. Shell
children must also carry execution, encoding, download, or temporary-staging arguments;
interpreter children must carry inline-code, remote-resource, decoding, or temporary-path
arguments. Direct curl, wget, certutil, bitsadmin, ftp, netcat, or socat children remain
intentionally ungated because payload retrieval or tunneling initiated by a web worker is
uncommon and is the behavior CISA recommends monitoring.
Note that ' /c ' and ' -Command ' are broad on their own - a legitimate application shelling
out will usually use them. They are included because they are the minimum evidence that a
shell was asked to EXECUTE something rather than merely being launched, which is the same
bar EDR-NIX5-RUN sets with ' -c '. Environments with known server-side automation should
baseline and filter the specific application, script, account, and maintenance-window
combinations rather than excluding the web-worker parent globally, which would disable the
detection entirely.
RELATIONSHIP TO EDR-SYM858-RUN - READ THIS BEFORE DEPLOYING BOTH
---------------------------------------------------------------
EDR-SYM858-RUN ('Suspicious WSUS Spawning Command Shell for Reconnaissance Analytic', L0) is
the only other Delta in the corpus anchored on an IIS worker parent. Its condition is
(ParentImage|endswith '\w3wp.exe' or '\wsusservice.exe') and (Image|endswith cmd.exe,
powershell.exe, pwsh.exe, powershell_ise.exe, whoami.exe, net.exe, net1.exe), with no
command-line gating at all.
The two are complementary, and that was verified by measurement rather than assumed:
- EDR-SYM858-RUN does NOT fire on the captured php2shell evidence. Its parent anchor
requires w3wp.exe or wsusservice.exe; the measured ParentImage on all five captured
children is C:\PHP\php-cgi.exe. This is the core gap this Delta closes.
- EDR-SYM858-RUN also has no curl.exe, certutil.exe, bitsadmin.exe, python.exe or other
interpreter/transfer children, so it would not have matched the captured curl.exe child
even with a matching parent.
- There IS one overlapping firing path: w3wp.exe spawning cmd.exe or powershell.exe.
On that path both Deltas can fire. This Delta is the stricter of the two there, because
it additionally requires execution-oriented command-line arguments, so any event that
satisfies this Delta on that path also satisfies EDR-SYM858-RUN. The Content team may
reasonably prefer to treat EDR-SYM858-RUN as the narrow WSUS/CVE-2025-59287 alert it is
named for and this Delta as the general web-worker rule. No change is made to
EDR-SYM858-RUN in this pull request.
Other Deltas reviewed and ruled out as overlaps: EDR-SYM961-RUN requires java/javaw parents
with mandatory Windchill command-line markers; EDR-SYM838-ERI is a tomcat-parent rare-child
event-rarity analytic on Windows; EDR-SYM881-RUN is SolarWinds Web Help Desk wrapper.exe;
EDR-SYM844-RUN, EDR-SYM973-RUN and EDR-SYM962-RUN are Windows file-creation rules with no
process lineage; EDR-NIX5-RUN is logsource.product linux and cannot fire on Windows. A
delta_search for 'php-cgi' returns zero results across the corpus.
VALIDATION STATUS
-----------------
L1, with emulation performed and cited in master_meta.ATD. The parser-mapping question that
held this at L0 through version 20261022.2 is resolved; see PARSER MAPPING below. No
selector changed on promotion.
Emulated on range AURORA01 on 2026-10-22, host WP01 (Windows Server 2022, IIS 10,
PHP 8.3.35 cgi-fcgi, Sysmon64 v15.15, config ft-sysmonconfig-export.xml). A token-gated,
non-parameterized PHP endpoint performed fixed inert actions only; no request input ever
reached a command line, and every command was a hardcoded argv array or fixed string.
Requests were driven from range-internal web01 through an nginx/ModSecurity WAF on port 80.
Children were spawned with proc_open and bypass_shell so that each is a DIRECT child of
php-cgi.exe via CreateProcess, with no intermediate cmd.exe.
PROVEN against the captured collector archive (sysmon_remote.txt). evidence_grep truncates
each line before the parent fields, so every claim below was proven by writing the deciding
field name and value into the search pattern itself:
- ParentImage C:\PHP\php-cgi.exe appears EXACTLY 5 times, and those 5 are precisely the
5 intended child spawns.
- Image C:\Windows\System32\cmd.exe with ParentImage C:\PHP\php-cgi.exe: 3 records
(22:16:26, 22:17:59, 22:19:07 UTC; PIDs 5096, 4428, 3736).
- Image C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with ParentImage
C:\PHP\php-cgi.exe: 1 record (22:16:30 UTC, PID 5816).
- Image C:\Windows\System32\curl.exe with ParentImage C:\PHP\php-cgi.exe: 1 record
(22:16:35 UTC, PID 6016). This satisfies selection_transfer_or_tunnel_tool.
- The child command line is populated and selectable alongside the parent anchor: the
marker string from the cmd.exe payload co-occurs with ParentImage C:\PHP\php-cgi.exe in
3 records. The captured command lines are 'cmd.exe /c echo <marker>' and
'powershell.exe -NoProfile -NonInteractive -Command Write-Output <marker>', which
satisfy ' /c ' and ' -NoProfile'/' -Command ' respectively.
- The account is populated: User IIS APPPOOL\DefaultAppPool co-occurs with
ParentImage C:\PHP\php-cgi.exe in all 5 records.
- Host is DeviceHostName-equivalent AURORA01-WP01 on every record.
- Event is Sysmon EID 1 Process Create via Microsoft-Windows-Sysmon/Operational, shipped
to the collector as expected.
NEGATIVE CONTROLS - both kinds behaved:
- Absence. A normal request to a stock WordPress page, a request with an INVALID token but
a valid action keyword, and a request with a valid token and no action keyword all
produced ZERO child processes. The ParentImage count of exactly 5 is itself the proof:
had any control spawned anything, the count would exceed 5.
- Specificity. The same three child images were then spawned locally on WP01 from a
non-web parent (powershell.exe, PID 2308) with marker strings suffixed -LOCALCTL. Those
records carry ParentImage C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe, so
they do NOT satisfy selection_webworker_parent. Identical children, different parent,
no match - which is the precise property a parent-anchored selector depends on.
REQUEST-PATH CORROBORATION
--------------------------
The IIS W3C log for the same window is committed alongside the Sysmon trace
It records the endpoint, the exact cs-uri-query, sc-status 200 for valid tokens and 403 for
the invalid-token control, confirming the full client -> WAF -> IIS -> php-cgi.exe path. It
is corroboration of the request path only, not validation evidence for this Delta's
process-creation logic.
---------------------------------------
selection_shell_suspicious_arguments and selection_interpreter_suspicious_arguments select on
the published attribute ChildProcessCommandLine, and violation_summary.grouping_attribute is
AccountName. ChildProcessCommandLine and ParentProcessCommandLine are the Endpoint Management
Systems names for the child and parent command line. The Microsoft Windows functionality
names the equivalent field CommandLine. That difference is a per-functionality naming
convention and not a gap. This Delta is functionality 'Endpoint Management Systems', so
ChildProcessCommandLine is the correct attribute for it to select on.
Policy building walkthrough can be found in this previous post:
