Skip to content

Siemphony is in beta and still being built. How a rule earns its badge.

Siemphony’s repertoire

Web server worker writes a server-side script into a web root

AN1108 has two halves — an unexpected file appearing in a web directory, and the server worker later spawning a shell — and this rule takes the first. The second is already published in this corpus as the T1505 parent's rule, which matches w3wp.exe, php-cgi.exe, httpd.exe, nginx.exe and sqlservr.exe spawning an interpreter and whose own description says it "sees the payoff and never the plant". This rule is the plant, so the two are complementary rather than duplicates. It requires all three of: a Sysmon EventID 11 file-create whose creating process is a web server worker, a target path inside a web directory, and a server-side script extension. The creator gate is what makes the rule worth running — a release pipeline, an administrator copying files or a package installer writes into the same directories with the same extensions, and none of them is w3wp.exe or php-cgi.exe, whereas an exploited web application writing its own backdoor is the worker process itself, which is the shape ProxyLogon-class web shell drops take. The path list is MITRE's `WebRootPath` knob, populated here with the default IIS layout plus the htdocs/httpdocs/public_html/webapps conventions third-party stacks use; the extension list and the process list are authored, not sourced from MITRE, whose `ParentProcess` knob describes the process-creation leg rather than this one. Three limits are structural, and the first is the one that decides whether this rule ever fires. Sysmon's own sample configurations filter FileCreate hard — the widely used ones include only a handful of extensions and directories — so on a stock deployment this rule returns zero rows not because nothing was dropped but because the event was never generated; the config must include FileCreate for the web roots and script extensions below before the rule means anything, and zero rows reads as quiet when it actually means blind. Second, the rule only sees shells the web server itself wrote: a shell copied in over SMB, unpacked by an installer, or written by an out-of-process ASP.NET Core application (the writer there is dotnet.exe or the application's own binary, neither of which is matched) is invisible here. Third, a JSP shell dropped through a servlet container is written by java.exe rather than by the tomcat9.exe service wrapper, and java.exe is deliberately left out of the creator list because it would match every build, IDE and CI job on the estate — so the Java arm of this technique is uncovered. What is not attempted: the analytic's Security 4624/4648 log source, because a logon record carries no file path and no process lineage and cannot express a web shell at all; MITRE's `PayloadEntropyThreshold` and `TimeWindow` knobs, which are a content measure and a cross-event correlation that lib/sigma models neither of; and the Linux and macOS analytics — AN1109's process leg (apache2 or nginx spawning a shell) is already published as this corpus's T1059.004 rule, its file leg needs auditd watches on the web root that no distribution loads by default, and AN1110's macos/unifiedlog source has no field vocabulary in the brief, in lib/sigma or in Sigma's taxonomy. Vocabulary note: this rule is written in the Sysmon EventID 11 names `Image` and `TargetFilename` that the brief's `file_event` logsource implies. An estate feeding the same category from Security 4663 instead has `ProcessName` and `ObjectName` and needs a field mapping before any of this matches — configuring *Audit Object Access* and a SACL without that mapping buys nothing here. UNVERIFIED AS AUTHORED — derived from MITRE ATT&CK DET0394, not hand-written and not tested by its author. Any lab result is on this rule's own page.Full description

The detection

The 4 SIEM queries below are previews produced by Siemphony’s own translator, not pySigma, and none has been executed against a real backend — review before you deploy.

detection.yml

title: Web server worker writes a server-side script into a web rootid: d1d48dd0-d5c7-4642-969d-4cae6a9057castatus: experimentaldescription: |  AN1108 has two halves — an unexpected file appearing in a web directory, and  the server worker later spawning a shell — and this rule takes the first. The  second is already published in this corpus as the T1505 parent's rule,  which matches w3wp.exe, php-cgi.exe, httpd.exe, nginx.exe and sqlservr.exe  spawning an interpreter and whose own description says it "sees the payoff and  never the plant". This rule is the plant, so the two are complementary rather  than duplicates. It requires all three of: a Sysmon EventID 11 file-create  whose creating process is a web server worker, a target path inside a web  directory, and a server-side script extension. The creator gate is what makes  the rule worth running — a release pipeline, an administrator copying files or  a package installer writes into the same directories with the same extensions,  and none of them is w3wp.exe or php-cgi.exe, whereas an exploited web  application writing its own backdoor is the worker process itself, which is the  shape ProxyLogon-class web shell drops take. The path list is MITRE's  `WebRootPath` knob, populated here with the default IIS layout plus the  htdocs/httpdocs/public_html/webapps conventions third-party stacks use; the  extension list and the process list are authored, not sourced from MITRE, whose  `ParentProcess` knob describes the process-creation leg rather than this one.  Three limits are structural, and the first is the one that decides whether this  rule ever fires. Sysmon's own sample configurations filter FileCreate hard —  the widely used ones include only a handful of extensions and directories — so  on a stock deployment this rule returns zero rows not because nothing was  dropped but because the event was never generated; the config must include  FileCreate for the web roots and script extensions below before the rule means  anything, and zero rows reads as quiet when it actually means blind. Second,  the rule only sees shells the web server itself wrote: a shell copied in over  SMB, unpacked by an installer, or written by an out-of-process ASP.NET Core  application (the writer there is dotnet.exe or the application's own binary,  neither of which is matched) is invisible here. Third, a JSP shell dropped  through a servlet container is written by java.exe rather than by the  tomcat9.exe service wrapper, and java.exe is deliberately left out of the  creator list because it would match every build, IDE and CI job on the estate —  so the Java arm of this technique is uncovered.  What is not attempted: the analytic's Security 4624/4648 log source, because a  logon record carries no file path and no process lineage and cannot express a  web shell at all; MITRE's `PayloadEntropyThreshold` and `TimeWindow` knobs,  which are a content measure and a cross-event correlation that lib/sigma models  neither of; and the Linux and macOS analytics — AN1109's process leg (apache2  or nginx spawning a shell) is already published as this corpus's T1059.004  rule, its file leg needs auditd watches on the web root that no distribution  loads by default, and AN1110's macos/unifiedlog source has no field vocabulary  in the brief, in lib/sigma or in Sigma's taxonomy. Vocabulary note: this rule  is written in the Sysmon EventID 11 names `Image` and `TargetFilename` that the  brief's `file_event` logsource implies. An estate feeding the same category  from Security 4663 instead has `ProcessName` and `ObjectName` and needs a field  mapping before any of this matches — configuring *Audit Object Access* and a  SACL without that mapping buys nothing here.  UNVERIFIED AS AUTHORED — derived from MITRE ATT&CK DET0394, not hand-written and not tested by its author. Any lab result is on this rule's own page.references:  - https://attack.mitre.org/techniques/T1505/003  - https://attack.mitre.org/detectionstrategies/DET0394author: Siemphony pipeline (machine-derived from MITRE ATT&CK v19.2)date: 2026-08-16tags:  - attack.persistence  - attack.t1505.003logsource:  category: file_event  product: windowsdetection:  selection_server_writer:    Image|endswith:      - '\w3wp.exe'      - '\php-cgi.exe'      - '\php.exe'      - '\httpd.exe'      - '\nginx.exe'  selection_web_directory:    TargetFilename|contains:      - '\inetpub\'      - '\wwwroot\'      - '\htdocs\'      - '\httpdocs\'      - '\public_html\'      - '\webapps\'      - '\www\'  selection_script_extension:    TargetFilename|endswith:      - '.asp'      - '.aspx'      - '.ashx'      - '.asmx'      - '.asax'      - '.cshtml'      - '.vbhtml'      - '.php'      - '.php3'      - '.php4'      - '.php5'      - '.php7'      - '.phtml'      - '.jsp'      - '.jspx'      - '.jspf'      - '.cfm'      - '.cfml'  condition: all of selection_*falsepositives:  - "Content management systems updating themselves. A WordPress, Joomla, Drupal or PrestaShop core or plugin update on a Windows PHP host is php-cgi.exe writing dozens of .php files into public_html or wwwroot in one burst, which is this rule's exact selection with nothing distinguishing it. On a CMS host this is the highest-volume match by a wide margin and is why the level is medium rather than high."  - "Deployment features that run inside the worker process rather than beside it. Web Deploy handled by the IIS management service, an application's own self-update or plugin installer, and CMS theme or template editors that save a page from the browser all write server-side script files as w3wp.exe during ordinary release and administration work."  - "Applications that generate script files as a feature: page builders and template editors that persist user-authored .php or .cshtml, report and form designers that emit an .aspx per saved definition, and caching layers that write compiled template output back into the web directory."  - "File-upload endpoints that store user content under the web root without rewriting the extension, where an uploaded document simply happens to end in one of these suffixes. The write is benign and the file is never requested, but the event is identical to a shell being placed."  - "Restore and migration work performed through the web tier, such as a site-migration or backup plugin unpacking an archive of the site's own pages back into place under the worker process account."level: medium

Sentinel · KQL

Run this as a search.

DeviceFileEvents| where ((InitiatingProcessFolderPath endswith "\\w3wp.exe" or InitiatingProcessFolderPath endswith "\\php-cgi.exe" or InitiatingProcessFolderPath endswith "\\php.exe" or InitiatingProcessFolderPath endswith "\\httpd.exe" or InitiatingProcessFolderPath endswith "\\nginx.exe") and (FolderPath contains "\\inetpub\\" or FolderPath contains "\\wwwroot\\" or FolderPath contains "\\htdocs\\" or FolderPath contains "\\httpdocs\\" or FolderPath contains "\\public_html\\" or FolderPath contains "\\webapps\\" or FolderPath contains "\\www\\") and (FolderPath endswith ".asp" or FolderPath endswith ".aspx" or FolderPath endswith ".ashx" or FolderPath endswith ".asmx" or FolderPath endswith ".asax" or FolderPath endswith ".cshtml" or FolderPath endswith ".vbhtml" or FolderPath endswith ".php" or FolderPath endswith ".php3" or FolderPath endswith ".php4" or FolderPath endswith ".php5" or FolderPath endswith ".php7" or FolderPath endswith ".phtml" or FolderPath endswith ".jsp" or FolderPath endswith ".jspx" or FolderPath endswith ".jspf" or FolderPath endswith ".cfm" or FolderPath endswith ".cfml"))

Splunk · SPL

Run this as a search.

index=* ((Image="*\\w3wp.exe" OR Image="*\\php-cgi.exe" OR Image="*\\php.exe" OR Image="*\\httpd.exe" OR Image="*\\nginx.exe") AND (TargetFilename="*\\inetpub\\*" OR TargetFilename="*\\wwwroot\\*" OR TargetFilename="*\\htdocs\\*" OR TargetFilename="*\\httpdocs\\*" OR TargetFilename="*\\public_html\\*" OR TargetFilename="*\\webapps\\*" OR TargetFilename="*\\www\\*") AND (TargetFilename="*.asp" OR TargetFilename="*.aspx" OR TargetFilename="*.ashx" OR TargetFilename="*.asmx" OR TargetFilename="*.asax" OR TargetFilename="*.cshtml" OR TargetFilename="*.vbhtml" OR TargetFilename="*.php" OR TargetFilename="*.php3" OR TargetFilename="*.php4" OR TargetFilename="*.php5" OR TargetFilename="*.php7" OR TargetFilename="*.phtml" OR TargetFilename="*.jsp" OR TargetFilename="*.jspx" OR TargetFilename="*.jspf" OR TargetFilename="*.cfm" OR TargetFilename="*.cfml"))

Elastic · ES|QL

Run this as a search.

FROM logs-*| WHERE ((TO_LOWER(process.executable) LIKE "*\\\\w3wp.exe" OR TO_LOWER(process.executable) LIKE "*\\\\php-cgi.exe" OR TO_LOWER(process.executable) LIKE "*\\\\php.exe" OR TO_LOWER(process.executable) LIKE "*\\\\httpd.exe" OR TO_LOWER(process.executable) LIKE "*\\\\nginx.exe") AND (TO_LOWER(file.path) LIKE "*\\\\inetpub\\\\*" OR TO_LOWER(file.path) LIKE "*\\\\wwwroot\\\\*" OR TO_LOWER(file.path) LIKE "*\\\\htdocs\\\\*" OR TO_LOWER(file.path) LIKE "*\\\\httpdocs\\\\*" OR TO_LOWER(file.path) LIKE "*\\\\public_html\\\\*" OR TO_LOWER(file.path) LIKE "*\\\\webapps\\\\*" OR TO_LOWER(file.path) LIKE "*\\\\www\\\\*") AND (TO_LOWER(file.path) LIKE "*.asp" OR TO_LOWER(file.path) LIKE "*.aspx" OR TO_LOWER(file.path) LIKE "*.ashx" OR TO_LOWER(file.path) LIKE "*.asmx" OR TO_LOWER(file.path) LIKE "*.asax" OR TO_LOWER(file.path) LIKE "*.cshtml" OR TO_LOWER(file.path) LIKE "*.vbhtml" OR TO_LOWER(file.path) LIKE "*.php" OR TO_LOWER(file.path) LIKE "*.php3" OR TO_LOWER(file.path) LIKE "*.php4" OR TO_LOWER(file.path) LIKE "*.php5" OR TO_LOWER(file.path) LIKE "*.php7" OR TO_LOWER(file.path) LIKE "*.phtml" OR TO_LOWER(file.path) LIKE "*.jsp" OR TO_LOWER(file.path) LIKE "*.jspx" OR TO_LOWER(file.path) LIKE "*.jspf" OR TO_LOWER(file.path) LIKE "*.cfm" OR TO_LOWER(file.path) LIKE "*.cfml"))

Wazuh · XML rule

Deploy to your manager — this is a rule, not a search.

<group name="sigma,windows,file_event,">  <!-- Rule ids must be unique on your manager; 100000+ is the user range. -->  <rule id="100000" level="7">    <!-- Set <if_sid> to the decoder/base rule for windows so this only evaluates relevant events. -->    <field name="Image" type="pcre2">(?i)(\\w3wp\.exe$|\\php-cgi\.exe$|\\php\.exe$|\\httpd\.exe$|\\nginx\.exe$)</field>    <field name="TargetFilename" type="pcre2">(?i)(\\inetpub\\|\\wwwroot\\|\\htdocs\\|\\httpdocs\\|\\public_html\\|\\webapps\\|\\www\\)</field>    <field name="TargetFilename" type="pcre2">(?i)(\.asp$|\.aspx$|\.ashx$|\.asmx$|\.asax$|\.cshtml$|\.vbhtml$|\.php$|\.php3$|\.php4$|\.php5$|\.php7$|\.phtml$|\.jsp$|\.jspx$|\.jspf$|\.cfm$|\.cfml$)</field>    <description>Web server worker writes a server-side script into a web root</description>    <mitre>      <id>T1505.003</id>    </mitre>  </rule></group>

Verdicts · reactions · comments

Community

Verdicts from engineers who actually deployed it, and the conversation around it.

Log in to react
Not yet reported on

Nobody has run this in a real environment and said what happened.

Verdicts from engineers who deployed it

0 cast

No verdicts reported yet. The first one is worth more than the tenth — say what you ran it against.

Log in to report a verdict.

Log in to join the discussion.

No comments yet. Someone who deploys this will have something to say about it.