<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Elastic Security Labs - Articles by Bryan Porras Blanch</title>
        <link>https://www.elastic.co/security-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Wed, 22 Jul 2026 20:42:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Security Labs - Articles by Bryan Porras Blanch</title>
            <url>https://www.elastic.co/security-labs/assets/security-labs-thumbnail.png</url>
            <link>https://www.elastic.co/security-labs</link>
        </image>
        <copyright>© 2026. elasticsearch B.V. All Rights Reserved</copyright>
        <item>
            <title><![CDATA[wp2shell hits WordPress: detecting pre-auth RCE from plugin drop to command execution]]></title>
            <link>https://www.elastic.co/security-labs/wp2shell-wordpress-rce-detection-elastic-defend</link>
            <guid>wp2shell-wordpress-rce-detection-elastic-defend</guid>
            <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
            <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://www.elastic.co/security-labs/assets/images/wp2shell-wordpress-rce-detection-elastic-defend/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>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>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>
<table>
<thead>
<tr>
<th align="left">WordPress branch</th>
<th align="left">Exposure</th>
<th align="left">Fixed in</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>&lt;= 6.8.5</code></td>
<td align="left">Not affected by the full RCE chain</td>
<td align="left">n/a</td>
</tr>
<tr>
<td align="left"><code>6.8.0</code> to <code>6.8.5</code></td>
<td align="left">SQL injection only (no full RCE chain on this branch)</td>
<td align="left">Patch to a non-vulnerable release (6.8.6)</td>
</tr>
<tr>
<td align="left"><code>6.8.6</code></td>
<td align="left">Patched / Not vulnerable (SQL injection fixed)</td>
<td align="left">Included in 6.8.6</td>
</tr>
<tr>
<td align="left"><code>6.9.0</code> to <code>6.9.4</code></td>
<td align="left">Full pre-auth RCE chain</td>
<td align="left">6.9.5</td>
</tr>
<tr>
<td align="left"><code>7.0.0</code> to <code>7.0.1</code></td>
<td align="left">Full pre-auth RCE chain</td>
<td align="left">7.0.2</td>
</tr>
</tbody>
</table>
<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>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>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>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://www.elastic.co/security-labs/assets/images/wp2shell-wordpress-rce-detection-elastic-defend/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>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://www.elastic.co/security-labs/assets/images/wp2shell-wordpress-rce-detection-elastic-defend/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>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://www.elastic.co/security-labs/assets/images/wp2shell-wordpress-rce-detection-elastic-defend/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>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://www.elastic.co/security-labs/assets/images/wp2shell-wordpress-rce-detection-elastic-defend/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 &quot;$d&quot; in */wp-content/plugins/*) cd / &amp;&amp; rm -rf &quot;$d&quot;;; 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>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://www.elastic.co/security-labs/assets/images/wp2shell-wordpress-rce-detection-elastic-defend/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>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 class="language-sql">file where event.type in (&quot;creation&quot;, &quot;change&quot;) and (
  process.name in (
    &quot;nginx&quot;, &quot;apache2&quot;, &quot;httpd&quot;, &quot;php-cgi&quot;, &quot;php-fcgi&quot;,
    &quot;php-cgi.cagefs&quot;,  &quot;sw-engine-fpm&quot;,
    &quot;wget&quot;, &quot;curl&quot;, &quot;bash&quot;, &quot;dash&quot;, &quot;sh&quot;, &quot;tcsh&quot;, &quot;csh&quot;,
    &quot;zsh&quot;, &quot;ksh&quot;, &quot;fish&quot;, &quot;mksh&quot;, &quot;busybox&quot;
  ) or
  process.name like (&quot;php-fpm*&quot;, &quot;lsphp*&quot;, &quot;*.cgi&quot;, &quot;*.fcgi&quot;)
) and
file.path like~ &quot;*/wp-content/plugins/*&quot;
</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>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 class="language-sql">process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and (
  process.parent.name in (
    &quot;nginx&quot;, &quot;apache2&quot;, &quot;httpd&quot;, &quot;caddy&quot;, &quot;mongrel_rails&quot;, &quot;uwsgi&quot;, &quot;daphne&quot;,
    &quot;httpd.worker&quot;, &quot;flask&quot;, &quot;php-cgi&quot;, &quot;php-fcgi&quot;, &quot;php-cgi.cagefs&quot;, 
    &quot;lswsctrl&quot;, &quot;varnishd&quot;, &quot;uvicorn&quot;, &quot;waitress-serve&quot;, &quot;starman&quot;,
    &quot;frankenphp&quot;, &quot;zabbix_server&quot;, &quot;asterisk&quot;, &quot;sw-engine-fpm&quot;
  ) or
  process.parent.name like (&quot;php-fpm*&quot;, &quot;gunicorn*&quot;, &quot;*.cgi&quot;, &quot;*.fcgi&quot;) or
  [...]
  [Additional web server technologies]
  [...]
) and
process.name in (
  &quot;bash&quot;, &quot;dash&quot;, &quot;sh&quot;, &quot;tcsh&quot;, &quot;csh&quot;, &quot;zsh&quot;, &quot;ksh&quot;, &quot;fish&quot;, &quot;busybox&quot;
) and
process.args in (&quot;-c&quot;, &quot;-cl&quot;, &quot;-lc&quot;) 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 &quot;web RCE just succeeded.&quot;</p>
<h3>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>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>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>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>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>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>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>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>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>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>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/security-labs/assets/images/wp2shell-wordpress-rce-detection-elastic-defend/wp2shell-wordpress-rce-detection-elastic-defend.webp" length="0" type="image/webp"/>
        </item>
    </channel>
</rss>