Skip to content

Commit 92e8def

Browse files
author
kernelsmith
committed
adds suspend to meterp and adds full pid validation
This fully fixes RM7223 and adds the suspend command to the meterpreter interface. Suspend allows you to suspend and resume running processes on the targethost. It was originally written as a post module (and the dll version will be submitted as such later), but egypt suggested I add it to meterpreter
1 parent 3b8914c commit 92e8def

File tree

1 file changed

+74
-31
lines changed
  • lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi

1 file changed

+74
-31
lines changed

lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb

Lines changed: 74 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def commands
8282
"shell" => "Drop into a system command shell",
8383
"shutdown" => "Shuts down the remote computer",
8484
"steal_token" => "Attempts to steal an impersonation token from the target process",
85-
"suspend" => "Suspends or resumes a list of processes"
85+
"suspend" => "Suspends or resumes a list of processes",
8686
"sysinfo" => "Gets information about the remote system, such as OS",
8787
}
8888
reqs = {
@@ -282,17 +282,17 @@ def cmd_kill(*args)
282282
end
283283

284284
# validate all the proposed pids first so we can bail if one is bogus
285-
args.each do |arg|
286-
if not is_valid_pid?(arg)
287-
print_error("#{arg} is not a valid pid")
288-
cmd_kill_help
289-
return false
290-
end
285+
clean_pids = validate_pids(args)
286+
args.uniq!
287+
diff = args - clean_pids.map {|e| e.to_s}
288+
if not diff.empty? # then we had an invalid pid
289+
print_error("The following pids are not valid:#{diff.join(", ").to_s}, quitting")
290+
return false
291291
end
292292

293293
# kill kill kill
294-
print_line("Killing: #{args.join(", ")}")
295-
client.sys.process.kill(*(args.map { |x| x.to_i }))
294+
print_line("Killing: #{clean_pids.join(", ").to_s}")
295+
client.sys.process.kill(*(clean_pids.map { |x| x }))
296296
return true
297297
end
298298

@@ -304,15 +304,56 @@ def cmd_kill_help
304304
end
305305

306306
#
307-
# Checks if +pid+ is a valid looking pid
308-
#
309-
def is_valid_pid?(pid)
310-
# in lieu of checking server side for pid validity at the moment, we just sanity check here
311-
pid.strip!
312-
return false if pid.strip =~ /^-/ # invalid if it looks "negative"
313-
return true if pid == "0" # allow them to kill pid 0, otherwise false
314-
# cuz everything returned from .to_i that's not an int returns 0, we depend on the statement above
315-
return true if pid.to_i > 0
307+
# validates an array of pids against the running processes on target host
308+
# behavior can be controlled to allow/deny proces 0 and the session's process
309+
# the pids:
310+
# - are converted to integers
311+
# - have had pid 0 removed unless allow_pid_0
312+
# - have had current session pid removed unless allow_session_pid (to protect the session)
313+
# - have redundant entries removed
314+
#
315+
# @param pids [Array<String>] The pids to validate
316+
# @param allow_pid_0 [Boolean] whether to consider a pid of 0 as valid
317+
# @param allow_session_pid [Boolean] whether to consider a pid = the current session pid as valid
318+
# @return [Array] Returns an array of valid pids
319+
320+
def validate_pids(arr_pids, allow_pid_0 = false, allow_session_pid = false)
321+
322+
return [] if (arr_pids.class != Array or arr_pids.empty?)
323+
pids = arr_pids.dup
324+
clean_pids = []
325+
# to minimize network traffic, we only get host processes once
326+
host_processes = client.sys.process.get_processes
327+
if host_processes.length < 1
328+
print_error "No running processes found on the target host."
329+
return []
330+
end
331+
332+
# get the current session pid so we don't suspend it later
333+
mypid = client.sys.process.getpid.to_i
334+
335+
# we convert to integers here separately because we want to uniq this array first so we
336+
# can avoid redundant lookups later
337+
pids.each_with_index do |pid,idx|
338+
next if pid.nil?
339+
pids[idx] = pid.to_i
340+
end
341+
# uniq'ify
342+
pids.uniq!
343+
# now we look up the pids & remove bad stuff if nec
344+
pids.delete_if do |p|
345+
( (p == 0 and not allow_pid_0) or (p == mypid and not allow_session_pid) )
346+
end
347+
pids.each do |pid|
348+
# find the process with this pid
349+
theprocess = host_processes.select {|x| x["pid"] == pid}.first
350+
if ( theprocess.nil? )
351+
next
352+
else
353+
clean_pids << pid
354+
end
355+
end
356+
return clean_pids
316357
end
317358

318359
#
@@ -715,26 +756,28 @@ def cmd_suspend(*args)
715756
continue = args.delete("-c") || false
716757
resume = args.delete ("-r") || false
717758

718-
# TODO add -u/-r to unsuspend/resume and -c continue
719759
# validate all the proposed pids first so we can bail if one is bogus
720-
args.each do |arg|
721-
if not is_valid_pid?(arg)
722-
print_error("#{arg} is not a valid pid")
723-
cmd_suspend_help
760+
clean_pids = validate_pids(args)
761+
args.uniq!
762+
diff = args - clean_pids.map {|e| e.to_s}
763+
if not diff.empty? # then we had an invalid pid
764+
print_error("The following pids are not valid:#{diff.join(", ").to_s}")
765+
if continue
766+
print_status("Continuing. Invalid args have been removed from the list.")
767+
else
768+
print_error("Quitting. Use -c to continue using only the valid pids.")
724769
return false
725770
end
726771
end
727772

728-
# suspend
729-
print_line("Suspending: #{args.join(", ")}")
730773
#client.sys.process.kill(*(args.map { |x| x.to_i }))
731774
targetprocess = nil
732775
if resume
776+
print_status("Resuming: #{clean_pids.join(", ").to_s}")
733777
begin
734-
pids.each do |pid|
778+
clean_pids.each do |pid|
735779
print_status("Targeting process with PID #{pid}...")
736780
targetprocess = client.sys.process.open(pid, PROCESS_ALL_ACCESS)
737-
vprint_status "Resuming threads"
738781
targetprocess.thread.each_thread do |x|
739782
targetprocess.thread.open(x).resume
740783
end
@@ -747,12 +790,12 @@ def cmd_suspend(*args)
747790
targetprocess.close if targetprocess
748791
return false unless continue
749792
end
750-
else
793+
else # suspend
794+
print_status("Suspending: #{clean_pids.join(", ").to_s}")
751795
begin
752-
pids.each do |pid|
796+
clean_pids.each do |pid|
753797
print_status("Targeting process with PID #{pid}...")
754798
targetprocess = client.sys.process.open(pid, PROCESS_ALL_ACCESS)
755-
vprint_status "Suspending threads"
756799
targetprocess.thread.each_thread do |x|
757800
targetprocess.thread.open(x).suspend
758801
end
@@ -774,7 +817,7 @@ def cmd_suspend(*args)
774817
#
775818
def cmd_suspend_help
776819
print_line("Usage: suspend [options] pid1 pid2 pid3 ...\n\nSuspend one or more processes.")
777-
print @@connect_opts.usage
820+
print @@suspend_opts.usage
778821
end
779822

780823
end

0 commit comments

Comments
 (0)