packetfu 1.0.5.pre → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/100kpackets.rb +41 -0
- data/examples/ackscan.rb +0 -0
- data/examples/simple-sniffer.rb +40 -0
- data/lib/packetfu.rb +42 -0
- data/lib/packetfu/capture.rb +1 -1
- data/lib/packetfu/config.rb +8 -5
- data/lib/packetfu/packet.rb +9 -21
- data/lib/packetfu/pcap.rb +26 -24
- data/lib/packetfu/protos/arp.rb +5 -2
- data/lib/packetfu/protos/eth.rb +13 -1
- data/lib/packetfu/protos/icmp.rb +4 -3
- data/lib/packetfu/protos/ip.rb +20 -2
- data/lib/packetfu/protos/tcp.rb +2 -6
- data/lib/packetfu/structfu.rb +7 -3
- data/lib/packetfu/utils.rb +27 -15
- data/lib/packetfu/version.rb +1 -1
- data/test/all_tests.rb +1 -0
- data/test/ethpacket_spec.rb +74 -0
- data/test/icmp_test.pcap +0 -0
- data/test/ip_test.pcap +0 -0
- data/test/packet_spec.rb +2 -1
- data/test/packet_subclasses_spec.rb +2 -1
- data/test/packetfu_spec.rb +2 -1
- data/test/structfu_spec.rb +2 -1
- data/test/tcp_spec.rb +2 -1
- data/test/tcp_test.pcap +0 -0
- data/test/test_arp.rb +1 -1
- data/test/test_eth.rb +1 -1
- data/test/test_hsrp.rb +1 -1
- data/test/test_icmp.rb +1 -1
- data/test/test_inject.rb +2 -4
- data/test/test_invalid.rb +1 -1
- data/test/test_ip.rb +1 -1
- data/test/test_ip6.rb +1 -1
- data/test/test_octets.rb +1 -1
- data/test/test_packet.rb +0 -1
- data/test/test_pcap.rb +1 -2
- data/test/test_structfu.rb +1 -1
- data/test/test_tcp.rb +1 -1
- data/test/test_udp.rb +1 -1
- data/test/udp_test.pcap +0 -0
- data/test/vlan-pcapr.cap +0 -0
- metadata +11 -27
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Used mainly to test for memory leaks and to demo the preferred ways of
|
4
|
+
# reading and writing packets to and from pcap files.
|
5
|
+
require './examples' # For path setting slight-of-hand
|
6
|
+
require 'packetfu'
|
7
|
+
|
8
|
+
include PacketFu
|
9
|
+
puts "Generating packets... (#{Time.now.utc})"
|
10
|
+
|
11
|
+
File.unlink("/tmp/out.pcap") if File.exists? "/tmp/out.pcap"
|
12
|
+
start_time = Time.now.utc
|
13
|
+
count = 0
|
14
|
+
|
15
|
+
100.times do
|
16
|
+
@pcaps = []
|
17
|
+
1000.times do
|
18
|
+
u = UDPPacket.new
|
19
|
+
u.ip_src = [rand(2**32-1)].pack("N")
|
20
|
+
u.ip_dst = [rand(2**32-1)].pack("N")
|
21
|
+
u.recalc
|
22
|
+
@pcaps << u
|
23
|
+
end
|
24
|
+
pfile = PcapFile.new
|
25
|
+
res = pfile.array_to_file(:filename => "/tmp/out.pcap", :array => @pcaps, :append => true)
|
26
|
+
count += res.last
|
27
|
+
puts "Wrote #{count} packets in #{Time.now.utc - start_time} seconds"
|
28
|
+
end
|
29
|
+
|
30
|
+
read_bytes_start = Time.now.utc
|
31
|
+
puts "Reading packet bytes..."
|
32
|
+
packet_bytes = PcapFile.read_packet_bytes "/tmp/out.pcap"
|
33
|
+
puts "Read #{packet_bytes.size} packet byte blobs in #{Time.now.utc - read_bytes_start} seconds."
|
34
|
+
|
35
|
+
read_packets_start = Time.now.utc
|
36
|
+
puts "Reading packets..."
|
37
|
+
packet_bytes = PcapFile.read_packets "/tmp/out.pcap"
|
38
|
+
puts "Read #{packet_bytes.size} parsed packets in #{Time.now.utc - read_packets_start} seconds."
|
39
|
+
|
40
|
+
|
41
|
+
|
data/examples/ackscan.rb
CHANGED
File without changes
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'examples'
|
3
|
+
require 'packetfu'
|
4
|
+
|
5
|
+
puts "Simple sniffer for PacketFu #{PacketFu.version}"
|
6
|
+
include PacketFu
|
7
|
+
iface = ARGV[0] || "eth0"
|
8
|
+
|
9
|
+
def sniff(iface)
|
10
|
+
cap = Capture.new(:iface => iface, :start => true)
|
11
|
+
cap.stream.each do |p|
|
12
|
+
pkt = Packet.parse p
|
13
|
+
if pkt.is_ip?
|
14
|
+
next if pkt.ip_saddr == Utils.ifconfig[:ip_saddr]
|
15
|
+
packet_info = [pkt.ip_saddr, pkt.ip_daddr, pkt.size, pkt.proto.last]
|
16
|
+
puts "%-15s -> %-15s %-4d %s" % packet_info
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
sniff(iface)
|
22
|
+
|
23
|
+
=begin
|
24
|
+
Results look like this:
|
25
|
+
145.58.33.95 -> 192.168.11.70 1514 TCP
|
26
|
+
212.233.158.76 -> 192.168.11.70 110 UDP
|
27
|
+
88.174.164.147 -> 192.168.11.70 110 UDP
|
28
|
+
145.58.33.95 -> 192.168.11.70 1514 TCP
|
29
|
+
145.58.33.95 -> 192.168.11.70 1514 TCP
|
30
|
+
145.58.33.95 -> 192.168.11.70 1514 TCP
|
31
|
+
145.58.33.95 -> 192.168.11.70 1514 TCP
|
32
|
+
8.8.8.8 -> 192.168.11.70 143 UDP
|
33
|
+
41.237.73.186 -> 192.168.11.70 60 TCP
|
34
|
+
145.58.33.95 -> 192.168.11.70 1514 TCP
|
35
|
+
145.58.33.95 -> 192.168.11.70 1514 TCP
|
36
|
+
8.8.8.8 -> 192.168.11.70 143 UDP
|
37
|
+
8.8.8.8 -> 192.168.11.70 128 UDP
|
38
|
+
8.8.8.8 -> 192.168.11.70 187 UDP
|
39
|
+
24.45.247.232 -> 192.168.11.70 70 TCP
|
40
|
+
=end
|
data/lib/packetfu.rb
CHANGED
@@ -25,6 +25,11 @@ module PacketFu
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
# Deal with Ruby's encoding by ignoring it.
|
29
|
+
def self.force_binary(str)
|
30
|
+
str.force_encoding "binary" if str.respond_to? :force_encoding
|
31
|
+
end
|
32
|
+
|
28
33
|
# Sets the expected byte order for a pcap file. See PacketFu::Read.set_byte_order
|
29
34
|
@byte_order = :little
|
30
35
|
|
@@ -93,6 +98,43 @@ module PacketFu
|
|
93
98
|
@packet_classes.map {|p| p.to_s.split("::").last.to_s.downcase.gsub(/packet$/,"")}
|
94
99
|
end
|
95
100
|
|
101
|
+
# The current inspect style. One of :hex, :dissect, or :default
|
102
|
+
# Note that :default means Ruby's default, which is usually
|
103
|
+
# far too long to be useful.
|
104
|
+
def self.inspect_style
|
105
|
+
@inspect_style ||= :dissect
|
106
|
+
end
|
107
|
+
|
108
|
+
# Setter for PacketFu's @inspect_style
|
109
|
+
def self.inspect_style=(arg)
|
110
|
+
@inspect_style = case arg
|
111
|
+
when :hex, :pretty
|
112
|
+
:hex
|
113
|
+
when :dissect, :verbose
|
114
|
+
:dissect
|
115
|
+
when :default, :ugly
|
116
|
+
:default
|
117
|
+
else
|
118
|
+
:dissect
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Switches inspect styles in a round-robin fashion between
|
123
|
+
# :dissect, :default, and :hex
|
124
|
+
def toggle_inspect
|
125
|
+
case @inspect_style
|
126
|
+
when :hex, :pretty
|
127
|
+
@inspect_style = :dissect
|
128
|
+
when :dissect, :verbose
|
129
|
+
@inspect_style = :default
|
130
|
+
when :default, :ugly
|
131
|
+
@inspect_style = :hex
|
132
|
+
else
|
133
|
+
@inspect_style = :dissect
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
96
138
|
end
|
97
139
|
|
98
140
|
require File.join(cwd,"packetfu","version")
|
data/lib/packetfu/capture.rb
CHANGED
@@ -31,7 +31,7 @@ module PacketFu
|
|
31
31
|
def initialize(args={})
|
32
32
|
@array = [] # Where the packet array goes.
|
33
33
|
@stream = [] # Where the stream goes.
|
34
|
-
@iface = args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo"
|
34
|
+
@iface = (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo").to_s
|
35
35
|
@snaplen = args[:snaplen] || 0xffff
|
36
36
|
@promisc = args[:promisc] || false # Sensible for some Intel wifi cards
|
37
37
|
@timeout = args[:timeout] || 1
|
data/lib/packetfu/config.rb
CHANGED
@@ -36,17 +36,20 @@ module PacketFu
|
|
36
36
|
@iface = args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo"
|
37
37
|
end
|
38
38
|
@pcapfile = "/tmp/out.pcap"
|
39
|
-
args.each_pair { |k,v| self.instance_variable_set(("
|
39
|
+
args.each_pair { |k,v| self.instance_variable_set(("@#{k}"),v) }
|
40
40
|
end
|
41
41
|
|
42
42
|
# Returns all instance variables as a hash (including custom variables set at initialization).
|
43
43
|
def config(arg=nil)
|
44
|
-
if arg
|
44
|
+
if arg
|
45
|
+
arg.each_pair {|k,v| self.instance_variable_set(("@" + k.to_s).intern, v)}
|
46
|
+
else
|
45
47
|
config_hash = {}
|
46
|
-
self.instance_variables.each
|
48
|
+
self.instance_variables.each do |v|
|
49
|
+
key = v.to_s.gsub(/^@/,"").to_sym
|
50
|
+
config_hash[key] = self.instance_variable_get(v)
|
51
|
+
end
|
47
52
|
config_hash
|
48
|
-
else
|
49
|
-
arg.each_pair {|k,v| self.instance_variable_set(("@" + k.to_s).intern, v)}
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
data/lib/packetfu/packet.rb
CHANGED
@@ -102,7 +102,7 @@ module PacketFu
|
|
102
102
|
# Put the entire packet on the wire by creating a temporary PacketFu::Inject object.
|
103
103
|
# TODO: Do something with auto-checksumming?
|
104
104
|
def to_w(iface=nil)
|
105
|
-
iface = iface || self.iface || PacketFu::Config.new.config[:iface]
|
105
|
+
iface = (iface || self.iface || PacketFu::Config.new.config[:iface]).to_s
|
106
106
|
inj = PacketFu::Inject.new(:iface => iface)
|
107
107
|
inj.array = [@headers[0].to_s]
|
108
108
|
inj.inject
|
@@ -467,11 +467,7 @@ module PacketFu
|
|
467
467
|
if self.class.name =~ /(::|^)PacketFu::Packet$/
|
468
468
|
raise NoMethodError, "method `new' called for abstract class #{self.class.name}"
|
469
469
|
end
|
470
|
-
|
471
|
-
@inspect_style = args[:inspect_style]
|
472
|
-
else
|
473
|
-
@inspect_style = :dissect
|
474
|
-
end
|
470
|
+
@inspect_style = args[:inspect_style] || PacketFu.inspect_style || :dissect
|
475
471
|
if args[:config]
|
476
472
|
args[:config].each_pair do |k,v|
|
477
473
|
case k
|
@@ -484,6 +480,13 @@ module PacketFu
|
|
484
480
|
end
|
485
481
|
end
|
486
482
|
|
483
|
+
# Delegate to PacketFu's inspect_style, since the
|
484
|
+
# class variable name is the same. Yay for namespace
|
485
|
+
# pollution!
|
486
|
+
def inspect_style=()
|
487
|
+
PacketFu.inspect_style(arg)
|
488
|
+
end
|
489
|
+
|
487
490
|
#method_missing() delegates protocol-specific field actions to the apporpraite
|
488
491
|
#class variable (which contains the associated packet type)
|
489
492
|
#This register-of-protocols style switch will work for the
|
@@ -525,21 +528,6 @@ module PacketFu
|
|
525
528
|
end
|
526
529
|
|
527
530
|
end # class Packet
|
528
|
-
|
529
|
-
# Switches inspect styles between :dissect, :default, and :hex
|
530
|
-
def toggle_inspect
|
531
|
-
case @inspect_style
|
532
|
-
when :hex, :pretty
|
533
|
-
@inspect_style = :dissect
|
534
|
-
when :dissect, :verbose
|
535
|
-
@inspect_style = :default
|
536
|
-
when :default, :ugly
|
537
|
-
@inspect_style = :hex
|
538
|
-
else
|
539
|
-
@inspect_style = :dissect
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
531
|
end
|
544
532
|
|
545
533
|
# vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
|
data/lib/packetfu/pcap.rb
CHANGED
@@ -246,34 +246,32 @@ module PacketFu
|
|
246
246
|
|
247
247
|
# Takes a given file name, and reads out the packets. If given a block,
|
248
248
|
# it will yield back a PcapPacket object per packet found.
|
249
|
-
def read(fname,&block)
|
249
|
+
def read(fname,&block)
|
250
|
+
file_header = PcapHeader.new
|
251
|
+
pcap_packets = PcapPackets.new
|
252
|
+
unless File.readable? fname
|
253
|
+
raise ArgumentError, "Cannot read file `#{fname}'"
|
254
|
+
end
|
250
255
|
begin
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
packet_count
|
261
|
-
|
262
|
-
warn "Packet ##{packet_count} is corrupted: expected #{len.to_i}, got #{pcap_packet.data.size}. Exiting."
|
263
|
-
break
|
264
|
-
end
|
265
|
-
if block
|
266
|
-
yield pcap_packet
|
267
|
-
else
|
268
|
-
pcap_packets << pcap_packet
|
269
|
-
end
|
270
|
-
end
|
271
|
-
unless block
|
272
|
-
return pcap_packets
|
256
|
+
file_handle = File.open(fname, "rb")
|
257
|
+
file_header.read file_handle.read(24)
|
258
|
+
packet_count = 0
|
259
|
+
pcap_packet = PcapPacket.new(:endian => file_header.endian)
|
260
|
+
while pcap_packet.read file_handle.read(16) do
|
261
|
+
len = pcap_packet.incl_len
|
262
|
+
pcap_packet.data = StructFu::String.new.read(file_handle.read(len.to_i))
|
263
|
+
packet_count += 1
|
264
|
+
if pcap_packet.data.size < len.to_i
|
265
|
+
warn "Packet ##{packet_count} is corrupted: expected #{len.to_i}, got #{pcap_packet.data.size}. Exiting."
|
266
|
+
break
|
273
267
|
end
|
268
|
+
pcap_packets << pcap_packet.clone
|
269
|
+
yield pcap_packets.last if block
|
270
|
+
end
|
274
271
|
ensure
|
275
272
|
file_handle.close
|
276
273
|
end
|
274
|
+
block ? packet_count : pcap_packets
|
277
275
|
end
|
278
276
|
|
279
277
|
# Takes a filename, and an optional block. If a block is given,
|
@@ -453,7 +451,11 @@ module PacketFu
|
|
453
451
|
end
|
454
452
|
append = args[:append]
|
455
453
|
if append
|
456
|
-
File.
|
454
|
+
if File.exists? filename
|
455
|
+
File.open(filename,'ab') {|file| file.write(self.body.to_s)}
|
456
|
+
else
|
457
|
+
File.open(filename,'wb') {|file| file.write(self.to_s)}
|
458
|
+
end
|
457
459
|
else
|
458
460
|
File.open(filename,'wb') {|file| file.write(self.to_s)}
|
459
461
|
end
|
data/lib/packetfu/protos/arp.rb
CHANGED
@@ -26,14 +26,17 @@ module PacketFu
|
|
26
26
|
include StructFu
|
27
27
|
|
28
28
|
def initialize(args={})
|
29
|
+
src_mac = args[:arp_src_mac] || (args[:config][:eth_src] if args[:config])
|
30
|
+
src_ip_bin = args[:arp_src_ip] || (args[:config][:ip_src_bin] if args[:config])
|
31
|
+
|
29
32
|
super(
|
30
33
|
Int16.new(args[:arp_hw] || 1),
|
31
34
|
Int16.new(args[:arp_proto] ||0x0800),
|
32
35
|
Int8.new(args[:arp_hw_len] || 6),
|
33
36
|
Int8.new(args[:arp_proto_len] || 4),
|
34
37
|
Int16.new(args[:arp_opcode] || 1),
|
35
|
-
EthMac.new.read(
|
36
|
-
Octets.new.read(
|
38
|
+
EthMac.new.read(src_mac),
|
39
|
+
Octets.new.read(src_ip_bin),
|
37
40
|
EthMac.new.read(args[:arp_dst_mac]),
|
38
41
|
Octets.new.read(args[:arp_dst_ip]),
|
39
42
|
StructFu::String.new.read(args[:body])
|
data/lib/packetfu/protos/eth.rb
CHANGED
@@ -132,6 +132,12 @@ module PacketFu
|
|
132
132
|
# For more on the construction on MAC addresses, see
|
133
133
|
# http://en.wikipedia.org/wiki/MAC_address
|
134
134
|
#
|
135
|
+
# TODO: Need to come up with a good way of dealing with vlan
|
136
|
+
# tagging. Having a usually empty struct member seems weird,
|
137
|
+
# but there may not be another way to do it if I want to preserve
|
138
|
+
# the Eth-ness of vlan-tagged 802.1Q packets. Also, may as well
|
139
|
+
# deal with 0x88a8 as well (http://en.wikipedia.org/wiki/802.1ad)
|
140
|
+
#
|
135
141
|
# ==== Header Definition
|
136
142
|
#
|
137
143
|
# EthMac :eth_dst # See EthMac
|
@@ -251,11 +257,17 @@ module PacketFu
|
|
251
257
|
# eth_pkt.eth_daddr="00:1c:24:aa:bb:cc"
|
252
258
|
#
|
253
259
|
# eth_pkt.to_w('eth0') # Inject on the wire. (require root)
|
260
|
+
#
|
254
261
|
class EthPacket < Packet
|
255
262
|
attr_accessor :eth_header
|
256
263
|
|
257
264
|
def self.can_parse?(str)
|
258
|
-
|
265
|
+
# XXX Temporary fix. Need to extend the EthHeader class to handle more.
|
266
|
+
valid_eth_types = [0x0800, 0x0806, 0x86dd]
|
267
|
+
return false unless str.size >= 14
|
268
|
+
type = str[12,2].unpack("n").first rescue nil
|
269
|
+
return false unless valid_eth_types.include? type
|
270
|
+
true
|
259
271
|
end
|
260
272
|
|
261
273
|
def read(str=nil,args={})
|
data/lib/packetfu/protos/icmp.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module PacketFu
|
2
2
|
|
3
|
-
# ICMPHeader is a complete ICMP struct, used in ICMPPacket. ICMP is
|
4
|
-
# administration and connectivity testing.
|
3
|
+
# ICMPHeader is a complete ICMP struct, used in ICMPPacket. ICMP is
|
4
|
+
# typically used for network administration and connectivity testing.
|
5
5
|
#
|
6
|
-
# For more on ICMP packets, see
|
6
|
+
# For more on ICMP packets, see
|
7
|
+
# http://www.networksorcery.com/enp/protocol/icmp.htm
|
7
8
|
#
|
8
9
|
# ==== Header Definition
|
9
10
|
#
|
data/lib/packetfu/protos/ip.rb
CHANGED
@@ -162,11 +162,29 @@ module PacketFu
|
|
162
162
|
# Getter for the checksum.
|
163
163
|
def ip_sum; self[:ip_sum].to_i; end
|
164
164
|
# Setter for the source IP address.
|
165
|
-
def ip_src=(i)
|
165
|
+
def ip_src=(i)
|
166
|
+
case i
|
167
|
+
when Numeric
|
168
|
+
self[:ip_src] = Octets.new.read([i].pack("N"))
|
169
|
+
when Octets
|
170
|
+
self[:ip_src] = i
|
171
|
+
else
|
172
|
+
typecast i
|
173
|
+
end
|
174
|
+
end
|
166
175
|
# Getter for the source IP address.
|
167
176
|
def ip_src; self[:ip_src].to_i; end
|
168
177
|
# Setter for the destination IP address.
|
169
|
-
def ip_dst=(i)
|
178
|
+
def ip_dst=(i)
|
179
|
+
case i
|
180
|
+
when Numeric
|
181
|
+
self[:ip_dst] = Octets.new.read([i].pack("N"))
|
182
|
+
when Octets
|
183
|
+
self[:ip_dst] = i
|
184
|
+
else
|
185
|
+
typecast i
|
186
|
+
end
|
187
|
+
end
|
170
188
|
# Getter for the destination IP address.
|
171
189
|
def ip_dst; self[:ip_dst].to_i; end
|
172
190
|
|
data/lib/packetfu/protos/tcp.rb
CHANGED
@@ -546,14 +546,10 @@ module PacketFu
|
|
546
546
|
opts
|
547
547
|
end
|
548
548
|
|
549
|
-
def force_binary(str)
|
550
|
-
str.force_encoding "binary" if str.respond_to? :force_encoding
|
551
|
-
end
|
552
|
-
|
553
549
|
# Reads a string to populate the object.
|
554
550
|
def read(str)
|
555
|
-
self.clear
|
556
|
-
force_binary(str)
|
551
|
+
self.clear
|
552
|
+
PacketFu.force_binary(str)
|
557
553
|
return self if(!str.respond_to? :to_s || str.nil?)
|
558
554
|
i = 0
|
559
555
|
while i < str.to_s.size
|
data/lib/packetfu/structfu.rb
CHANGED
@@ -33,9 +33,13 @@ module StructFu
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
# Handle deep copies correctly
|
36
|
+
# Handle deep copies correctly. Marshal in 1.9, re-read myself on 1.8
|
37
37
|
def clone
|
38
|
-
|
38
|
+
begin
|
39
|
+
Marshal.load(Marshal.dump(self))
|
40
|
+
rescue
|
41
|
+
self.class.new.read(self.to_s)
|
42
|
+
end
|
39
43
|
end
|
40
44
|
|
41
45
|
# Ints all have a value, an endianness, and a default value.
|
@@ -282,7 +286,7 @@ class Struct
|
|
282
286
|
# Monkeypatch for Struct to include some string safety -- anything that uses
|
283
287
|
# Struct is going to presume binary strings anyway.
|
284
288
|
def force_binary(str)
|
285
|
-
|
289
|
+
PacketFu.force_binary(str)
|
286
290
|
end
|
287
291
|
|
288
292
|
end
|
data/lib/packetfu/utils.rb
CHANGED
@@ -27,13 +27,12 @@ module PacketFu
|
|
27
27
|
# It goes without saying, spewing forged ARP packets on your network is a great way to really
|
28
28
|
# irritate your co-workers.
|
29
29
|
def self.arp(target_ip,args={})
|
30
|
-
|
31
|
-
|
30
|
+
iface = args[:iface] || :eth0
|
31
|
+
args[:config] ||= whoami?(:iface => iface)
|
32
|
+
arp_pkt = PacketFu::ARPPacket.new(:flavor => (args[:flavor] || :none), :config => args[:config])
|
32
33
|
arp_pkt.eth_daddr = "ff:ff:ff:ff:ff:ff"
|
33
34
|
arp_pkt.arp_daddr_mac = "00:00:00:00:00:00"
|
34
|
-
arp_pkt.
|
35
|
-
arp_pkt.arp_daddr_ip = target_ip
|
36
|
-
iface = (args[:iface] || ($packetfu_default.iface if $packetfu_default) || "eth0")
|
35
|
+
arp_pkt.arp_daddr_ip = target_ip
|
37
36
|
# Stick the Capture object in its own thread.
|
38
37
|
cap_thread = Thread.new do
|
39
38
|
target_mac = nil
|
@@ -59,9 +58,15 @@ module PacketFu
|
|
59
58
|
# operation; a UDP packet is generated and dropped on to the default (or named)
|
60
59
|
# interface, and then captured (which means you need to be root to do this).
|
61
60
|
#
|
62
|
-
# whoami? returns a hash of :eth_saddr, :eth_src, :ip_saddr, :ip_src,
|
63
|
-
# :eth_dst, and :eth_daddr (the last two are usually suitable
|
64
|
-
# gateway mac address). It's most useful as an argument to
|
61
|
+
# whoami? returns a hash of :eth_saddr, :eth_src, :ip_saddr, :ip_src,
|
62
|
+
# :ip_src_bin, :eth_dst, and :eth_daddr (the last two are usually suitable
|
63
|
+
# for a gateway mac address). It's most useful as an argument to
|
64
|
+
# PacketFu::Config.new, or as an argument to the many Packet constructors.
|
65
|
+
#
|
66
|
+
# Note that if you have multiple interfaces with the same route (such as when
|
67
|
+
# wlan0 and eth0 are associated to the same network), the "first" one
|
68
|
+
# according to Pcap.lookupdev will be used, regardless of which :iface you
|
69
|
+
# pick.
|
65
70
|
#
|
66
71
|
# === Parameters
|
67
72
|
# :iface => "eth0"
|
@@ -72,7 +77,10 @@ module PacketFu
|
|
72
77
|
# Since this network is IANA reserved (for now), this network should be handled by your default gateway
|
73
78
|
# and default interface.
|
74
79
|
def self.whoami?(args={})
|
75
|
-
|
80
|
+
unless args.kind_of? Hash
|
81
|
+
raise ArgumentError, "Argument to `whoami?' must be a Hash"
|
82
|
+
end
|
83
|
+
if args[:iface].to_s =~ /^lo/ # Linux loopback more or less. Need a switch for windows loopback, too.
|
76
84
|
dst_host = "127.0.0.1"
|
77
85
|
else
|
78
86
|
dst_host = (args[:target] || IPAddr.new((rand(16777216) + 2969567232), Socket::AF_INET).to_s)
|
@@ -80,8 +88,11 @@ module PacketFu
|
|
80
88
|
|
81
89
|
dst_port = rand(0xffff-1024)+1024
|
82
90
|
msg = "PacketFu whoami? packet #{(Time.now.to_i + rand(0xffffff)+1)}"
|
83
|
-
|
84
|
-
|
91
|
+
iface = (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || :lo ).to_s
|
92
|
+
cap = PacketFu::Capture.new(:iface => iface, :promisc => false, :start => true, :filter => "udp and dst host #{dst_host} and dst port #{dst_port}")
|
93
|
+
udp_sock = UDPSocket.new
|
94
|
+
udp_sock.send(msg,0,dst_host,dst_port)
|
95
|
+
udp_sock = nil
|
85
96
|
cap.save
|
86
97
|
pkt = Packet.parse(cap.array[0]) unless cap.save.zero?
|
87
98
|
timeout = 0
|
@@ -90,12 +101,13 @@ module PacketFu
|
|
90
101
|
timeout = 1.1 # Cancel the timeout
|
91
102
|
if pkt.payload == msg
|
92
103
|
my_data = {
|
93
|
-
:iface => args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo",
|
104
|
+
:iface => (args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo").to_s,
|
94
105
|
:pcapfile => args[:pcapfile] || "/tmp/out.pcap",
|
95
106
|
:eth_saddr => pkt.eth_saddr,
|
96
107
|
:eth_src => pkt.eth_src.to_s,
|
97
108
|
:ip_saddr => pkt.ip_saddr,
|
98
|
-
:ip_src => pkt.ip_src
|
109
|
+
:ip_src => pkt.ip_src,
|
110
|
+
:ip_src_bin => [pkt.ip_src].pack("N"),
|
99
111
|
:eth_dst => pkt.eth_dst.to_s,
|
100
112
|
:eth_daddr => pkt.eth_daddr
|
101
113
|
}
|
@@ -107,7 +119,7 @@ module PacketFu
|
|
107
119
|
cap.save
|
108
120
|
pkt = Packet.parse(cap.array[0]) unless cap.save.zero?
|
109
121
|
end
|
110
|
-
raise SocketError, "Didn't receive the whomi() packet." if !pkt
|
122
|
+
raise SocketError, "Didn't receive the whomi() packet, can't automatically configure." if !pkt
|
111
123
|
cap = nil
|
112
124
|
end
|
113
125
|
my_data
|
@@ -161,7 +173,7 @@ module PacketFu
|
|
161
173
|
end
|
162
174
|
ifconfig_data.each do |s|
|
163
175
|
case s
|
164
|
-
when /inet addr:[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9])(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]))?/i
|
176
|
+
when /inet addr:[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
|
165
177
|
ret[:ip_saddr] = $1
|
166
178
|
ret[:ip_src] = [IPAddr.new($1).to_i].pack("N")
|
167
179
|
ret[:ip4_obj] = IPAddr.new($1)
|
data/lib/packetfu/version.rb
CHANGED
data/test/all_tests.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
$:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
|
2
|
+
require 'packetfu'
|
3
|
+
|
4
|
+
include PacketFu
|
5
|
+
|
6
|
+
describe EthPacket, "when read from a pcap file" do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
parsed_packets = PcapFile.read_packets(File.join(".","sample.pcap"))
|
10
|
+
@eth_packet = parsed_packets.first
|
11
|
+
end
|
12
|
+
|
13
|
+
context "is a regular ethernet packet" do
|
14
|
+
|
15
|
+
subject { @eth_packet }
|
16
|
+
|
17
|
+
it "should be an EthPacket kind of packet" do
|
18
|
+
subject.should be_kind_of EthPacket
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have a dest mac address" do
|
22
|
+
subject.eth_daddr.should == "00:03:2f:1a:74:de"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have a source mac address" do
|
26
|
+
subject.eth_saddr.should == "00:1b:11:51:b7:ce"
|
27
|
+
end
|
28
|
+
|
29
|
+
its(:size) { should == 78 }
|
30
|
+
|
31
|
+
it "should have a payload in its first header" do
|
32
|
+
subject.headers.first.body.should_not be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
context "an EthPacket's first header" do
|
36
|
+
|
37
|
+
subject { @eth_packet.headers.first }
|
38
|
+
|
39
|
+
it "should be 64 bytes" do
|
40
|
+
subject.body.sz.should == 64
|
41
|
+
end
|
42
|
+
|
43
|
+
context "EthHeader struct members" do
|
44
|
+
if RUBY_VERSION =~ /^1\.8/
|
45
|
+
its(:members) { should include :eth_dst.to_s }
|
46
|
+
its(:members) { should include :eth_src.to_s }
|
47
|
+
its(:members) { should include :eth_proto.to_s }
|
48
|
+
its(:members) { should include :body.to_s }
|
49
|
+
else
|
50
|
+
its(:members) { should include :eth_dst }
|
51
|
+
its(:members) { should include :eth_src }
|
52
|
+
its(:members) { should include :eth_proto }
|
53
|
+
its(:members) { should include :body }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
context "isn't a regular Ethernet packet" do
|
62
|
+
|
63
|
+
subject {
|
64
|
+
parsed_packets = PcapFile.read_packets(File.join(".","vlan-pcapr.cap"))
|
65
|
+
parsed_packets.first
|
66
|
+
}
|
67
|
+
|
68
|
+
it "should not be an EthPacket" do
|
69
|
+
subject.should_not be_kind_of EthPacket
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/test/icmp_test.pcap
CHANGED
Binary file
|
data/test/ip_test.pcap
CHANGED
Binary file
|
data/test/packet_spec.rb
CHANGED
data/test/packetfu_spec.rb
CHANGED
data/test/structfu_spec.rb
CHANGED
data/test/tcp_spec.rb
CHANGED
data/test/tcp_test.pcap
CHANGED
Binary file
|
data/test/test_arp.rb
CHANGED
data/test/test_eth.rb
CHANGED
data/test/test_hsrp.rb
CHANGED
data/test/test_icmp.rb
CHANGED
data/test/test_inject.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib/")
|
3
|
-
|
4
2
|
require 'test/unit'
|
5
|
-
|
6
|
-
# Needed if you're using the gem version of pcaprub. Obviated in 1.9.
|
3
|
+
$:.unshift File.join(File.expand_path(File.dirname(__FILE__)), "..", "lib")
|
7
4
|
require 'packetfu'
|
8
5
|
|
6
|
+
|
9
7
|
class InjectTest < Test::Unit::TestCase
|
10
8
|
|
11
9
|
def test_cap
|
data/test/test_invalid.rb
CHANGED
data/test/test_ip.rb
CHANGED
data/test/test_ip6.rb
CHANGED
data/test/test_octets.rb
CHANGED
data/test/test_packet.rb
CHANGED
data/test/test_pcap.rb
CHANGED
data/test/test_structfu.rb
CHANGED
data/test/test_tcp.rb
CHANGED
data/test/test_udp.rb
CHANGED
data/test/udp_test.pcap
CHANGED
Binary file
|
data/test/vlan-pcapr.cap
ADDED
Binary file
|
metadata
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packetfu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 5
|
10
|
-
- pre
|
11
|
-
version: 1.0.5.pre
|
4
|
+
prerelease:
|
5
|
+
version: 1.1.0
|
12
6
|
platform: ruby
|
13
7
|
authors:
|
14
8
|
- Tod Beardsley
|
@@ -16,7 +10,7 @@ autorequire:
|
|
16
10
|
bindir: bin
|
17
11
|
cert_chain: []
|
18
12
|
|
19
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-11 00:00:00 -05:00
|
20
14
|
default_executable:
|
21
15
|
dependencies:
|
22
16
|
- !ruby/object:Gem::Dependency
|
@@ -27,11 +21,6 @@ dependencies:
|
|
27
21
|
requirements:
|
28
22
|
- - ">="
|
29
23
|
- !ruby/object:Gem::Version
|
30
|
-
hash: 63
|
31
|
-
segments:
|
32
|
-
- 0
|
33
|
-
- 9
|
34
|
-
- 2
|
35
24
|
version: 0.9.2
|
36
25
|
type: :development
|
37
26
|
version_requirements: *id001
|
@@ -43,7 +32,6 @@ extensions: []
|
|
43
32
|
|
44
33
|
extra_rdoc_files:
|
45
34
|
- README
|
46
|
-
- INSTALL
|
47
35
|
- .document
|
48
36
|
files:
|
49
37
|
- lib/packetfu.rb
|
@@ -89,7 +77,9 @@ files:
|
|
89
77
|
- test/arp_test.pcap
|
90
78
|
- test/test_inject.rb
|
91
79
|
- test/test_eth.rb
|
80
|
+
- test/ethpacket_spec.rb
|
92
81
|
- test/packet_spec.rb
|
82
|
+
- test/vlan-pcapr.cap
|
93
83
|
- test/sample-ipv6.pcap
|
94
84
|
- test/test_hsrp.rb
|
95
85
|
- test/test_structfu.rb
|
@@ -105,6 +95,7 @@ files:
|
|
105
95
|
- examples/examples.rb
|
106
96
|
- examples/simple-stats.rb
|
107
97
|
- examples/arphood.rb
|
98
|
+
- examples/simple-sniffer.rb
|
108
99
|
- examples/ethernet.rb
|
109
100
|
- examples/arp.rb
|
110
101
|
- examples/slammer.rb
|
@@ -113,8 +104,9 @@ files:
|
|
113
104
|
- examples/ackscan.rb
|
114
105
|
- examples/ids.rb
|
115
106
|
- examples/new-simple-stats.rb
|
107
|
+
- examples/100kpackets.rb
|
116
108
|
has_rdoc: true
|
117
|
-
homepage:
|
109
|
+
homepage: https://github.com/todb/packetfu
|
118
110
|
licenses:
|
119
111
|
- BSD
|
120
112
|
post_install_message:
|
@@ -127,27 +119,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
127
119
|
requirements:
|
128
120
|
- - ">="
|
129
121
|
- !ruby/object:Gem::Version
|
130
|
-
hash: 3
|
131
|
-
segments:
|
132
|
-
- 0
|
133
122
|
version: "0"
|
134
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
124
|
none: false
|
136
125
|
requirements:
|
137
|
-
- - "
|
126
|
+
- - ">="
|
138
127
|
- !ruby/object:Gem::Version
|
139
|
-
|
140
|
-
segments:
|
141
|
-
- 1
|
142
|
-
- 3
|
143
|
-
- 1
|
144
|
-
version: 1.3.1
|
128
|
+
version: "0"
|
145
129
|
requirements:
|
146
130
|
- sdoc, for generating local documentation
|
147
131
|
- rspec, v2.6.2 or later, for testing
|
148
132
|
- pcaprub v0.9.2 or later, for packet capture/inject
|
149
133
|
rubyforge_project: packetfu
|
150
|
-
rubygems_version: 1.
|
134
|
+
rubygems_version: 1.6.2
|
151
135
|
signing_key:
|
152
136
|
specification_version: 3
|
153
137
|
summary: PacketFu is a mid-level packet manipulation library.
|