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
|
||||
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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue