packetfu 1.1.9 → 1.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/bench/octets.rb +9 -9
- data/examples/100kpackets.rb +12 -12
- data/examples/ackscan.rb +16 -16
- data/examples/arp.rb +35 -35
- data/examples/arphood.rb +36 -36
- data/examples/dissect_thinger.rb +6 -6
- data/examples/new-simple-stats.rb +23 -23
- data/examples/packetfu-shell.rb +25 -25
- data/examples/simple-sniffer.rb +9 -9
- data/examples/simple-stats.rb +23 -23
- data/examples/slammer.rb +3 -3
- data/lib/packetfu.rb +127 -127
- data/lib/packetfu/capture.rb +169 -169
- data/lib/packetfu/config.rb +52 -52
- data/lib/packetfu/inject.rb +56 -56
- data/lib/packetfu/packet.rb +528 -528
- data/lib/packetfu/pcap.rb +579 -579
- data/lib/packetfu/protos/arp.rb +90 -90
- data/lib/packetfu/protos/arp/header.rb +158 -158
- data/lib/packetfu/protos/arp/mixin.rb +36 -36
- data/lib/packetfu/protos/eth.rb +44 -44
- data/lib/packetfu/protos/eth/header.rb +243 -243
- data/lib/packetfu/protos/eth/mixin.rb +3 -3
- data/lib/packetfu/protos/hsrp.rb +69 -69
- data/lib/packetfu/protos/hsrp/header.rb +107 -107
- data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
- data/lib/packetfu/protos/icmp.rb +71 -71
- data/lib/packetfu/protos/icmp/header.rb +82 -82
- data/lib/packetfu/protos/icmp/mixin.rb +14 -14
- data/lib/packetfu/protos/invalid.rb +49 -49
- data/lib/packetfu/protos/ip.rb +69 -69
- data/lib/packetfu/protos/ip/header.rb +291 -291
- data/lib/packetfu/protos/ip/mixin.rb +40 -40
- data/lib/packetfu/protos/ipv6.rb +50 -50
- data/lib/packetfu/protos/ipv6/header.rb +188 -188
- data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
- data/lib/packetfu/protos/tcp.rb +176 -176
- data/lib/packetfu/protos/tcp/ecn.rb +35 -35
- data/lib/packetfu/protos/tcp/flags.rb +74 -74
- data/lib/packetfu/protos/tcp/header.rb +268 -268
- data/lib/packetfu/protos/tcp/hlen.rb +32 -32
- data/lib/packetfu/protos/tcp/mixin.rb +46 -46
- data/lib/packetfu/protos/tcp/option.rb +321 -321
- data/lib/packetfu/protos/tcp/options.rb +95 -95
- data/lib/packetfu/protos/tcp/reserved.rb +35 -35
- data/lib/packetfu/protos/udp.rb +116 -116
- data/lib/packetfu/protos/udp/header.rb +91 -91
- data/lib/packetfu/protos/udp/mixin.rb +3 -3
- data/lib/packetfu/structfu.rb +280 -280
- data/lib/packetfu/utils.rb +226 -217
- data/lib/packetfu/version.rb +41 -41
- data/packetfu.gemspec +2 -1
- data/spec/ethpacket_spec.rb +48 -48
- data/spec/packet_spec.rb +57 -57
- data/spec/packet_subclasses_spec.rb +8 -8
- data/spec/packetfu_spec.rb +59 -59
- data/spec/structfu_spec.rb +268 -268
- data/spec/tcp_spec.rb +75 -75
- data/test/all_tests.rb +13 -13
- data/test/func_lldp.rb +3 -3
- data/test/ptest.rb +2 -2
- data/test/test_arp.rb +116 -116
- data/test/test_capture.rb +45 -45
- data/test/test_eth.rb +68 -68
- data/test/test_hsrp.rb +9 -9
- data/test/test_icmp.rb +52 -52
- data/test/test_inject.rb +18 -18
- data/test/test_invalid.rb +16 -16
- data/test/test_ip.rb +36 -36
- data/test/test_ip6.rb +48 -48
- data/test/test_octets.rb +21 -21
- data/test/test_packet.rb +154 -154
- data/test/test_pcap.rb +170 -170
- data/test/test_structfu.rb +97 -97
- data/test/test_tcp.rb +320 -320
- data/test/test_udp.rb +76 -76
- metadata +4 -3
@@ -3,105 +3,105 @@ require 'packetfu/protos/tcp/option'
|
|
3
3
|
|
4
4
|
module PacketFu
|
5
5
|
|
6
|
-
|
6
|
+
class TcpOptions < Array
|
7
7
|
|
8
|
-
|
8
|
+
include StructFu
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
10
|
+
# If args[:pad] is set, the options line is automatically padded out
|
11
|
+
# with NOPs.
|
12
|
+
def to_s(args={})
|
13
|
+
opts = self.map {|x| x.to_s}.join
|
14
|
+
if args[:pad]
|
15
|
+
unless (opts.size % 4).zero?
|
16
|
+
(4 - (opts.size % 4)).times { opts << "\x01" }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
opts
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
22
|
+
# Reads a string to populate the object.
|
23
|
+
def read(str)
|
24
|
+
self.clear
|
25
|
+
PacketFu.force_binary(str)
|
26
|
+
return self if(!str.respond_to? :to_s || str.nil?)
|
27
|
+
i = 0
|
28
|
+
while i < str.to_s.size
|
29
|
+
this_opt = case str[i,1].unpack("C").first
|
30
|
+
when 0; ::PacketFu::TcpOption::EOL.new
|
31
|
+
when 1; ::PacketFu::TcpOption::NOP.new
|
32
|
+
when 2; ::PacketFu::TcpOption::MSS.new
|
33
|
+
when 3; ::PacketFu::TcpOption::WS.new
|
34
|
+
when 4; ::PacketFu::TcpOption::SACKOK.new
|
35
|
+
when 5; ::PacketFu::TcpOption::SACK.new
|
36
|
+
when 6; ::PacketFu::TcpOption::ECHO.new
|
37
|
+
when 7; ::PacketFu::TcpOption::ECHOREPLY.new
|
38
|
+
when 8; ::PacketFu::TcpOption::TS.new
|
39
|
+
else; ::PacketFu::TcpOption.new
|
40
|
+
end
|
41
|
+
this_opt.read str[i,str.size]
|
42
|
+
unless this_opt.has_optlen?
|
43
|
+
this_opt.value = nil
|
44
|
+
this_opt.optlen = nil
|
45
|
+
end
|
46
|
+
self << this_opt
|
47
|
+
i += this_opt.sz
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
52
|
+
# Decode parses the TcpOptions object's member options, and produces a
|
53
|
+
# human-readable string by iterating over each element's decode() function.
|
54
|
+
# If TcpOptions elements were not initially created as TcpOptions, an
|
55
|
+
# attempt will be made to convert them.
|
56
|
+
#
|
57
|
+
# The output of decode is suitable as input for TcpOptions#encode.
|
58
|
+
def decode
|
59
|
+
decoded = self.map do |x|
|
60
|
+
if x.kind_of? TcpOption
|
61
|
+
x.decode
|
62
|
+
else
|
63
|
+
x = TcpOptions.new.read(x).decode
|
64
|
+
end
|
65
|
+
end
|
66
|
+
decoded.join(",")
|
67
|
+
end
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
69
|
+
# Encode takes a human-readable string and appends the corresponding
|
70
|
+
# binary options to the TcpOptions object. To completely replace the contents
|
71
|
+
# of the object, use TcpOptions#encode! instead.
|
72
|
+
#
|
73
|
+
# Options are comma-delimited, and are identical to the output of the
|
74
|
+
# TcpOptions#decode function. Note that the syntax can be unforgiving, so
|
75
|
+
# it may be easier to create the subclassed TcpOptions themselves directly,
|
76
|
+
# but this method can be less typing if you know what you're doing.
|
77
|
+
#
|
78
|
+
# Note that by using TcpOptions#encode, strings supplied as values which
|
79
|
+
# can be converted to numbers will be converted first.
|
80
|
+
#
|
81
|
+
# === Example
|
82
|
+
#
|
83
|
+
# t = TcpOptions.new
|
84
|
+
# t.encode("MS:1460,WS:6")
|
85
|
+
# t.to_s # => "\002\004\005\264\002\003\006"
|
86
|
+
# t.encode("NOP")
|
87
|
+
# t.to_s # => "\002\004\005\264\002\003\006\001"
|
88
|
+
def encode(str)
|
89
|
+
opts = str.split(/[\s]*,[\s]*/)
|
90
|
+
opts.each do |o|
|
91
|
+
kind,value = o.split(/[\s]*:[\s]*/)
|
92
|
+
klass = TcpOption.const_get(kind.upcase)
|
93
|
+
value = value.to_i if value =~ /^[0-9]+$/
|
94
|
+
this_opt = klass.new
|
95
|
+
this_opt.encode(value)
|
96
|
+
self << this_opt
|
97
|
+
end
|
98
|
+
self
|
99
|
+
end
|
100
100
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
101
|
+
# Like TcpOption#encode, except the entire contents are replaced.
|
102
|
+
def encode!(str)
|
103
|
+
self.clear if self.size > 0
|
104
|
+
encode(str)
|
105
|
+
end
|
106
|
+
end
|
107
107
|
end
|
@@ -1,43 +1,43 @@
|
|
1
1
|
# -*- coding: binary -*-
|
2
2
|
module PacketFu
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
# Implements the Reserved bits for TCPHeader.
|
4
|
+
#
|
5
|
+
# ==== Header Definition
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# Fixnum (1 bit) :r1
|
9
|
+
# Fixnum (1 bit) :r2
|
10
|
+
# Fixnum (1 bit) :r3
|
11
|
+
class TcpReserved < Struct.new(:r1, :r2, :r3)
|
12
12
|
|
13
|
-
|
13
|
+
include StructFu
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
def initialize(args={})
|
16
|
+
super(
|
17
|
+
args[:r1] || 0,
|
18
|
+
args[:r2] || 0,
|
19
|
+
args[:r3] || 0) if args.kind_of? Hash
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
# Returns the Reserved field as an integer.
|
23
|
+
def to_i
|
24
|
+
(r1.to_i << 2) + (r2.to_i << 1) + r3.to_i
|
25
|
+
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
27
|
+
# Reads a string to populate the object.
|
28
|
+
def read(str)
|
29
|
+
force_binary(str)
|
30
|
+
return self if str.nil? || str.size.zero?
|
31
|
+
if 1.respond_to? :ord
|
32
|
+
byte = str[0].ord
|
33
|
+
else
|
34
|
+
byte = str[0]
|
35
|
+
end
|
36
|
+
self[:r1] = byte & 0b00000100 == 0b00000100 ? 1 : 0
|
37
|
+
self[:r2] = byte & 0b00000010 == 0b00000010 ? 1 : 0
|
38
|
+
self[:r3] = byte & 0b00000001 == 0b00000001 ? 1 : 0
|
39
|
+
self
|
40
|
+
end
|
41
41
|
|
42
|
-
|
42
|
+
end
|
43
43
|
end
|
data/lib/packetfu/protos/udp.rb
CHANGED
@@ -10,133 +10,133 @@ require 'packetfu/protos/udp/mixin'
|
|
10
10
|
|
11
11
|
module PacketFu
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
13
|
+
# UDPPacket is used to construct UDP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
|
14
|
+
#
|
15
|
+
# == Example
|
16
|
+
#
|
17
|
+
# udp_pkt = PacketFu::UDPPacket.new
|
18
|
+
# udp_pkt.udp_src=rand(0xffff-1024) + 1024
|
19
|
+
# udp_pkt.udp_dst=53
|
20
|
+
#
|
21
|
+
# udp_pkt.ip_saddr="1.2.3.4"
|
22
|
+
# udp_pkt.ip_daddr="10.20.30.40"
|
23
|
+
#
|
24
|
+
# udp_pkt.recalc
|
25
|
+
# udp_pkt.to_f('/tmp/udp.pcap')
|
26
|
+
#
|
27
|
+
# == Parameters
|
28
|
+
#
|
29
|
+
# :eth
|
30
|
+
# A pre-generated EthHeader object.
|
31
|
+
# :ip
|
32
|
+
# A pre-generated IPHeader object.
|
33
|
+
# :flavor
|
34
|
+
# TODO: Sets the "flavor" of the UDP packet. UDP packets don't tend have a lot of
|
35
|
+
# flavor, but their underlying ip headers do.
|
36
|
+
# :config
|
37
|
+
# A hash of return address details, often the output of Utils.whoami?
|
38
|
+
class UDPPacket < Packet
|
39
39
|
include ::PacketFu::EthHeaderMixin
|
40
40
|
include ::PacketFu::IPHeaderMixin
|
41
41
|
include ::PacketFu::UDPHeaderMixin
|
42
42
|
|
43
|
-
|
43
|
+
attr_accessor :eth_header, :ip_header, :udp_header
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
def self.can_parse?(str)
|
46
|
+
return false unless str.size >= 28
|
47
|
+
return false unless EthPacket.can_parse? str
|
48
|
+
return false unless IPPacket.can_parse? str
|
49
|
+
return false unless str[23,1] == "\x11"
|
50
|
+
return true
|
51
|
+
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
53
|
+
def read(str=nil, args={})
|
54
|
+
raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
|
55
|
+
@eth_header.read(str)
|
56
|
+
if args[:strip]
|
57
|
+
udp_body_len = self.ip_len - self.ip_hlen - 8
|
58
|
+
@udp_header.body.read(@udp_header.body.to_s[0,udp_body_len])
|
59
|
+
end
|
60
|
+
super(args)
|
61
|
+
self
|
62
|
+
end
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
64
|
+
def initialize(args={})
|
65
|
+
@eth_header = EthHeader.new(args).read(args[:eth])
|
66
|
+
@ip_header = IPHeader.new(args).read(args[:ip])
|
67
|
+
@ip_header.ip_proto=0x11
|
68
|
+
@udp_header = UDPHeader.new(args).read(args[:icmp])
|
69
|
+
@ip_header.body = @udp_header
|
70
|
+
@eth_header.body = @ip_header
|
71
|
+
@headers = [@eth_header, @ip_header, @udp_header]
|
72
|
+
super
|
73
|
+
udp_calc_sum
|
74
|
+
end
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
76
|
+
# udp_calc_sum() computes the UDP checksum, and is called upon intialization.
|
77
|
+
# It usually should be called just prior to dropping packets to a file or on the wire.
|
78
|
+
def udp_calc_sum
|
79
|
+
# This is /not/ delegated down to @udp_header since we need info
|
80
|
+
# from the IP header, too.
|
81
|
+
checksum = (ip_src.to_i >> 16)
|
82
|
+
checksum += (ip_src.to_i & 0xffff)
|
83
|
+
checksum += (ip_dst.to_i >> 16)
|
84
|
+
checksum += (ip_dst.to_i & 0xffff)
|
85
|
+
checksum += 0x11
|
86
|
+
checksum += udp_len.to_i
|
87
|
+
checksum += udp_src.to_i
|
88
|
+
checksum += udp_dst.to_i
|
89
|
+
checksum += udp_len.to_i
|
90
|
+
if udp_len.to_i >= 8
|
91
|
+
# For IP trailers. This isn't very reliable. :/
|
92
|
+
real_udp_payload = payload.to_s[0,(udp_len.to_i-8)]
|
93
|
+
else
|
94
|
+
# I'm not going to mess with this right now.
|
95
|
+
real_udp_payload = payload
|
96
|
+
end
|
97
|
+
chk_payload = (real_udp_payload.size % 2 == 0 ? real_udp_payload : real_udp_payload + "\x00")
|
98
|
+
chk_payload.unpack("n*").each {|x| checksum = checksum+x}
|
99
|
+
checksum = checksum % 0xffff
|
100
|
+
checksum = 0xffff - checksum
|
101
|
+
checksum == 0 ? 0xffff : checksum
|
102
|
+
@udp_header.udp_sum = checksum
|
103
|
+
end
|
104
104
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
105
|
+
# udp_recalc() recalculates various fields of the UDP packet. Valid arguments are:
|
106
|
+
#
|
107
|
+
# :all
|
108
|
+
# Recomputes all calculated fields.
|
109
|
+
# :udp_sum
|
110
|
+
# Recomputes the UDP checksum.
|
111
|
+
# :udp_len
|
112
|
+
# Recomputes the UDP length.
|
113
|
+
def udp_recalc(args=:all)
|
114
|
+
case args
|
115
|
+
when :udp_len
|
116
|
+
@udp_header.udp_recalc
|
117
|
+
when :udp_sum
|
118
|
+
udp_calc_sum
|
119
|
+
when :all
|
120
|
+
@udp_header.udp_recalc
|
121
|
+
udp_calc_sum
|
122
|
+
else
|
123
|
+
raise ArgumentError, "No such field `#{arg}'"
|
124
|
+
end
|
125
|
+
end
|
126
126
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
127
|
+
# Peek provides summary data on packet contents.
|
128
|
+
def peek_format
|
129
|
+
peek_data = ["U "]
|
130
|
+
peek_data << "%-5d" % self.to_s.size
|
131
|
+
peek_data << "%-21s" % "#{self.ip_saddr}:#{self.udp_sport}"
|
132
|
+
peek_data << "->"
|
133
|
+
peek_data << "%21s" % "#{self.ip_daddr}:#{self.udp_dport}"
|
134
|
+
peek_data << "%23s" % "I:"
|
135
|
+
peek_data << "%04x" % self.ip_id
|
136
|
+
peek_data.join
|
137
|
+
end
|
138
138
|
|
139
|
-
|
139
|
+
end
|
140
140
|
|
141
141
|
end
|
142
142
|
|