## https://sploitus.com/exploit?id=DE794BDF-395F-5543-8B31-41A8A3A60353
# CVE-2026-63030
The WordPress REST API **batch routing confusion + SQL injection** leads to a **unauthorized admin creation β remote code execution** complete exploitation chain. This tool is **only intended for authorized security testing, vulnerability reproduction, and research purposes**. It is **strictly prohibited from being used on unauthorized targets**.
## Vulnerability Description
| Item | Description |
|------|------|
| CVE | CVE-2026-63030 |
| Component | WordPress Core REST API (`/batch/v1`) |
| Affected Versions | WordPress **6.9.0 β 6.9.4**, **7.0.0 β 7.0.1** (as per official announcements) |
| Attack Prerequisite | The target has enabled the REST API batch function; the site must have at least one published article or page |
| Permission Requirement | **No login required (Pre-Auth)** |
| Damage | Creating an admin account and uploading plugins to achieve RCE |
## Attack Chain
```
REST batch routing confusion
β author__not_in SQL injection
β UNIONWP_Post / oEmbed cache / Customizer changeset
β Customizer switches to the existing admin context when published
β REST /wp/v2/users creates a new admin
β Uploading a one-time plugin from the backend
β Executing system commands
```
The core issue isnβt **stacked write libraries**, but rather:
1. The `serve_batch_request_v1()` verification phase and scheduling phase donβt synchronize the `$matches` / `$validation` indexes, causing **route confusion**. After this confusion, the string `author_exclude` enters `WP_Query`βs `author__not_in`, forming **SQL injection**.
2. Using `UNION SELECT` to forge the entire `wp_posts` table, triggering oEmbed/Customizer logic to switch the current userβs context to an existing admin.
3. Calling the REST user creation interface as an admin to assign a new admin account.
4. Uploading a plugin from the backend as an admin to execute commands.
## Environment Requirements
- Python **3.10+** (Standard library only, no third-party dependencies)
- The target is a WordPress instance that you have permission to test with
- The target PHP must retain at least one executable function (`proc_open` / `passthru` / `system` / `shell_exec` / `exec` / `popen`)
- If `disable_functions` disables all command execution functions, itβs still possible to create an admin, but RCE will fail.
## File Description
```
CVE-2026-63030/
βββ exp.py # Complete exploitation script (Single file, zero dependencies)
βββ README.md # This document
```
## Usage
### Minimum usage (Automatically creates an admin, defaults to `id`)
```bash
python3 exp.py --url http://192.168.1.10:8080/
```
### Customizing All Parameters
```bash
python3 exp.py --url http://192.168.199.134:9005/ --username mht --password 'mht666' --email mht@bdziyi.com --command 'id' --timeout 60
```
### Parameter Description
| Parameter | Required | Default | Description |
|----------|----------|------------|-------------|
| `--url` | Yes | None | The root URL of the target WordPress |
| `--username` | No | `wp2_` | The username of the admin to be created |
| `--password` | No | `Wp2!` | The password of the admin to be created |
| `--email` | No | `@wp2shell.invalid` | The email of the admin to be created |
| `--command` / `-c` | No | `id` | The command to be executed via a one-time plugin |
| `--timeout` | No | `60` | Single HTTP timeout (seconds) |
## Expected Output Example
```text
[*] Creating administrator via SQLi -> customizer bridge... [+] Generated administrator: labadmin
[*] Password: S3cure!pass
[*] Email: labadmin@lab.local
[*] Logging in as generated administrator... [+] Authenticated. [*] Uploading one-shot command plugin... [*] Plugin path: http://192.168.1.10:8080/wp-content/plugins/lab_wp2_cmd_xxxxxxxx/lab_wp2_cmd_xxxxxxxx.php
[*] Executing command: id
[+] command output:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
[via: proc_open]
[*] Deleting generated administrator... [+] Generated administrator removed. [*] Cleaning up temporary plugin... [+] Temporary plugin removed.
```
The script will attempt to:
1. Remove the newly created admin account
2. Delete the temporary plugin directory
If it fails midway, please manually clean up based on the account and password information in the output.
## Notes
1. **Only authorized environments**: Do not use this tool on unauthorized systems.
2. **Target restrictions**: The script automatically rejects literal target URLs on public networks. Only private network/IP-local addresses, loopback addresses, or domain names are allowed.
3. **PHP hardening**: If `has been disabled for security reasons` appears, it means `disable_functions` has disabled the corresponding functions. The PoC will try multiple methods in sequence; if all are disabled, RCE wonβt work, but an admin can still be created.
4. **Site content**: At least one published article or page is required as a seed for the oEmbed cache.
5. **Idempotency and cleanup**: Each run creates a new admin and temporary plugin. Successful paths will be automatically cleaned up; if failed, manually check `wp-admin/users.php` and `wp-content/plugins/`.
## Fix Suggestions
- Upgrade to the officially fixed version (based on WordPress security announcements)
- When patches are unavailable, temporarily restrict unauthorized access to `/batch/v1`
- In production environments, maintain `disable_functions` and minimal permission web user configurations to reduce the impact of RCE
## Disclaimer
The code in this repository is solely for security research and vulnerability reproduction purposes. Users must ensure compliance with local laws and the authorization scope of the target system. The author assumes no responsibility for any misuse of this code.