← All hunts high TLP:CLEAR Part 1 of 2

WordPress REST API Exploitation and Plugin Staging

An attacker is exploiting the wp2shell WordPress Core RCE chain to upload and stage a malicious plugin by abusing the unauthenticated REST batch API.

Based on research by Elastic Security Labs 2026-09-20 9 steps · 3 queries T1059 T1190 T1505.003

Brief

Why This Hunt Matters

Elastic Security Labs recently published an analysis of wp2shell hits WordPress, detailing a pre-authentication RCE chain (CVE-2026-63030 and CVE-2026-60137). The exploit abuses the WordPress REST batch API to bypass authentication and eventually stage a malicious plugin. Because these plugins can contain any arbitrary PHP code, they is a versatile persistence mechanism. We designed this hunt to find the transition from API exploitation to file-system persistence.

How the Hunt Flows

The hunt begins by scoping the environment for vulnerable assets. The first query checks vulnerability findings for CVEs associated with wp2shell. This allows an analyst to prioritize hosts already known to be running vulnerable versions of WordPress, though the hunt continues even if specific CVE records are missing.

Next, the hunt moves into a parallel correlation phase. One branch monitors HTTP activity for requests targeting the /batch/v1 endpoint. It specifically looks for anomalies like the wp2shell User-Agent or common toolstrings like python and curl hitting these routes. This surface captures the initial exploitation attempt at the network level.

Simultaneously, the second branch monitors file activity on the host. It stacks the creation of PHP files within the WordPress plugin and upload directories. The query filters for files dropped by web server processes such as apache2, httpd, or php-fpm. By counting the prevalence of these files across the estate, the hunt highlights rare drops that do not align with standard administrative updates.

Finally, a triage step correlates these events. An analyst or agent examines if a host received a suspicious batch API request shortly followed by the creation of a rare PHP file. This correlation bridges the gap between a network-level exploit and its host-level impact, providing high confidence for a malicious verdict.

Blind Spots

This hunt has two primary limitations. First, it relies on HTTP telemetry. If the traffic is encrypted and the logging source does not have access to decrypted request details, the specific batch API parameters and User-Agents remain invisible. Second, the hunt focuses on the plugin-staging mechanism. If an adversary uses an alternate path, such as writing a shell directly to a cache directory via INTO OUTFILE, the file-activity query does not see the event.

In this series

Steps

  1. Scope for vulnerable WordPress instances

    Query · scoping

    Identify hosts with reported vulnerabilities corresponding to the wp2shell exploit chain to prioritize the hunt.

    reads hb_vulnerability_findingsql
    SELECT device_uid, cve_uid, severity, affected_package_version, first_seen FROM hb_vulnerability_finding WHERE instr(',' || '{{vulnerable_cves}}' || ',', ',' || cve_uid || ',') > 0

    What a hit looks like. A list of device UIDs running vulnerable WordPress versions. Silence means no known-vulnerable instances are recorded in the inventory.

  2. Detect batch API route confusion attempts

    Query · detection candidate

    Find HTTP requests targeting the vulnerable WordPress batch endpoint, specifically looking for common PoC User-Agents or anomalous traffic patterns.

    reads hb_http_activitysql
    SELECT device_hostname, url_full, user_agent, src_endpoint_ip, time FROM hb_http_activity WHERE (LOWER(url_full) LIKE '%/batch/v1%' OR LOWER(url_full) LIKE '%rest_route=/batch/v1%') AND (LOWER(user_agent) LIKE '%wp2shell%' OR LOWER(user_agent) LIKE '%curl%' OR LOWER(user_agent) LIKE '%python%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')

    What a hit looks like. Requests to the batch API from unusual User-Agents. Any hit on 'wp2shell' is a high-confidence indicator of exploitation.

  3. Identify rare PHP files written by web servers

    Query · baseline

    Stack-count the creation of new PHP files in WordPress plugin directories by the web server process to highlight anomalous staging activity.

    reads hb_file_activitysql
    SELECT LOWER(file_path) AS plugin_path, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_file_activity WHERE (LOWER(file_path) LIKE '%/wp-content/plugins/%.php' OR LOWER(file_path) LIKE '%/wp-content/uploads/%.zip') AND (LOWER(process_name) LIKE '%apache%' OR LOWER(process_name) LIKE '%httpd%' OR LOWER(process_name) LIKE '%php-fpm%') AND activity_id = 1 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY plugin_path HAVING host_count <= 2 ORDER BY host_count ASC

    What a hit looks like. A small number of hosts showing new PHP or ZIP files dropped into WordPress content directories by the web server runtime.

  4. Triage WordPress exploitation and staging

    Agent triage

    Evaluate whether the HTTP requests and file staging events together constitute a confirmed exploitation of CVE-2026-63030.

  5. Route on verdict

    Decision

    Direct the workflow to immediate containment if the agent confirms exploitation.

  6. Isolate compromised WordPress host

    Response action

    Prevent further exploitation, lateral movement, or data exfiltration by isolating the affected host.

  7. Manual forensic review

    Analyst task

    Verify the findings and investigate post-exploitation activity such as shell execution or lateral movement.

  8. Incident close-out

    Analyst task

    Record the hunt results and update the vulnerability inventory.

Coverage

Scenario coverage

StageCoveredHow, or why not
Exploitation of WordPress REST batch API
T1190
Yes scoping-vulnerable-assets, http-batch-api-exploitation
Malicious plugin staging on disk
T1505.003
Yes rare-plugin-file-drops
Shell execution by web server process
T1059
Out of scope Covered by follow-on behavioral hunts in the series.
System and privilege reconnaissance
T1082 · T1033
Out of scope Covered by general Linux discovery hunts.
Indicator removal and cleanup
T1070.004
Out of scope Cleanup activity is the last stage and requires separate file-deletion logic.

Blind spots

  • Needs TLS decryption at the log source (hb_http_activity). Encrypted traffic may prevent seeing the User-Agent or full URL query parameters used in the exploit chain. It would answer Were the specific batch API parameters (SQL injection payloads) visible in the request?.
  • Needs Full file monitoring outside wp-content/plugins/. If the attacker avoids the plugin-staging mechanism and writes directly to other writable directories, the rare-plugin query will not see the file drop. It would answer Did the attacker drop a webshell using INTO OUTFILE into the cache or uploads directory?.

Parameters & data

Parameters

ParameterTypeDefaultWhat it is
lookback_daysnumber14Days of history to examine.
scope_hostslist[host]Optional list of hostnames to restrict the hunt; leave empty to scan the entire estate.
vulnerable_cveslist[string]CVE-2026-60137, CVE-2026-63030CVE IDs associated with the wp2shell vulnerability.

Telemetry

SourceCategoryTelemetry
Endpoint telemetry (hb_ surfaces)endpointendpoint
Web server / proxy logssiemnetwork

Source

Download hunt.md Definition (JSON) An open hunt.md file; it runs anywhere that reads the format.
---
analysis: While a single rule may alert on the plugin drop, this hunt correlates the
  initial API exploit (network surface) with the resulting staging activity (file
  surface) and uses prevalence to identify the specific malicious plugin among legitimate
  site updates. This reduces false positives from authorized administrative plugin
  installs.
blind_spots:
- id: missing-http-telemetry
  question: Were the specific batch API parameters (SQL injection payloads) visible
    in the request?
  requires: TLS decryption at the log source (hb_http_activity)
  risk: Encrypted traffic may prevent seeing the User-Agent or full URL query parameters
    used in the exploit chain.
  stage: exploit-wordpress-batch-api
- id: alternate-staging-paths
  question: Did the attacker drop a webshell using INTO OUTFILE into the cache or
    uploads directory?
  requires: Full file monitoring outside wp-content/plugins/
  risk: If the attacker avoids the plugin-staging mechanism and writes directly to
    other writable directories, the rare-plugin query will not see the file drop.
  stage: malicious-plugin-staging
coverage:
- stage: exploit-wordpress-batch-api
  status: covered
  steps:
  - scoping-vulnerable-assets
  - http-batch-api-exploitation
- stage: malicious-plugin-staging
  status: covered
  steps:
  - rare-plugin-file-drops
- reason: Covered by follow-on behavioral hunts in the series.
  stage: shell-spawn-from-web-server
  status: out_of_scope
- reason: Covered by general Linux discovery hunts.
  stage: post-exploitation-discovery
  status: out_of_scope
- reason: Cleanup activity is the last stage and requires separate file-deletion logic.
  stage: automated-self-cleanup
  status: out_of_scope
guardrails:
  claims: no_unsupported
  evidence: citation_required
  missing_data: not_benign
  telemetry: untrusted
hunt:
  applicability: campaign-specific
  handoff: promote-to-detection
  justification: WordPress RCE (wp2shell) provides a pre-authentication path to full
    host compromise. Given the high prevalence of WordPress and the availability of
    public PoCs, an unmitigated compromise on a production web server poses a critical
    risk of data theft and lateral movement.
  methodology: model-assisted
  trigger: intel-report
hypothesis: An attacker is exploiting the wp2shell WordPress Core RCE chain to upload
  and stage a malicious plugin by abusing the unauthenticated REST batch API.
labels:
- hunt
- attack.t1190
- attack.t1505.003
- attack.t1059
name: WordPress REST API Exploitation and Plugin Staging
parameters:
  lookback_days:
    default: '14'
    description: Days of history to examine.
    type: number
  scope_hosts:
    default: []
    description: Optional list of hostnames to restrict the hunt; leave empty to scan
      the entire estate.
    type: list[host]
  vulnerable_cves:
    default:
    - CVE-2026-60137
    - CVE-2026-63030
    description: CVE IDs associated with the wp2shell vulnerability.
    from:
      kind: article
      observed: '2026-07-23'
      ref: elastic-security-labs-wp2shell
    type: list[string]
provenance:
  authors:
  - name: Huntbase hunt generation
    org: huntbase.io
  generated:
    by: huntbase-hunt-generation
    from: https://www.elastic.co/security-labs/blog/wp2shell-wordpress-rce-detection-elastic-defend
    gates:
    - dry-run
    - lint
    - critic
    model: hb_google/gemini-3-flash-preview
rationale: Prioritize internet-facing hosts running WordPress 6.9.x or 7.0.x. If vulnerability
  findings are incomplete, run the HTTP exploitation query across all systems identified
  as web servers.
references:
- name: "Elastic Security Labs \u2014 wp2shell hits WordPress"
  url: https://www.elastic.co/security-labs/blog/wp2shell-wordpress-rce-detection-elastic-defend
related:
- hunt: wordpress-webshell-execution
  reason: This hunt focuses on the staging stage; a follow-on hunt is required to
    detect the execution of commands via the webshell once it is staged.
  relation: follows
scenario:
  stages:
  - name: Exploitation of WordPress REST batch API
    observables:
    - POST /?rest_route=/batch/v1
    - POST /wp-json/batch/v1
    - 'User-Agent: wp2shell'
    - CVE-2026-63030
    - CVE-2026-60137
    slug: exploit-wordpress-batch-api
    tactic: initial-access
    techniques:
    - T1190
  - name: Malicious plugin staging on disk
    observables:
    - wp-content/plugins/wp2shell_*
    - wp-content/uploads/wp2shell_*.zip
    - temp-write-test-*
    - wp-content/upgrade/wp2shell_*/
    - wp-content/plugins/wp2shell_*.php
    slug: malicious-plugin-staging
    tactic: persistence
    techniques:
    - T1505.003
  - name: Shell execution by web server process
    observables:
    - apache2 spawning dash
    - httpd spawning sh
    - php-fpm spawning bash
    - sh -c -- id; whoami; hostname
    slug: shell-spawn-from-web-server
    tactic: execution
    techniques:
    - T1059
  - name: System and privilege reconnaissance
    observables:
    - uname
    - cat /etc/passwd
    - find / -perm -u=s -type f
    slug: post-exploitation-discovery
    tactic: discovery
    techniques:
    - T1082
    - T1033
  - name: Indicator removal and cleanup
    observables:
    - rm -rf wp-content/plugins/wp2shell_*
    - deletion of staged zip files under wp-content/uploads/
    slug: automated-self-cleanup
    tactic: defense-evasion
    techniques:
    - T1070.004
  summary: Attackers leverage a pre-authentication RCE vulnerability in the WordPress
    REST batch API (CVE-2026-63030) to stage malicious plugins or web shells on vulnerable
    servers. Once established, the web server process is used to execute system shells
    for reconnaissance and automated artifact cleanup.
series:
  index: 1
  slug: wp2shell-hits-wordpress-detecting-pre-auth-rce-from-plugin-drop-to-command-execution
  title: 'wp2shell hits WordPress: detecting pre-auth RCE from plugin drop to command
    execution'
  total: 2
severity: high
targets:
  analyst:
    name: Tier-2 analyst
    role: analyst
  endpoint:
    category: endpoint
    name: Endpoint telemetry (hb_ surfaces)
    telemetry:
    - endpoint
  hunter:
    agent: true
    name: Hunt agent
  web:
    category: siem
    name: Web server / proxy logs
    telemetry:
    - network
tlp: clear
type: investigation
---


# WordPress REST API Exploitation and Plugin Staging

This hunt identifies the early stages of the wp2shell attack chain (CVE-2026-63030 and CVE-2026-60137). It first scopes the environment for vulnerable WordPress versions, then correlates evidence of the specific batch API route confusion exploit with file-system staging events where the web server process drops new PHP files into plugin directories. By focusing on the web-server-to-file-drop relationship, the hunt remains durable against variations in the final shell payload.

## scoping-vulnerable-assets
<!-- Scope for vulnerable WordPress instances -->
Identify hosts with reported vulnerabilities corresponding to the wp2shell exploit chain to prioritize the hunt.

```sqlite target=endpoint role=scoping params=(vulnerable_cves=vulnerable_cves)
~~~yaml
expected: A list of device UIDs running vulnerable WordPress versions. Silence means
  no known-vulnerable instances are recorded in the inventory.
reads:
- affected_package_version
- cve_uid
- device_uid
- first_seen
- severity
silence: not_evidence_of_absence
source: hb_vulnerability_finding
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_uid, cve_uid, severity, affected_package_version, first_seen FROM hb_vulnerability_finding WHERE instr(',' || '{{vulnerable_cves}}' || ',', ',' || cve_uid || ',') > 0
```

## correlate-exploitation-activity
<!-- Correlate exploitation and staging -->
parallel:
- → http-batch-api-exploitation
- → rare-plugin-file-drops
join: → triage-exploitation-evidence

## http-batch-api-exploitation
<!-- Detect batch API route confusion attempts -->
Find HTTP requests targeting the vulnerable WordPress batch endpoint, specifically looking for common PoC User-Agents or anomalous traffic patterns.

```sqlite target=web role=detection-candidate params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Requests to the batch API from unusual User-Agents. Any hit on 'wp2shell'
  is a high-confidence indicator of exploitation.
reads:
- device_hostname
- src_endpoint_ip
- time
- url_full
- user_agent
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, url_full, user_agent, src_endpoint_ip, time FROM hb_http_activity WHERE (LOWER(url_full) LIKE '%/batch/v1%' OR LOWER(url_full) LIKE '%rest_route=/batch/v1%') AND (LOWER(user_agent) LIKE '%wp2shell%' OR LOWER(user_agent) LIKE '%curl%' OR LOWER(user_agent) LIKE '%python%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```

## rare-plugin-file-drops
<!-- Identify rare PHP files written by web servers -->
Stack-count the creation of new PHP files in WordPress plugin directories by the web server process to highlight anomalous staging activity.

```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
  compare: first_seen
  window: '{{lookback_days}}d'
expected: A small number of hosts showing new PHP or ZIP files dropped into WordPress
  content directories by the web server runtime.
prevalence:
  by: device_hostname
  key:
  - file_path
  rare_below: 3
reads:
- activity_id
- device_hostname
- file_path
- process_name
- time
silence: not_evidence_of_absence
source: hb_file_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT LOWER(file_path) AS plugin_path, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_file_activity WHERE (LOWER(file_path) LIKE '%/wp-content/plugins/%.php' OR LOWER(file_path) LIKE '%/wp-content/uploads/%.zip') AND (LOWER(process_name) LIKE '%apache%' OR LOWER(process_name) LIKE '%httpd%' OR LOWER(process_name) LIKE '%php-fpm%') AND activity_id = 1 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY plugin_path HAVING host_count <= 2 ORDER BY host_count ASC
```

## triage-exploitation-evidence
<!-- Triage WordPress exploitation and staging -->
```agent target=hunter
cite: required
context:
- http-batch-api-exploitation
- rare-plugin-file-drops
max_iterations: 4
objective: Determine if any host shows a sequence where a REST batch request is followed
  by the creation of a rare PHP file in a WordPress plugin or upload path by the web
  server process.
success_criteria: A verdict of malicious, suspicious, or benign per host with citations
  for the specific HTTP logs and file events.
tools:
- endpoint
- web
```

## determine-response
<!-- Route on verdict -->
if~: "The triage-exploitation-evidence verdict is malicious for at least one host, indicating a confirmed plugin drop via the batch API." (confidence: high, judge=hunter)
then: → isolate-compromised-host
indeterminate: → manual-forensic-review
unavailable: → manual-forensic-review (blind_spot: missing-http-telemetry)
else: → incident-close-out

## isolate-compromised-host
<!-- Isolate compromised WordPress host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the host identified in the triage verdict. Preserve the WordPress directory and web server logs for forensic analysis before remediation.
```
→ manual-forensic-review

## manual-forensic-review
<!-- Manual forensic review -->
```manual target=analyst
Examine the plugin directory identified by the hunt. Check for shell spawns from the web server process (e.g., apache2 spawning sh or dash) and verify if any commands were executed by the staged PHP script. Check for evidence of SQL injection payloads in the web server access logs.
```
→ incident-close-out

## incident-close-out
<!-- Incident close-out -->
```manual target=analyst
Document the hosts examined and any identified compromises. If no activity was found on vulnerable hosts, mark them for urgent patching. Update vulnerability management records to reflect the patch status of WordPress instances.
```
→ end

Run it

Take this hunt into your environment.

Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.

Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, critic, then reviewed by a person.