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-b9f4589650da
unstable
Tod Beardsley 2011-05-19 13:00:35 +00:00
parent 76ebc59f29
commit c95080d83d
4 changed files with 229 additions and 9 deletions

View File

@ -36,11 +36,6 @@ module PacketFu
end
require "packetfu/capture"
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
@ -53,6 +48,7 @@ require "packetfu/ip"
require "packetfu/arp"
require "packetfu/icmp"
require "packetfu/udp"
require "packetfu/hsrp" # Depends on UDP
require "packetfu/tcp"
require "packetfu/ipv6" # This is pretty minimal.
require "packetfu/utils"
@ -60,7 +56,9 @@ require "packetfu/config"
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
# in a while, when I remember

View File

@ -10,8 +10,11 @@ PacketFu is rdoc-compatable. In the same directory as this file, run "rdoc" by i
== Requirements
PcapRub:
$ svn co http://www.metasploit.com/svn/framework3/trunk/external/pcaprub
or
$ 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.

174
lib/packetfu/hsrp.rb Normal file
View File

@ -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

View File

@ -12,14 +12,51 @@ module PacketFu
str.force_encoding "binary" if str.respond_to? :force_encoding
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
# Packet subclass.
#
# There is an assumption here that all incoming packets are either EthPacket
# 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.
def self.parse(packet,args={})
parse_app = true if(args[:parse_app].nil? or args[:parse_app])
force_binary(packet)
if packet.size >= 14 # Min size for Ethernet. No check for max size, yet.
case packet[12,2] # Check the Eth protocol field.
@ -37,7 +74,7 @@ module PacketFu
when "\x01"; p = ICMPPacket.new # Returns an ICMPPacket.
else; p = IPPacket.new # Returns an IPPacket since we can't tell the transport layer.
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
when "\x08\x06" # It's 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)
end
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
#method_missing() delegates protocol-specific field actions to the apporpraite
@ -77,6 +115,8 @@ module PacketFu
@icmp_header.send(sym,*args)
when /^udp_/
@udp_header.send(sym,*args)
when /^hsrp_/
@hsrp_header.send(sym,*args)
when /^tcp_/
@tcp_header.send(sym,*args)
when /^ipv6_/
@ -87,7 +127,7 @@ module PacketFu
end
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
else
super
@ -328,6 +368,9 @@ module PacketFu
# Hexify provides a neatly-formatted dump of binary data, familar to hex readers.
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}/)
chars = str.to_s.gsub(/[\x00-\x1f\x7f-\xff]/,'.')
chars_lines = chars.scan(/.{1,16}/)
@ -427,6 +470,8 @@ module PacketFu
def is_tcp? ; self.proto.include? "TCP"; end
# Returns true if this is an UDP packet. Else, false.
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.
def is_arp? ; self.proto.include? "ARP"; end
# Returns true if this is an IPv6 packet. Else, false.