## https://sploitus.com/exploit?id=370A6437-567A-59DC-8F50-24EDC8AE7907
# wp2shell-poc
Independent proof-of-concept for the unauthenticated WordPress REST batch route-confusion
SQL injection associated with Searchlight Cyber's wp2shell advisory.
This repository is not Searchlight Cyber's official checker. `check` confirms the SQLi path,
`read` demonstrates database read, and `shell` opens a plugin-backed command shell either with
supplied administrator credentials or by first exercising the SQLi-to-admin bridge.

**Detection / IoCs:** Elastic Security Labs and Eye Security published detection guidance and indicators for this chain. See the [Elastic write-up](https://www.elastic.co/security-labs/wp2shell-wordpress-rce-detection-elastic-defend) and the [Eye Security defenders guide](https://labs.eye.security/wp2shell-defenders-guide/).
## Affected versions
Searchlight Cyber's advisory lists these wp2shell RCE exposure ranges:
| Version range | Status |
| ------------- | ------ |
| [options]
```
Or `pip install .` to get a `wp2shell` command on your `PATH`.
### check โ non-destructive vulnerability check
Prints passive WordPress markers and public version hints first, then sends a benign batch marker
probe. A vulnerable batch implementation returns HTTP 207 with the route-confusion marker pattern
`parse_path_failed`, `block_cannot_read`, and `rest_batch_not_allowed`.
The marker probe is based on the WordPress core fix. The malformed `///` request creates
`parse_path_failed`; a `/wp/v2/posts` request acts as a batch-allowed spacer; the
`/wp/v2/block-renderer/...` route is not batch-allowed but returns `block_cannot_read` if its
handler is reached anonymously; `/batch/v1` gives `rest_batch_not_allowed`. On vulnerable builds
the parse error shifts the batch handler arrays out of step, so the spacer request is dispatched
under the block-renderer handler. Fixed builds keep the arrays aligned, so this exact all-three
pattern should not appear for the crafted probe.
By default, `check` stops there and does not send an SQLi payload. Use `--confirm-sqli` when you
also want an active SQLi confirmation. The confirmation tries the UNION read primitive first and
falls back to paired timing probes if UNION reflection is unavailable.
The signals are independent: a version hint is only a hint, the marker pattern shows route
confusion, and `--confirm-sqli` shows a payload reached the database. A WAF can block the payload,
so a failed confirmation doesn't prove the bug is absent.
```
./wp2shell.py check http://target
./wp2shell.py check targets.txt # scan every URL in the file
```
### read โ extract data through SQL injection
```
./wp2shell.py read http://target # server fingerprint
./wp2shell.py read http://target --preset users # user logins and password hashes
./wp2shell.py read http://target --query "SELECT @@version"
```
By default extraction is `--technique auto`, which tries the available methods in this order:
1. **union** โ forges a fake `WP_Post` row via `UNION` and reads its title back from the REST
response as `||HEX(value)||`. The payload uses the same `/wp/v2/posts/999999` source route with
`orderby=none` and `per_page=500` so the fake row survives as a rendered post. One request per
value.
2. **error** โ `EXTRACTVALUE`/`UPDATEXML` leak ~15 bytes per request, when the target reflects
MySQL errors (e.g. `WP_DEBUG_DISPLAY` on).
3. **blind** โ boolean binary search, ~8 requests per character; reads the posts collection
`X-WP-Total` header as the true/false signal and needs no reflected value.
Force one with `--technique union|error|blind`. These read paths do not write database rows.
### shell โ command execution
With `--user` and `--password`, `shell` logs in with supplied administrator credentials and uses
WordPress plugin upload behavior.
Without credentials, `shell` first runs the pre-auth SQLi-to-admin bridge, logs in as the generated
administrator, then uploads the plugin shell.
```
./wp2shell.py shell http://target --user admin --password '' --cmd id
./wp2shell.py shell http://target --user admin --password '' -i # interactive shell
./wp2shell.py shell http://target --cmd id # pre-auth bridge
./wp2shell.py shell http://target -i # pre-auth interactive
```
`shell` uploads a plugin webshell (locked behind a random path and a per-run token) and prints its
path. The uploaded webshell is removed automatically. When the pre-auth bridge creates an
administrator, that generated account is removed automatically after the shell session finishes.
## Options
| Option | Applies to | Description |
| ------------------- | ---------- | -------------------------------------------------------------------- |
| `--proxy URL` | all | Route traffic through an HTTP proxy (for example, Burp). |
| `--timeout N` | all | Request timeout in seconds. |
| `--sleep N` | check | Delay used by the timing fallback for `--confirm-sqli`. |
| `--samples N` | check | Timing pairs used by the timing fallback for `--confirm-sqli`. |
| `--confirm-sqli` | check | Also send an active SQLi confirmation payload. |
| `--preset` | read | `fingerprint` or `users`. |
| `--technique` | read | `auto` (default), `union` (in-band, forges a fake post), `error` (in-band, needs visible DB errors), or `blind`. |
| `--query` | read | A scalar SQL expression to read. |
| `--prefix` | read | Database table prefix (default `wp_`). |
| `--max-length N` | read | Maximum characters read per value (default 128). |
| `--user` / `--password` | shell | Optional admin credentials; omit both to use the pre-auth bridge. |
| `--cmd` | shell | Command to run (omit when using `-i`). |
| `-i` / `--interactive` | shell | Open an interactive shell after deploying. |
## Remediation
Update to WordPress 7.0.2, or 6.9.5 if the site is on the 6.9 branch. Until then,
block both `/wp-json/batch/v1` and the `rest_route=/batch/v1` query parameter at
the edge, or require authentication for the batch endpoint via the
`rest_pre_dispatch` filter.
## Legal
For authorized security testing only. Use it exclusively against systems you own or have explicit
written permission to test. No warranty is provided and no liability is accepted for misuse.
## References
- WordPress 7.0.2 release announcement โ
- Searchlight Cyber wp2shell advisory โ
- Hackify โ wp2shell technical analysis โ
- sergiointel/wp2shell-poc SQLi-to-admin bridge โ
- Elastic Security Labs โ wp2shell detection & IoCs โ
- Eye Security โ wp2shell defenders guide โ