Share
## https://sploitus.com/exploit?id=674E72B3-2120-5D8E-A875-D3106809159E
# WP2Shell Detector and Validation PoC

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/downloads/)
[![CVE-2026-63030](https://img.shields.io/badge/CVE-2026--63030-critical.svg)](https://nvd.nist.gov/vuln/detail/CVE-2026-63030)
[![CVE-2026-60137](https://img.shields.io/badge/CVE-2026--60137-critical.svg)](https://nvd.nist.gov/vuln/detail/CVE-2026-60137)
[![Security Research](https://img.shields.io/badge/purpose-authorized%20security%20testing-informational.svg)](#legal-and-ethical-use)

> **CVE coverage:** CVE-2026-63030 and CVE-2026-60137
> **Intended use:** Authorized security testing, defensive validation, and disposable localhost labs only

WP2Shell is a WordPress Core vulnerability chain combining a pre-authentication
REST API batch-route confusion bug (**CVE-2026-63030**) with a `WP_Query`
`author__not_in` SQL injection primitive (**CVE-2026-60137**). This repository
provides a Python proof-of-concept scanner and safe validator so defenders can
fingerprint affected WordPress installations, confirm the vulnerable behavior
in an isolated lab, and verify remediation โ€” without needing to extract data
or achieve code execution.

## Overview

This project fingerprints WordPress installations and validates the two vulnerability primitives associated with the **WP2Shell** WordPress Core vulnerability chain:

- **CVE-2026-63030** โ€” REST API batch-route confusion.
- **CVE-2026-60137** โ€” incomplete sanitization of `WP_Query::author__not_in`, which can result in SQL injection when attacker-controlled input reaches the parameter.

When both conditions are present, an unauthenticated request may reach the vulnerable SQL construction through the WordPress REST API batch endpoint. Public advisories describe the combined impact as potentially leading to remote code execution.

This repository should be used only on systems you own or are explicitly authorized to test. Prefer an isolated Docker or virtual-machine lab bound to `127.0.0.1`.

---

## Safety Notice

The current source file contains state-changing functionality, including database-data extraction attempts, file-write attempts, authentication workflows, administrator creation, plugin upload, and command execution.

- WordPress fingerprinting.
- Version assessment.
- Local source-tree version checks.
- Safe route-confusion validation.
- Non-destructive timing-based SQL injection confirmation in an isolated lab.
- Reporting, evidence collection, and remediation.

---

## Vulnerability Summary

### CVE-2026-63030 โ€” REST API Batch-Route Confusion

WordPress affected releases can lose alignment between internal arrays used to track:

- Parsed batch requests.
- Matched REST route handlers.
- Validation results.

When a malformed batch member is accepted into one internal array but not another, later requests can become associated with the wrong handler. A request may therefore be validated as one route but executed using another route's callback.

**Security impact:**

- REST request/handler desynchronization.
- Validation against the wrong route schema.
- Unexpected nested batch execution.
- Pre-authentication reachability of otherwise constrained code paths.
- When combined with CVE-2026-60137, potential SQL injection and further compromise.


### CVE-2026-60137 โ€” `author__not_in` SQL Injection

The affected `WP_Query` implementation does not consistently normalize `author__not_in` before using it to construct an SQL `NOT IN (...)` condition.

The parameter normally expects a list of integer author IDs. If a scalar string reaches the vulnerable query construction without the intended REST-schema validation, unsafe SQL structure can survive into the database query.

**Security impact:**

- Blind SQL injection.
- Boolean or timing-based database oracle.
- Potential disclosure of WordPress database contents.
- Increased impact when chained with CVE-2026-63030.

---

## Affected Versions

| WordPress branch | Affected | Fixed release |
|---|---:|---:|
| 6.8.x | CVE-2026-60137 only: 6.8.0โ€“6.8.5 | 6.8.6 |
| 6.9.x | Both issues: 6.9.0โ€“6.9.4 | 6.9.5 |
| 7.0.x | Both issues: 7.0.0โ€“7.0.1 | 7.0.2 |
| 7.1 prerelease | Beta 1 affected | Beta 2 |
| Earlier than 6.8 | Not affected by these two CVEs | N/A |

WordPress released fixes on July 17, 2026 and enabled forced automatic updates for affected installations because of the severity.

---

## Vulnerability Workflow

```text
Unauthenticated client
        |
        v
WordPress REST batch endpoint
        |
        v
Malformed batch member creates request/handler misalignment
        |
        v
Later request is validated against one route
but dispatched using another route's handler
        |
        v
Scalar author_exclude reaches WP_Query as author__not_in
        |
        v
Unsafe value reaches SQL NOT IN (...) construction
        |
        v
Blind SQL timing or Boolean oracle
        |
        v
Potential database compromise
        |
        v
Potential application-level compromise and RCE
```

The detector should stop after confirming the route-confusion and SQL-injection primitives. It does not need to extract data or execute commands to establish that an affected installation is vulnerable.

---

## Detection Workflow

### Phase 1 โ€” Normalize the target

The tool:

1. Adds a default `http` or `https` scheme if missing.
2. Normalizes the WordPress installation path.
3. Rejects unsupported URL schemes and embedded credentials.
4. Applies redirect, proxy, TLS, and timeout policies.

### Phase 2 โ€” Identify WordPress

The scanner checks for:

- `wp-content/` references.
- `wp-includes/` references.
- WordPress generator metadata.
- REST API discovery links.
- WordPress REST index structure.
- The `wp/v2` namespace.
- Optional feed and `readme.html` fingerprints.

### Phase 3 โ€” Determine the version

Version evidence can come from:

- HTML generator metadata.
- Feed generator metadata.
- WordPress core asset query strings.
- HTTP generator headers.
- `readme.html`.
- A local `wp-includes/version.php` file.

Evidence is scored and reconciled. Conflicting remote version indicators lower confidence.

### Phase 4 โ€” Check batch-route exposure

The scanner attempts to discover `/batch/v1` through:

```text
/?rest_route=/
/wp-json/
```

The route can be addressed using either:

```text
/?rest_route=/batch/v1
/wp-json/batch/v1
```

### Phase 5 โ€” Safe route-confusion probe

The safe probe contains:

1. A deliberately malformed internal path.
2. A request to an invalid post ID with a harmless nested public `GET`.
3. A following `/batch/v1` request.

A vulnerable server returns an outer `207 Multi-Status` response in which the invalid post request is processed as a nested batch request.

The detector reports:

```text
route-confusion-observed
```

when it sees:

- The expected `parse_path_failed` marker.
- A second outer response with status `207`.
- A nested `responses` array showing that the harmless internal request ran.

### Phase 6 โ€” Timing-based SQLi confirmation

A non-destructive SQLi validator should send paired requests that differ only by a constant Boolean condition:

```text
False control -> no deliberate database delay
True test     -> deliberate database delay
```

The validator must:

- Accept the expected outer HTTP `207`.
- Parse both the outer and nested batch envelopes.
- Verify the malformed-request markers.
- Ensure the SQLi-bearing nested request returned a valid response.
- Collect multiple true and false samples.
- Randomize or interleave sample order.
- Compare medians rather than a single request.
- Report insufficient measurements as `inconclusive`.

A repeatable gap between true and false samples confirms that attacker-controlled input reached SQL evaluation. No database contents need to be selected or extracted.

---

## Requirements

- Python 3.9 or later.
- No third-party Python packages are required by the supplied script.
- Network access to the authorized WordPress installation.
- For local testing:
  - A disposable WordPress 7.0.1 or 6.9.4 lab.
  - A database container or dedicated test database.
  - The web service bound to `127.0.0.1`.
  - No production credentials or data.

Check Python:

```bash
python3 --version
```

Optional syntax validation:

```bash
python3 -m py_compile WP2Shell_CVE-2026-63030_POC.py
```

---

## Installation

Rename the supplied script to a predictable filename:

```bash
mv 'poc(1).py' WP2Shell_CVE-2026-63030_POC.py
chmod +x WP2Shell_CVE-2026-63030_POC.py
```

Display global help:

```bash
python3 WP2Shell_CVE-2026-63030_POC.py --help
```

Display the version:

```bash
python3 WP2Shell_CVE-2026-63030_POC.py --version
```

---

## Commands

The script defines three commands:

```text
remote   Fingerprint and scan HTTP(S) WordPress targets.
local    Read the installed WordPress version from a source tree.
exploit  State-changing proof-of-concept path.
```

### Current Build Status

| Command | Status |
|---|---|
| `local` | Implemented |
| `remote` | CLI is defined, but `run_remote()` is currently a stub and returns an error |
| `exploit` | Contains state-changing functionality; restrict to a disposable local lab and separate it from defensive scanning |

The source currently prints the following for `remote`:

```text
Remote scanning not fully implemented in this snippet. Use 'local' or 'exploit'.
```

Restore a complete `run_remote()` implementation before advertising bulk remote scanning as functional.

---

# Local Version Assessment

The `local` command reads:

```text
/wp-includes/version.php
```

and extracts `$wp_version`.

## Basic usage

```bash
python3 WP2Shell_CVE-2026-63030_POC.py local \
  --wordpress-root /var/www/html
```

## JSON report

```bash
python3 WP2Shell_CVE-2026-63030_POC.py local \
  --wordpress-root /var/www/html \
  --format json \
  --output local-result.json
```

## Docker-based WordPress installation

When the WordPress container is named `wordpress`, copy or mount the source tree to the host, or run the version check inside the container:

```bash
docker compose exec wordpress php -r \
  'require "/var/www/html/wp-includes/version.php"; echo $wp_version, PHP_EOL;'
```

## Vulnerable lab setup with compose.yaml

Use the provided `compose.yaml` to run a disposable local lab bound to `127.0.0.1:8080`:

```bash
docker compose up -d
```

Once the first-time WordPress setup completes in the browser at `http://127.0.0.1:8080`, you can run:

```bash
python3 WP2Shell_CVE-2026-63030_POC.py remote \
  --authorized \
  --target http://127.0.0.1:8080 \
  --active-probe \
  --format json
```

To stop and remove the lab:

```bash
docker compose down -v
```

## Local command options

| Option | Description |
|---|---|
| `--wordpress-root PATH` | Required directory containing `wp-includes/version.php` |
| `-f, --format` | `table`, `json`, `jsonl`, or `csv` |
| `-o, --output FILE` | Write the report to a file |
| `--fail-on` | Exit-code policy: `never`, `vulnerable`, or `unknown` |

## Example vulnerable-version output

```json
[
  {
    "selected_version": "7.0.1",
    "version_assessment": "affected-wp2shell",
    "verdict": "CONFIRMED_AFFECTED_VERSION",
    "severity": "critical"
  }
]
```

A local version result confirms that the installed release is in the published affected range. It does not, by itself, demonstrate runtime exploitability or the effect of a backported patch.

---

# Remote Scanner Interface

The `remote` parser supports the following arguments, but the supplied `run_remote()` implementation is currently incomplete.

## Single target

Expected interface after restoring `run_remote()`:

```bash
python3 WP2Shell_CVE-2026-63030_POC.py remote \
  --authorized \
  --target https://wordpress.example \
  --active-probe
```

## Multiple explicit targets

```bash
python3 WP2Shell_CVE-2026-63030_POC.py remote \
  --authorized \
  --target https://site-one.example \
  --target https://site-two.example \
  --active-probe
```

## Target file

Create `targets.txt`:

```text
https://site-one.example/
https://site-two.example/blog/
```

Expected interface:

```bash
python3 WP2Shell_CVE-2026-63030_POC.py remote \
  --authorized \
  --targets-file targets.txt \
  --active-probe \
  --format jsonl \
  --output results.jsonl
```

## Standard input

```bash
cat targets.txt | python3 WP2Shell_CVE-2026-63030_POC.py remote \
  --authorized \
  --stdin
```

## Proxy usage

```bash
python3 WP2Shell_CVE-2026-63030_POC.py remote \
  --authorized \
  --target https://wordpress.example \
  --active-probe \
  --proxy http://127.0.0.1:8081
```

## Custom request header

```bash
python3 WP2Shell_CVE-2026-63030_POC.py remote \
  --authorized \
  --target https://wordpress.example \
  --header 'Authorization: Bearer TEST_TOKEN'
```

## Remote options

| Option | Purpose |
|---|---|
| `-u, --target URL` | Target URL; repeatable |
| `-l, --targets-file FILE` | One target per line; repeatable |
| `--stdin` | Read targets from stdin |
| `--authorized` | Required authorization acknowledgement |
| `--default-scheme` | Scheme applied when omitted |
| `-c, --concurrency` | Concurrent target workers |
| `--rate` | Aggregate request rate |
| `--timeout` | Per-request timeout |
| `--retries` | Retry count |
| `--max-targets` | Maximum number of accepted targets |
| `--max-body-bytes` | Maximum retained response body |
| `-k, --insecure` | Disable TLS verification |
| `--no-redirects` | Disable redirects |
| `--allow-cross-host-redirects` | Permit redirects to another host |
| `--proxy` | HTTP/HTTPS proxy |
| `-H, --header` | Custom header; repeatable |
| `--user-agent` | Override User-Agent |
| `--fingerprint-level` | `quick`, `standard`, or `extended` |
| `--active-probe` | Send the safe route-confusion probe |
| `--rest-endpoint` | `query`, `pretty`, or `both` |
| `--include-request-log` | Add URL, status, and timing metadata |
| `-f, --format` | `table`, `json`, `jsonl`, or `csv` |
| `-o, --output` | Write output to a file |
| `--fail-on` | Exit-code policy |

---

# Safe Runtime Validation

For a non-destructive local validation of both primitives, use a dedicated validator that:

- Refuses non-loopback hosts.
- Confirms route confusion first.
- Runs constant-condition timing samples only after route confusion succeeds.
- Does not extract data or execute commands.

Example workflow:

```bash
python3 wp2shell_local_validator.py check \
  --authorized \
  --target http://127.0.0.1:8080 \
  --sqli \
  --delay 2 \
  --samples 4 \
  --warmups 2 \
  --debug \
  --dump-dir evidence \
  --output result.json
```

Expected vulnerable-lab timing pattern:

```text
False controls: approximately 0.03โ€“0.10 seconds
True tests:     consistently delayed
```

Expected verdict:

```text
FULL_VULNERABILITY_PRIMITIVES_CONFIRMED
```

A timing test can execute the delay expression more than once, so a configured two-second delay may produce an observed delay near four seconds. The important signal is the repeatable separation between true and false conditions.

---

## Output Formats

### Table

Best for interactive terminal use:

```bash
--format table
```

### JSON

Best for evidence and integration:

```bash
--format json --output result.json
```

### JSON Lines

Best for large target sets:

```bash
--format jsonl --output results.jsonl
```

### CSV

Best for spreadsheets and reporting:

```bash
--format csv --output results.csv
```

---

## Verdicts

| Verdict | Meaning |
|---|---|
| `CONFIRMED_VULNERABLE_BEHAVIOR` | Runtime route-confusion behavior was observed |
| `CONFIRMED_AFFECTED_VERSION` | Local source version is in an affected range |
| `LIKELY_VULNERABLE` | Remote version evidence indicates an affected release |
| `VULNERABLE_SQLI_ONLY` | Version is affected by CVE-2026-60137 but outside the complete route-confusion range |
| `AFFECTED_VERSION_BUT_BEHAVIOR_NOT_OBSERVED` | Affected version detected, but safe runtime behavior was absent |
| `PATCHED_VERSION` | Version meets the published fixed boundary |
| `NOT_AFFECTED` | Version is outside the affected branch |
| `POTENTIALLY_EXPOSED_VERSION_UNKNOWN` | WordPress and the batch route were found, but the version is hidden |
| `WORDPRESS_VERSION_UNKNOWN` | WordPress detected without reliable version evidence |
| `ERROR` | The target could not be assessed |
| `NOT_WORDPRESS_OR_NOT_DETECTED` | No reliable WordPress evidence |

---

## Exit Codes

The scanner uses policy-driven exit codes.

| Code | Meaning |
|---:|---|
| `0` | No policy-triggering result, or `--fail-on never` |
| `2` | Vulnerable or affected result under the default policy |
| `3` | Unknown or inconclusive result when `--fail-on unknown` is selected |
| `1` | Argument, input, or incomplete-command error |

A vulnerability finding can therefore return a non-zero status intentionally.

Example:

```bash
python3 WP2Shell_CVE-2026-63030_POC.py local \
  --wordpress-root /var/www/html \
  --fail-on vulnerable

echo $?
```

---

## Vulnerable-versus-Fixed Differential Test

A strong validation compares two clean environments.

### Vulnerable environment

```text
WordPress 7.0.1
```

Expected:

```text
route-confusion-observed
repeatable true/false SQL timing difference
```

### Fixed environment

```text
WordPress 7.0.2
```

Expected:

```text
route-confusion-not-observed
SQLi timing test not reached or no valid timing oracle
```

Always recreate the WordPress volume when changing versions. Reusing a volume can preserve old or automatically updated core files.

```bash
docker compose down -v
docker compose pull
docker compose up -d
```

---

## Remediation

Update immediately to one of the fixed releases or a later supported release:

- WordPress 6.8.6.
- WordPress 6.9.5.
- WordPress 7.0.2.
- WordPress 7.1 beta2 or later, for prerelease testing.

Temporary controls should cover both batch endpoint forms:

```text
/wp-json/batch/v1
/?rest_route=/batch/v1
```

Additional defensive actions:

1. Review logs for anonymous batch requests, malformed internal URLs, nested batch structures, and unusual `author_exclude` values.
2. Review administrator accounts, plugin changes, unexpected PHP files, and database access after any period of exposure.
3. Verify WordPress Core checksums and restore from a known-good backup if compromise is suspected.
4. Rotate secrets and credentials stored in the WordPress database when SQL injection exploitation may have occurred.

Blocking the endpoint is a temporary mitigation, not a substitute for updating WordPress Core.

---

## Detection Ideas

Potential indicators include:

- Anonymous `POST` requests to either batch endpoint form.
- HTTP `207` responses containing nested `responses` arrays.
- Malformed internal URLs in batch request bodies.
- Nested `requests` objects inside another batch member.
- Scalar or SQL-shaped `author_exclude` values.
- Repeated paired requests with alternating fast and delayed responses.
- Unexpected WordPress administrator creation.
- Unexpected plugin installation or activation.
- New PHP files under writable WordPress directories.
- Database queries containing unusual `author__not_in` expressions.

---


## Known Limitations in the Supplied Script

- Cookie handling is not equivalent to a persistent browser session.
- Database table prefixes are assumed in some code paths.
- Database `FILE` privilege and filesystem paths vary by deployment.
- `INTO OUTFILE` is usually restricted and cannot overwrite existing files.
- WordPress may disable plugin installation through `DISALLOW_FILE_MODS`.
- Timing thresholds can be affected by proxies, WAFs, PHP timeouts, database timeouts, and load.
- Version strings can be hidden, spoofed, cached, or inconsistent.
- An observed affected version does not prove the absence of a security backport.
- A missing timing signal does not prove the server is patched.

---

## References

- WordPress 7.0.2 Security Release:  
  https://wordpress.org/news/2026/07/wordpress-7-0-2-release/

- WordPress 7.0.2 Documentation and changed files:  
  https://wordpress.org/documentation/wordpress-version/version-7-0-2/

- NVD โ€” CVE-2026-63030:  
  https://nvd.nist.gov/vuln/detail/CVE-2026-63030

- NVD โ€” CVE-2026-60137:  
  https://nvd.nist.gov/vuln/detail/CVE-2026-60137

- WordPress release archive:  
  https://wordpress.org/download/releases/

---

## Legal and Ethical Use

Use this project only when:

- You own the system, or
- You have explicit written authorization, and
- The requested test activity is within the agreed scope.

Do not expose deliberately vulnerable WordPress installations to the public Internet. Use disposable credentials, synthetic data, isolated networks, and clean snapshots. Destroy or reset the lab after testing.

The safest proof of vulnerability is the minimum evidence necessary to demonstrate the issue:

```text
Affected version
        +
Route-confusion behavior
        +
Repeatable constant-condition SQL timing oracle
```

Credential theft, persistence, webshell installation, and command execution are unnecessary for confirming that the vulnerability exists.