See #4430, committing upstream fixes to packetfu to enable app parsing. This will change very soon, but at least get the two repo's synced up.
git-svn-id: file:///home/svn/framework3/trunk@12662 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
76ebc59f29
commit
c95080d83d
|
@ -36,11 +36,6 @@ module PacketFu
|
||||||
end
|
end
|
||||||
require "packetfu/capture"
|
require "packetfu/capture"
|
||||||
require "packetfu/inject"
|
require "packetfu/inject"
|
||||||
else
|
|
||||||
# At this moment, we don't care about missing pcaprub for PacketFu; we do need to deal
|
|
||||||
# with this eventually if we unseat Racket, but for now, PacketFu is just being used
|
|
||||||
# to read/write libpcap files, which doesn't require pcaprub.
|
|
||||||
# warn "Warning: Missing pcaprub, cannot load PacketFu::Capture or PacketFu::Inject"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -53,6 +48,7 @@ require "packetfu/ip"
|
||||||
require "packetfu/arp"
|
require "packetfu/arp"
|
||||||
require "packetfu/icmp"
|
require "packetfu/icmp"
|
||||||
require "packetfu/udp"
|
require "packetfu/udp"
|
||||||
|
require "packetfu/hsrp" # Depends on UDP
|
||||||
require "packetfu/tcp"
|
require "packetfu/tcp"
|
||||||
require "packetfu/ipv6" # This is pretty minimal.
|
require "packetfu/ipv6" # This is pretty minimal.
|
||||||
require "packetfu/utils"
|
require "packetfu/utils"
|
||||||
|
@ -60,7 +56,9 @@ require "packetfu/config"
|
||||||
|
|
||||||
module PacketFu
|
module PacketFu
|
||||||
|
|
||||||
VERSION = "1.0.1" # Version 1.0.0 was released July 31, 2010
|
# Version 1.0.0 was released July 31, 2010
|
||||||
|
# Version 1.0.1 is unreleased.
|
||||||
|
VERSION = "1.0.1"
|
||||||
|
|
||||||
# Returns the current version of PacketFu. Incremented every once
|
# Returns the current version of PacketFu. Incremented every once
|
||||||
# in a while, when I remember
|
# in a while, when I remember
|
||||||
|
|
|
@ -10,8 +10,11 @@ PacketFu is rdoc-compatable. In the same directory as this file, run "rdoc" by i
|
||||||
== Requirements
|
== Requirements
|
||||||
|
|
||||||
PcapRub:
|
PcapRub:
|
||||||
|
|
||||||
$ svn co http://www.metasploit.com/svn/framework3/trunk/external/pcaprub
|
$ svn co http://www.metasploit.com/svn/framework3/trunk/external/pcaprub
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
$ rvm gem install pcaprub
|
$ rvm gem install pcaprub
|
||||||
|
|
||||||
Marshall Beddoe's PcapRub is required only for packet reading and writing from a network interfaces (which is a pretty big only). PcapRub itself relies on libpcap 0.9.8 or later for packet injection. PcapRub also requires root privilieges to access the interface directly.
|
Marshall Beddoe's PcapRub is required only for packet reading and writing from a network interfaces (which is a pretty big only). PcapRub itself relies on libpcap 0.9.8 or later for packet injection. PcapRub also requires root privilieges to access the interface directly.
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
module PacketFu
|
||||||
|
|
||||||
|
# HSRPHeader is a complete HSRP struct, used in HSRPPacket. HSRP is typically used for
|
||||||
|
# fault-tolerant default gateway in IP routing environment.
|
||||||
|
#
|
||||||
|
# For more on HSRP packets, see http://www.networksorcery.com/enp/protocol/hsrp.htm
|
||||||
|
#
|
||||||
|
# Submitted by fropert@packetfault.org. Thanks, Francois!
|
||||||
|
#
|
||||||
|
# ==== Header Definition
|
||||||
|
#
|
||||||
|
# Int8 :hsrp_version Default: 0 # Version
|
||||||
|
# Int8 :hsrp_opcode # Opcode
|
||||||
|
# Int8 :hsrp_state # State
|
||||||
|
# Int8 :hsrp_hellotime Default: 3 # Hello Time
|
||||||
|
# Int8 :hsrp_holdtime Default: 10 # Hold Time
|
||||||
|
# Int8 :hsrp_priority # Priority
|
||||||
|
# Int8 :hsrp_group # Group
|
||||||
|
# Int8 :hsrp_reserved Default: 0 # Reserved
|
||||||
|
# String :hsrp_password # Authentication Data
|
||||||
|
# Octets :hsrp_vip # Virtual IP Address
|
||||||
|
# String :body
|
||||||
|
class HSRPHeader < Struct.new(:hsrp_version, :hsrp_opcode, :hsrp_state,
|
||||||
|
:hsrp_hellotime, :hsrp_holdtime,
|
||||||
|
:hsrp_priority, :hsrp_group,
|
||||||
|
:hsrp_reserved, :hsrp_password,
|
||||||
|
:hsrp_vip, :body)
|
||||||
|
|
||||||
|
include StructFu
|
||||||
|
|
||||||
|
def initialize(args={})
|
||||||
|
super(
|
||||||
|
Int8.new(args[:hsrp_version] || 0),
|
||||||
|
Int8.new(args[:hsrp_opcode]),
|
||||||
|
Int8.new(args[:hsrp_state]),
|
||||||
|
Int8.new(args[:hsrp_hellotime] || 3),
|
||||||
|
Int8.new(args[:hsrp_holdtime] || 10),
|
||||||
|
Int8.new(args[:hsrp_priority]),
|
||||||
|
Int8.new(args[:hsrp_group]),
|
||||||
|
Int8.new(args[:hsrp_reserved] || 0),
|
||||||
|
StructFu::String.new.read(args[:hsrp_password] || "cisco\x00\x00\x00"),
|
||||||
|
Octets.new.read(args[:hsrp_vip] || ("\x00" * 4)),
|
||||||
|
StructFu::String.new.read(args[:body])
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the object in string form.
|
||||||
|
def to_s
|
||||||
|
self.to_a.map {|x| x.to_s}.join
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reads a string to populate the object.
|
||||||
|
def read(str)
|
||||||
|
force_binary(str)
|
||||||
|
return self if str.nil?
|
||||||
|
self[:hsrp_version].read(str[0,1])
|
||||||
|
self[:hsrp_opcode].read(str[1,1])
|
||||||
|
self[:hsrp_state].read(str[2,1])
|
||||||
|
self[:hsrp_hellotime].read(str[3,1])
|
||||||
|
self[:hsrp_holdtime].read(str[4,1])
|
||||||
|
self[:hsrp_priority].read(str[5,1])
|
||||||
|
self[:hsrp_group].read(str[6,1])
|
||||||
|
self[:hsrp_reserved].read(str[7,1])
|
||||||
|
self[:hsrp_password].read(str[8,8])
|
||||||
|
self[:hsrp_vip].read(str[16,4])
|
||||||
|
self[:body].read(str[20,str.size]) if str.size > 20
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Setter for the type.
|
||||||
|
def hsrp_version=(i); typecast i; end
|
||||||
|
# Getter for the type.
|
||||||
|
def hsrp_version; self[:hsrp_version].to_i; end
|
||||||
|
# Setter for the type.
|
||||||
|
def hsrp_opcode=(i); typecast i; end
|
||||||
|
# Getter for the type.
|
||||||
|
def hsrp_opcode; self[:hsrp_opcode].to_i; end
|
||||||
|
# Setter for the type.
|
||||||
|
def hsrp_state=(i); typecast i; end
|
||||||
|
# Getter for the type.
|
||||||
|
def hsrp_state; self[:hsrp_state].to_i; end
|
||||||
|
# Setter for the type.
|
||||||
|
def hsrp_hellotime=(i); typecast i; end
|
||||||
|
# Getter for the type.
|
||||||
|
def hsrp_hellotime; self[:hsrp_hellotime].to_i; end
|
||||||
|
# Setter for the type.
|
||||||
|
def hsrp_holdtime=(i); typecast i; end
|
||||||
|
# Getter for the type.
|
||||||
|
def hsrp_holdtime; self[:hsrp_holdtime].to_i; end
|
||||||
|
# Setter for the type.
|
||||||
|
def hsrp_priority=(i); typecast i; end
|
||||||
|
# Getter for the type.
|
||||||
|
def hsrp_priority; self[:hsrp_priority].to_i; end
|
||||||
|
# Setter for the type.
|
||||||
|
def hsrp_group=(i); typecast i; end
|
||||||
|
# Getter for the type.
|
||||||
|
def hsrp_group; self[:hsrp_group].to_i; end
|
||||||
|
# Setter for the type.
|
||||||
|
def hsrp_reserved=(i); typecast i; end
|
||||||
|
# Getter for the type.
|
||||||
|
def hsrp_reserved; self[:hsrp_reserved].to_i; end
|
||||||
|
|
||||||
|
def hsrp_addr=(addr)
|
||||||
|
self[:hsrp_vip].read_quad(addr)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a more readable IP source address.
|
||||||
|
def hsrp_addr
|
||||||
|
self[:hsrp_vip].to_x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# HSRPPacket is used to construct HSRP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
|
||||||
|
#
|
||||||
|
# == Example
|
||||||
|
#
|
||||||
|
# hsrp_pkt.new
|
||||||
|
# hsrp_pkt.hsrp_opcode = 0
|
||||||
|
# hsrp_pkt.hsrp_state = 16
|
||||||
|
# hsrp_pkt.hsrp_priority = 254
|
||||||
|
# hsrp_pkt.hsrp_group = 1
|
||||||
|
# hsrp_pkt.hsrp_vip = 10.100.100.254
|
||||||
|
# hsrp_pkt.recalc
|
||||||
|
# hsrp_pkt.to_f('/tmp/hsrp.pcap')
|
||||||
|
#
|
||||||
|
# == Parameters
|
||||||
|
#
|
||||||
|
# :eth
|
||||||
|
# A pre-generated EthHeader object.
|
||||||
|
# :ip
|
||||||
|
# A pre-generated IPHeader object.
|
||||||
|
# :udp
|
||||||
|
# A pre-generated UDPHeader object.
|
||||||
|
# :flavor
|
||||||
|
# TODO: HSRP packets don't tend have any flavor.
|
||||||
|
# :config
|
||||||
|
# A hash of return address details, often the output of Utils.whoami?
|
||||||
|
class HSRPPacket < Packet
|
||||||
|
|
||||||
|
attr_accessor :eth_header, :ip_header, :udp_header, :hsrp_header
|
||||||
|
|
||||||
|
def initialize(args={})
|
||||||
|
@eth_header = EthHeader.new(args).read(args[:eth])
|
||||||
|
@ip_header = IPHeader.new(args).read(args[:ip])
|
||||||
|
@ip_header.ip_proto = 0x11
|
||||||
|
@udp_header = UDPHeader.new(args).read(args[:udp])
|
||||||
|
@hsrp_header = HSRPHeader.new(args).read(args[:hsrp])
|
||||||
|
@udp_header.body = @hsrp_header
|
||||||
|
@ip_header.body = @udp_header
|
||||||
|
@eth_header.body = @ip_header
|
||||||
|
|
||||||
|
@headers = [@eth_header, @ip_header, @udp_header, @hsrp_header]
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# Peek provides summary data on packet contents.
|
||||||
|
def peek(args={})
|
||||||
|
peek_data = ["H "]
|
||||||
|
peek_data << "%-5d" % self.to_s.size
|
||||||
|
peek_data << "%-21s" % "#{self.hsrp_vip}:#{self.hsrp_group}:#{self.hsrp_password}"
|
||||||
|
peek_data << "%23s" % "U:"
|
||||||
|
peek_data << "%-21s" % "#{self.ip_saddr}:#{self.udp_sport}"
|
||||||
|
peek_data << "->"
|
||||||
|
peek_data << "%21s" % "#{self.ip_daddr}:#{self.udp_dport}"
|
||||||
|
peek_data.join
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
|
@ -12,14 +12,51 @@ module PacketFu
|
||||||
str.force_encoding "binary" if str.respond_to? :force_encoding
|
str.force_encoding "binary" if str.respond_to? :force_encoding
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# parse_app makes a valiant attempt at picking out particular applications (beyond
|
||||||
|
# the transport layer). As of right now, this only accounts for HSRP. I don't really
|
||||||
|
# intend to get very far with this because I need a better way to parse packets anyway --
|
||||||
|
# each packet type (including application layers) should be responsible for their own
|
||||||
|
# parsing rules. But, let's assume that'll never happen, so continue with this folly.
|
||||||
|
#
|
||||||
|
# This is an optional step, since it can lead to misidentified applications, depending
|
||||||
|
# on the strategy used to pick out app layers. For example, we really shouldn't have
|
||||||
|
# a rule that says that HTTP must be port 80, since it can easily be on 3128 or any
|
||||||
|
# other arbitrary port. However, we can say with certainty that HSRP must be dst port
|
||||||
|
# 1985, because that's what the RFC dictates.
|
||||||
|
def self.parse_app(parsed_packet,packet)
|
||||||
|
if parsed_packet.is_udp?
|
||||||
|
# Figure out UDP protocols (DNS, DHCP, etc)
|
||||||
|
# All HSRP is dst 224.0.0.2:1985 with a TTL of 1, so sayeth RFC 2281.
|
||||||
|
if(
|
||||||
|
parsed_packet.ip_ttl == 1 and
|
||||||
|
parsed_packet.ip_dst == 0xe0000002 and
|
||||||
|
parsed_packet.udp_dst == 1985
|
||||||
|
)
|
||||||
|
return HSRPPacket.new.read(packet)
|
||||||
|
else
|
||||||
|
return parsed_packet
|
||||||
|
end
|
||||||
|
elsif parsed_packet.is_tcp?
|
||||||
|
# Figure out TCP protocols (HTTP, SSH, etc)
|
||||||
|
return parsed_packet
|
||||||
|
else
|
||||||
|
# I don't know any others.
|
||||||
|
return parsed_packet
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Parse() creates the correct packet type based on the data, and returns the apporpiate
|
# Parse() creates the correct packet type based on the data, and returns the apporpiate
|
||||||
# Packet subclass.
|
# Packet subclass.
|
||||||
#
|
#
|
||||||
# There is an assumption here that all incoming packets are either EthPacket
|
# There is an assumption here that all incoming packets are either EthPacket
|
||||||
# or InvalidPacket types.
|
# or InvalidPacket types.
|
||||||
#
|
#
|
||||||
|
# If application-layer parsing is /not/ desired, that should be indicated explicitly
|
||||||
|
# with an argument of :parse_app => false.
|
||||||
|
#
|
||||||
# New packet types should get an entry here.
|
# New packet types should get an entry here.
|
||||||
def self.parse(packet,args={})
|
def self.parse(packet,args={})
|
||||||
|
parse_app = true if(args[:parse_app].nil? or args[:parse_app])
|
||||||
force_binary(packet)
|
force_binary(packet)
|
||||||
if packet.size >= 14 # Min size for Ethernet. No check for max size, yet.
|
if packet.size >= 14 # Min size for Ethernet. No check for max size, yet.
|
||||||
case packet[12,2] # Check the Eth protocol field.
|
case packet[12,2] # Check the Eth protocol field.
|
||||||
|
@ -37,7 +74,7 @@ module PacketFu
|
||||||
when "\x01"; p = ICMPPacket.new # Returns an ICMPPacket.
|
when "\x01"; p = ICMPPacket.new # Returns an ICMPPacket.
|
||||||
else; p = IPPacket.new # Returns an IPPacket since we can't tell the transport layer.
|
else; p = IPPacket.new # Returns an IPPacket since we can't tell the transport layer.
|
||||||
end
|
end
|
||||||
else; p = IPPacket.new # Returns an EthPacket since we don't know any other IP version.
|
else; p = IPPacket.new # Returns an IPPacket of this crazy IP version.
|
||||||
end
|
end
|
||||||
when "\x08\x06" # It's arp
|
when "\x08\x06" # It's arp
|
||||||
if packet.size >= 28 # Min size for complete arp
|
if packet.size >= 28 # Min size for complete arp
|
||||||
|
@ -55,7 +92,8 @@ module PacketFu
|
||||||
p = InvalidPacket.new # Not the right size for Ethernet (jumbo frames are okay)
|
p = InvalidPacket.new # Not the right size for Ethernet (jumbo frames are okay)
|
||||||
end
|
end
|
||||||
parsed_packet = p.read(packet,args)
|
parsed_packet = p.read(packet,args)
|
||||||
return parsed_packet
|
app_parsed_packet = parse_app ? parse_app(parsed_packet,packet) : nil
|
||||||
|
return app_parsed_packet || parsed_packet
|
||||||
end
|
end
|
||||||
|
|
||||||
#method_missing() delegates protocol-specific field actions to the apporpraite
|
#method_missing() delegates protocol-specific field actions to the apporpraite
|
||||||
|
@ -77,6 +115,8 @@ module PacketFu
|
||||||
@icmp_header.send(sym,*args)
|
@icmp_header.send(sym,*args)
|
||||||
when /^udp_/
|
when /^udp_/
|
||||||
@udp_header.send(sym,*args)
|
@udp_header.send(sym,*args)
|
||||||
|
when /^hsrp_/
|
||||||
|
@hsrp_header.send(sym,*args)
|
||||||
when /^tcp_/
|
when /^tcp_/
|
||||||
@tcp_header.send(sym,*args)
|
@tcp_header.send(sym,*args)
|
||||||
when /^ipv6_/
|
when /^ipv6_/
|
||||||
|
@ -87,7 +127,7 @@ module PacketFu
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond_to?(sym, include_private = false)
|
def respond_to?(sym, include_private = false)
|
||||||
if sym.to_s =~ /^(invalid|eth|arp|ip|icmp|udp|tcp|ipv6)_/
|
if sym.to_s =~ /^(invalid|eth|arp|ip|icmp|udp|hsrp|tcp|ipv6)_/
|
||||||
self.instance_variable_get("@#{$1}_header").respond_to? sym
|
self.instance_variable_get("@#{$1}_header").respond_to? sym
|
||||||
else
|
else
|
||||||
super
|
super
|
||||||
|
@ -328,6 +368,9 @@ module PacketFu
|
||||||
|
|
||||||
# Hexify provides a neatly-formatted dump of binary data, familar to hex readers.
|
# Hexify provides a neatly-formatted dump of binary data, familar to hex readers.
|
||||||
def hexify(str)
|
def hexify(str)
|
||||||
|
if str.respond_to? :force_encoding
|
||||||
|
str.force_encoding("ASCII-8BIT")
|
||||||
|
end
|
||||||
hexascii_lines = str.to_s.unpack("H*")[0].scan(/.{1,32}/)
|
hexascii_lines = str.to_s.unpack("H*")[0].scan(/.{1,32}/)
|
||||||
chars = str.to_s.gsub(/[\x00-\x1f\x7f-\xff]/,'.')
|
chars = str.to_s.gsub(/[\x00-\x1f\x7f-\xff]/,'.')
|
||||||
chars_lines = chars.scan(/.{1,16}/)
|
chars_lines = chars.scan(/.{1,16}/)
|
||||||
|
@ -427,6 +470,8 @@ module PacketFu
|
||||||
def is_tcp? ; self.proto.include? "TCP"; end
|
def is_tcp? ; self.proto.include? "TCP"; end
|
||||||
# Returns true if this is an UDP packet. Else, false.
|
# Returns true if this is an UDP packet. Else, false.
|
||||||
def is_udp? ; self.proto.include? "UDP"; end
|
def is_udp? ; self.proto.include? "UDP"; end
|
||||||
|
# Returns true if this is an HSRP packet. Else, false.
|
||||||
|
def is_hsrp? ; self.proto.include? "HSRP"; end
|
||||||
# Returns true if this is an ARP packet. Else, false.
|
# Returns true if this is an ARP packet. Else, false.
|
||||||
def is_arp? ; self.proto.include? "ARP"; end
|
def is_arp? ; self.proto.include? "ARP"; end
|
||||||
# Returns true if this is an IPv6 packet. Else, false.
|
# Returns true if this is an IPv6 packet. Else, false.
|
||||||
|
|
Loading…
Reference in New Issue