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

View File

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

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