Files
metasploit-gs/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

493 lines
13 KiB
Ruby
Raw Normal View History

# -*- coding: binary -*-
2005-07-09 21:18:49 +00:00
require 'rex/post/process'
require 'rex/post/meterpreter/packet'
require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/channels/pools/stream_pool'
require 'rex/post/meterpreter/extensions/stdapi/stdapi'
2005-07-09 21:18:49 +00:00
require 'rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/image'
require 'rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/io'
require 'rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/memory'
require 'rex/post/meterpreter/extensions/stdapi/sys/process_subsystem/thread'
module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Sys
##
#
# This class implements the Rex::Post::Process interface.
#
##
class Process < Rex::Post::Process
include Rex::Post::Meterpreter::ObjectAliasesContainer
2013-08-30 16:28:33 -05:00
##
#
# Class methods
#
##
2013-08-30 16:28:33 -05:00
class << self
attr_accessor :client
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
2005-04-15 07:03:33 +00:00
# Returns the process identifier of the process supplied in key if it's
2005-11-15 05:22:13 +00:00
# valid.
#
2005-04-15 07:03:33 +00:00
def Process.[](key)
2020-03-12 09:59:08 +00:00
return if key.nil?
2005-04-15 07:03:33 +00:00
each_process { |p|
if (p['name'].downcase == key.downcase)
return p['pid']
end
}
2013-08-30 16:28:33 -05:00
2005-04-15 07:03:33 +00:00
return nil
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
2024-01-06 15:54:49 -05:00
# Attaches to the supplied process with a given set of permissions.
2005-11-15 05:22:13 +00:00
#
2005-04-16 07:29:06 +00:00
def Process.open(pid = nil, perms = nil)
real_perms = 0
2013-08-30 16:28:33 -05:00
if (perms == nil)
perms = PROCESS_ALL
end
2013-08-30 16:28:33 -05:00
if (perms & PROCESS_READ) > 0
real_perms |= PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION
end
2013-08-30 16:28:33 -05:00
if (perms & PROCESS_WRITE) > 0
real_perms |= PROCESS_SET_SESSIONID | PROCESS_VM_WRITE | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION
end
2013-08-30 16:28:33 -05:00
if (perms & PROCESS_EXECUTE) > 0
real_perms |= PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_CREATE_PROCESS | PROCESS_SUSPEND_RESUME
end
2013-08-30 16:28:33 -05:00
return _open(pid, real_perms)
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
# Low-level process open.
#
2005-04-16 07:29:06 +00:00
def Process._open(pid, perms, inherit = false)
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_ATTACH)
2013-08-30 16:28:33 -05:00
if (pid == nil)
pid = 0
end
2013-08-30 16:28:33 -05:00
# Populate the request
request.add_tlv(TLV_TYPE_PID, pid)
request.add_tlv(TLV_TYPE_PROCESS_PERMS, perms)
request.add_tlv(TLV_TYPE_INHERIT, inherit)
2013-08-30 16:28:33 -05:00
# Transmit the request
response = self.client.send_request(request)
handle = response.get_tlv_value(TLV_TYPE_HANDLE)
2013-08-30 16:28:33 -05:00
# If the handle is valid, allocate a process instance and return it
if (handle != nil)
2005-04-16 07:29:06 +00:00
return self.new(pid, handle)
end
2013-08-30 16:28:33 -05:00
return nil
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
# Executes an application using the arguments provided
2005-04-16 20:37:27 +00:00
#
# Hash arguments supported:
#
# Hidden => true/false
# Channelized => true/false
# Suspended => true/false
2005-07-25 03:07:57 +00:00
# InMemory => true/false
2005-04-16 20:37:27 +00:00
#
def Process.execute(path, arguments = nil, opts = nil)
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_EXECUTE)
2005-04-16 20:37:27 +00:00
flags = 0
2013-08-30 16:28:33 -05:00
2005-04-16 20:37:27 +00:00
# If we were supplied optional arguments...
if (opts != nil)
if (opts['Hidden'])
flags |= PROCESS_EXECUTE_FLAG_HIDDEN
end
if (opts['Channelized'])
flags |= PROCESS_EXECUTE_FLAG_CHANNELIZED
end
if (opts['Suspended'])
flags |= PROCESS_EXECUTE_FLAG_SUSPENDED
end
2008-04-28 16:57:49 +00:00
if (opts['UseThreadToken'])
flags |= PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN
end
if (opts['Desktop'])
flags |= PROCESS_EXECUTE_FLAG_DESKTOP
end
if (opts['Session'])
flags |= PROCESS_EXECUTE_FLAG_SESSION
request.add_tlv( TLV_TYPE_PROCESS_SESSION, opts['Session'] )
end
2019-04-18 15:30:26 +08:00
if (opts['Subshell'])
flags |= PROCESS_EXECUTE_FLAG_SUBSHELL
end
2021-09-21 11:36:50 +01:00
if (opts['Pty'])
flags |= PROCESS_EXECUTE_FLAG_PTY
end
2019-12-17 01:04:55 +01:00
if (opts['ParentPid'])
request.add_tlv(TLV_TYPE_PARENT_PID, opts['ParentPid']);
request.add_tlv(TLV_TYPE_PROCESS_PERMS, PROCESS_ALL_ACCESS)
request.add_tlv(TLV_TYPE_INHERIT, false)
end
2005-07-22 03:38:37 +00:00
inmem = opts['InMemory']
if inmem
2013-08-30 16:28:33 -05:00
2005-07-22 03:38:37 +00:00
# add the file contents into the tlv
f = ::File.new(path, 'rb')
request.add_tlv(TLV_TYPE_VALUE_DATA, f.read(f.stat.size))
2005-07-22 03:38:37 +00:00
f.close
2013-08-30 16:28:33 -05:00
2005-07-22 03:38:37 +00:00
# replace the path with the "dummy"
path = inmem.kind_of?(String) ? inmem : 'cmd'
end
end
2013-08-30 16:28:33 -05:00
2011-07-21 15:26:15 +00:00
request.add_tlv(TLV_TYPE_PROCESS_PATH, client.unicode_filter_decode( path ));
2013-08-30 16:28:33 -05:00
2005-07-22 03:38:37 +00:00
# If process arguments were supplied
if (arguments != nil)
request.add_tlv(TLV_TYPE_PROCESS_ARGUMENTS, arguments);
2005-04-16 20:37:27 +00:00
end
2013-08-30 16:28:33 -05:00
2005-04-16 20:37:27 +00:00
request.add_tlv(TLV_TYPE_PROCESS_FLAGS, flags);
2013-08-30 16:28:33 -05:00
2005-04-16 20:37:27 +00:00
response = client.send_request(request)
2013-08-30 16:28:33 -05:00
2005-04-16 20:37:27 +00:00
# Get the response parameters
pid = response.get_tlv_value(TLV_TYPE_PID)
handle = response.get_tlv_value(TLV_TYPE_PROCESS_HANDLE)
channel_id = response.get_tlv_value(TLV_TYPE_CHANNEL_ID)
channel = nil
2013-08-30 16:28:33 -05:00
2005-04-16 20:37:27 +00:00
# If we were creating a channel out of this
if (channel_id != nil)
channel = Rex::Post::Meterpreter::Channels::Pools::StreamPool.new(client,
2020-03-12 15:45:31 -04:00
channel_id, "stdapi_process", CHANNEL_FLAG_SYNCHRONOUS, response)
2005-04-16 20:37:27 +00:00
end
2013-08-30 16:28:33 -05:00
2009-06-17 01:34:33 +00:00
# Return a process instance
2005-04-16 20:37:27 +00:00
return self.new(pid, handle, channel)
end
2013-08-30 16:28:33 -05:00
2021-05-04 20:48:58 +01:00
#
# Execute an application and capture the output
#
def Process.capture_output(path, arguments = nil, opts = nil, time_out = 15)
start = Time.now.to_i
process = execute(path, arguments, opts)
data = ""
# Wait up to time_out seconds for the first bytes to arrive
while (d = process.channel.read)
data << d
if d == ""
if Time.now.to_i - start < time_out
sleep 0.1
else
break
end
end
end
data.chomp! if data
begin
process.channel.close
rescue IOError => e
# Channel was already closed, but we got the cmd output, so let's soldier on.
end
process.close
return data
end
2005-11-15 05:22:13 +00:00
#
# Kills one or more processes.
#
2005-04-18 00:10:38 +00:00
def Process.kill(*args)
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_KILL)
2013-08-30 16:28:33 -05:00
2005-04-18 00:10:38 +00:00
args.each { |id|
request.add_tlv(TLV_TYPE_PID, id)
}
2013-08-30 16:28:33 -05:00
2005-04-18 00:10:38 +00:00
client.send_request(request)
2013-08-30 16:28:33 -05:00
2005-04-18 00:10:38 +00:00
return true
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
# Gets the process id that the remote side is executing under.
#
def Process.getpid
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_GETPID)
2013-08-30 16:28:33 -05:00
response = client.send_request(request)
2013-08-30 16:28:33 -05:00
return response.get_tlv_value(TLV_TYPE_PID)
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
# Enumerates all of the elements in the array returned by get_processes.
#
2005-04-15 07:03:33 +00:00
def Process.each_process(&block)
self.get_processes.each(&block)
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
2012-05-16 17:22:55 -06:00
# Returns a ProcessList of processes as Hash objects with keys for 'pid',
# 'ppid', 'name', 'path', 'user', 'session' and 'arch'.
2005-11-15 05:22:13 +00:00
#
2005-04-15 07:03:33 +00:00
def Process.get_processes
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_GET_PROCESSES)
2012-05-16 17:22:55 -06:00
processes = ProcessList.new
2013-08-30 16:28:33 -05:00
2005-04-15 07:03:33 +00:00
response = client.send_request(request)
2013-08-30 16:28:33 -05:00
2005-04-15 07:03:33 +00:00
response.each(TLV_TYPE_PROCESS_GROUP) { |p|
arch = ""
2013-08-30 16:28:33 -05:00
pa = p.get_tlv_value(TLV_TYPE_PROCESS_ARCH)
if !pa.nil?
2010-01-25 10:49:51 +00:00
if pa == 1 # PROCESS_ARCH_X86
arch = ARCH_X86
2010-01-25 10:49:51 +00:00
elsif pa == 2 # PROCESS_ARCH_X64
arch = ARCH_X64
end
else
arch = p.get_tlv_value(TLV_TYPE_PROCESS_ARCH_NAME)
end
2013-08-30 16:28:33 -05:00
processes <<
2005-04-15 07:03:33 +00:00
{
'pid' => p.get_tlv_value(TLV_TYPE_PID),
2012-05-16 17:22:55 -06:00
'ppid' => p.get_tlv_value(TLV_TYPE_PARENT_PID),
2011-07-21 15:26:15 +00:00
'name' => client.unicode_filter_encode( p.get_tlv_value(TLV_TYPE_PROCESS_NAME) ),
'path' => client.unicode_filter_encode( p.get_tlv_value(TLV_TYPE_PROCESS_PATH) ),
'session' => p.get_tlv_value(TLV_TYPE_PROCESS_SESSION),
2011-07-21 15:26:15 +00:00
'user' => client.unicode_filter_encode( p.get_tlv_value(TLV_TYPE_USER_NAME) ),
'arch' => arch
2005-04-15 07:03:33 +00:00
}
}
2013-08-30 16:28:33 -05:00
2005-04-15 07:03:33 +00:00
return processes
end
2013-08-30 16:28:33 -05:00
2005-12-13 05:59:59 +00:00
#
# An alias for get_processes.
#
def Process.processes
self.get_processes
end
2013-08-30 16:28:33 -05:00
#
# Search memory for supplied regexes and return matches
#
def Process.memory_search(pid: 0, needles: [''], min_match_length: 5, max_match_length: 127)
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_MEMORY_SEARCH)
request.add_tlv(TLV_TYPE_PID, pid)
needles.each { |needle| request.add_tlv(TLV_TYPE_MEMORY_SEARCH_NEEDLE, needle) }
request.add_tlv(TLV_TYPE_MEMORY_SEARCH_MATCH_LEN, max_match_length)
request.add_tlv(TLV_TYPE_UINT, min_match_length)
self.client.send_request(request)
end
##
#
# Instance methods
#
##
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
# Initializes the process instance and its aliases.
#
2005-04-16 20:37:27 +00:00
def initialize(pid, handle, channel = nil)
self.client = self.class.client
self.handle = handle
self.channel = channel
2013-08-30 16:28:33 -05:00
2005-04-16 07:29:06 +00:00
# If the process identifier is zero, then we must lookup the current
# process identifier
if (pid == 0)
self.pid = client.sys.process.getpid
else
self.pid = pid
end
2013-08-30 16:28:33 -05:00
initialize_aliases(
{
2005-04-15 07:53:20 +00:00
'image' => Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessSubsystem::Image.new(self),
2005-04-16 20:37:27 +00:00
'io' => Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessSubsystem::IO.new(self),
'memory' => Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessSubsystem::Memory.new(self),
2005-04-16 07:29:06 +00:00
'thread' => Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessSubsystem::Thread.new(self),
})
2013-08-30 16:28:33 -05:00
2016-03-04 12:08:19 -06:00
# Ensure the remote object is closed when all references are removed
ObjectSpace.define_finalizer(self, self.class.finalize(client, handle))
end
2013-08-30 16:28:33 -05:00
2016-03-04 12:08:19 -06:00
def self.finalize(client, handle)
proc do
deferred_close_proc = proc do
begin
self.close(client, handle)
rescue => e
elog("finalize method for Process failed", error: e)
end
end
# Schedule the finalizing logic out-of-band; as this logic might be called in the context of a Signal.trap, which can't synchronize mutexes
client.framework.sessions.schedule(deferred_close_proc)
end
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
# Returns the executable name of the process.
#
2005-04-16 07:29:06 +00:00
def name
return get_info()['name']
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
# Returns the path to the process' executable.
#
2005-04-16 07:29:06 +00:00
def path
return get_info()['path']
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
#
# Closes the handle to the process that was opened.
#
def self.close(client, handle)
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_CLOSE)
2005-04-15 07:03:33 +00:00
request.add_tlv(TLV_TYPE_HANDLE, handle)
client.send_request(request, nil)
handle = nil
2005-04-15 07:03:33 +00:00
return true
end
2013-08-30 16:28:33 -05:00
#
# Instance method
#
2016-03-04 12:08:19 -06:00
def close(handle = self.handle)
unless self.pid.nil?
ObjectSpace.undefine_finalizer(self)
self.class.close(self.client, handle)
self.pid = nil
end
end
2013-08-30 16:28:33 -05:00
#
# Block until this process terminates on the remote side.
2024-01-06 15:54:49 -05:00
# By default we choose not to allow a packet response timeout to
# occur as we may be waiting indefinatly for the process to terminate.
#
def wait( timeout = -1 )
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_WAIT)
2013-08-30 16:28:33 -05:00
request.add_tlv(TLV_TYPE_HANDLE, self.handle)
2013-08-30 16:28:33 -05:00
self.client.send_request(request, timeout)
2013-08-30 16:28:33 -05:00
self.handle = nil
2013-08-30 16:28:33 -05:00
return true
end
2013-08-30 16:28:33 -05:00
2005-11-15 05:22:13 +00:00
attr_reader :client, :handle, :channel, :pid # :nodoc:
protected
2005-11-15 05:22:13 +00:00
attr_writer :client, :handle, :channel, :pid # :nodoc:
2005-04-16 07:29:06 +00:00
2005-11-15 05:22:13 +00:00
#
# Gathers information about the process and returns a hash.
#
2005-04-16 07:29:06 +00:00
def get_info
request = Packet.create_request(COMMAND_ID_STDAPI_SYS_PROCESS_GET_INFO)
2005-04-16 07:29:06 +00:00
info = {}
request.add_tlv(TLV_TYPE_HANDLE, handle)
# Send the request
response = client.send_request(request)
# Populate the hash
2011-07-21 15:26:15 +00:00
info['name'] = client.unicode_filter_encode( response.get_tlv_value(TLV_TYPE_PROCESS_NAME) )
info['path'] = client.unicode_filter_encode( response.get_tlv_value(TLV_TYPE_PROCESS_PATH) )
2005-04-16 07:29:06 +00:00
return info
end
end
2012-05-16 17:22:55 -06:00
#
# Simple wrapper class for storing processes
#
class ProcessList < Array
#
2016-08-10 13:30:09 -05:00
# Create a Rex::Text::Table out of the processes stored in this list
2012-05-16 17:22:55 -06:00
#
2016-08-10 13:30:09 -05:00
# +opts+ is passed on to Rex::Text::Table.new, mostly unmolested
2012-05-16 17:22:55 -06:00
#
# Note that this output is affected by Rex::Post::Meterpreter::Client#unicode_filter_encode
#
def to_table(opts={})
if empty?
2016-08-10 13:30:09 -05:00
return Rex::Text::Table.new(opts)
2012-05-16 17:22:55 -06:00
end
2013-08-30 16:28:33 -05:00
column_headers = [ "PID", "PPID", "Name", "Arch", "Session", "User", "Path" ]
column_headers.delete_if do |h|
none? { |process| process.has_key?(h.downcase) } ||
all? { |process| process[h.downcase].nil? }
2017-06-12 01:20:59 -04:00
end
2013-08-30 16:28:33 -05:00
2012-05-16 17:22:55 -06:00
opts = {
'Header' => 'Process List',
'Indent' => 1,
'Columns' => column_headers
2012-05-16 17:22:55 -06:00
}.merge(opts)
2013-08-30 16:28:33 -05:00
2016-08-10 13:30:09 -05:00
tbl = Rex::Text::Table.new(opts)
each do |process|
tbl << column_headers.map do |header|
col = header.downcase
next unless process.keys.any? { |process_header| process_header == col }
val = process[col]
if col == 'session'
val == 0xFFFFFFFF ? '' : val.to_s
else
val
end
end
end
2013-08-30 16:28:33 -05:00
2012-05-16 17:22:55 -06:00
tbl
end
end
end; end; end; end; end; end