packetgen 2.1.4 → 2.2.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.
- checksums.yaml +4 -4
- data/lib/packetgen/config.rb +1 -0
- data/lib/packetgen/header.rb +3 -0
- data/lib/packetgen/header/bootp.rb +151 -0
- data/lib/packetgen/header/dhcp.rb +65 -0
- data/lib/packetgen/header/dhcp/option.rb +154 -0
- data/lib/packetgen/header/dhcp/options.rb +42 -0
- data/lib/packetgen/header/dns.rb +4 -4
- data/lib/packetgen/header/dns/name.rb +2 -2
- data/lib/packetgen/header/dns/rr.rb +1 -1
- data/lib/packetgen/header/eap/md5.rb +1 -1
- data/lib/packetgen/header/esp.rb +1 -1
- data/lib/packetgen/header/eth.rb +1 -1
- data/lib/packetgen/header/http.rb +8 -0
- data/lib/packetgen/header/http/headers.rb +76 -0
- data/lib/packetgen/header/http/request.rb +88 -0
- data/lib/packetgen/header/http/response.rb +116 -0
- data/lib/packetgen/header/ike/notify.rb +1 -1
- data/lib/packetgen/header/ike/sa.rb +2 -2
- data/lib/packetgen/header/ike/ts.rb +1 -1
- data/lib/packetgen/header/ip.rb +32 -2
- data/lib/packetgen/header/ipv6.rb +1 -1
- data/lib/packetgen/packet.rb +4 -2
- data/lib/packetgen/types.rb +1 -0
- data/lib/packetgen/types/cstring.rb +67 -0
- data/lib/packetgen/types/fields.rb +7 -5
- data/lib/packetgen/types/int.rb +1 -0
- data/lib/packetgen/types/int_string.rb +10 -3
- data/lib/packetgen/types/string.rb +12 -4
- data/lib/packetgen/types/tlv.rb +44 -12
- data/lib/packetgen/utils.rb +51 -1
- data/lib/packetgen/utils/arp_spoofer.rb +2 -2
- data/lib/packetgen/version.rb +1 -1
- metadata +11 -2
data/lib/packetgen/packet.rb
CHANGED
@@ -385,7 +385,9 @@ module PacketGen
|
|
385
385
|
hdr = hdr.read(binary_str)
|
386
386
|
# First header is found when:
|
387
387
|
# * for one known header,
|
388
|
+
# * +#parse?+ is true
|
388
389
|
# * it exists a known binding with a upper header
|
390
|
+
next unless hdr.parse?
|
389
391
|
search_header(hdr) do
|
390
392
|
first_header = hklass.to_s.gsub(/.*::/, '')
|
391
393
|
end
|
@@ -404,8 +406,8 @@ module PacketGen
|
|
404
406
|
str = last_known_hdr.body
|
405
407
|
nheader = nh.new
|
406
408
|
nheader = nheader.read(str)
|
409
|
+
next unless nheader.parse?
|
407
410
|
add_header nheader, parsing: true
|
408
|
-
nheader.dissect if nheader.respond_to? :dissect
|
409
411
|
end
|
410
412
|
decode_packet_bottom_up = (@headers.last != last_known_hdr)
|
411
413
|
end
|
@@ -413,7 +415,7 @@ module PacketGen
|
|
413
415
|
|
414
416
|
def search_header(hdr)
|
415
417
|
hdr.class.known_headers.each do |nh, bindings|
|
416
|
-
if bindings.check?(hdr)
|
418
|
+
if bindings.check?(hdr)
|
417
419
|
yield nh
|
418
420
|
break
|
419
421
|
end
|
data/lib/packetgen/types.rb
CHANGED
@@ -13,6 +13,7 @@ require_relative 'types/int'
|
|
13
13
|
require_relative 'types/enum'
|
14
14
|
require_relative 'types/string'
|
15
15
|
require_relative 'types/int_string'
|
16
|
+
require_relative 'types/cstring'
|
16
17
|
require_relative 'types/fields'
|
17
18
|
require_relative 'types/array'
|
18
19
|
require_relative 'types/tlv'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# This file is part of PacketGen
|
3
|
+
# See https://github.com/sdaubert/packetgen for more informations
|
4
|
+
# Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
|
5
|
+
# This program is published under MIT license.
|
6
|
+
|
7
|
+
module PacketGen
|
8
|
+
module Types
|
9
|
+
|
10
|
+
# This class handles null-terminated strings (aka C strings).
|
11
|
+
# @author Sylvain Daubert
|
12
|
+
class CString < ::String
|
13
|
+
|
14
|
+
# @param [String] str
|
15
|
+
# @param [Hash] options
|
16
|
+
# @option options [Integer] :static_length set a static length for this string
|
17
|
+
def initialize(options={})
|
18
|
+
super()
|
19
|
+
@static_length = options[:static_length]
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [::String] str
|
23
|
+
# @return [String] self
|
24
|
+
def read(str)
|
25
|
+
s = str.to_s
|
26
|
+
if @static_length.is_a? Integer
|
27
|
+
s = s[0, @static_length]
|
28
|
+
end
|
29
|
+
idx = s.index(0.chr)
|
30
|
+
s = s[0, idx] unless idx.nil?
|
31
|
+
self.replace s
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# get null-terminated string
|
36
|
+
# @return [String]
|
37
|
+
def to_s
|
38
|
+
if @static_length.is_a? Integer
|
39
|
+
if self.size >= @static_length
|
40
|
+
s = self[0, @static_length]
|
41
|
+
s[-1] = 0.chr
|
42
|
+
PacketGen.force_binary s
|
43
|
+
else
|
44
|
+
PacketGen.force_binary(self + "\0" * (@static_length - self.length))
|
45
|
+
end
|
46
|
+
else
|
47
|
+
PacketGen.force_binary(self + 0.chr)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Integer]
|
52
|
+
def sz
|
53
|
+
if @static_length.is_a? Integer
|
54
|
+
@static_length
|
55
|
+
else
|
56
|
+
to_s.size
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [String]
|
61
|
+
def to_human
|
62
|
+
idx = self.index(0.chr) || self.sz
|
63
|
+
self[0, idx].inspect
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -132,7 +132,7 @@ module PacketGen
|
|
132
132
|
# +:builder+ option is not set.
|
133
133
|
# @option options [Object] :default default value
|
134
134
|
# @option options [Lambda] :builder lambda to construct this field.
|
135
|
-
#
|
135
|
+
# Parameters to this lambda is the caller object and the field type class.
|
136
136
|
# @option options [Lambda] :optional define this field as optional. Given lambda
|
137
137
|
# is used to known if this field is present or not. Parameter to this lambda is
|
138
138
|
# the being defined Field object.
|
@@ -296,7 +296,7 @@ module PacketGen
|
|
296
296
|
type, default, builder, optional, enum, field_options = ary
|
297
297
|
default = default.call if default.is_a?(Proc)
|
298
298
|
@fields[field] = if builder
|
299
|
-
builder.call(self)
|
299
|
+
builder.call(self, type)
|
300
300
|
elsif enum
|
301
301
|
type.new(enum)
|
302
302
|
elsif !field_options.empty?
|
@@ -376,18 +376,20 @@ module PacketGen
|
|
376
376
|
start = 0
|
377
377
|
fields.each do |field|
|
378
378
|
next unless is_present?(field)
|
379
|
+
obj = nil
|
379
380
|
if self[field].respond_to? :width
|
380
381
|
width = self[field].width
|
381
|
-
self[field].read str[start, width]
|
382
|
+
obj = self[field].read str[start, width]
|
382
383
|
start += width
|
383
384
|
elsif self[field].respond_to? :sz
|
384
|
-
self[field].read str[start..-1]
|
385
|
+
obj = self[field].read str[start..-1]
|
385
386
|
size = self[field].sz
|
386
387
|
start += size
|
387
388
|
else
|
388
|
-
self[field].read str[start..-1]
|
389
|
+
obj = self[field].read str[start..-1]
|
389
390
|
start = str.size
|
390
391
|
end
|
392
|
+
self[field] = obj unless obj == self[field]
|
391
393
|
end
|
392
394
|
|
393
395
|
self
|
data/lib/packetgen/types/int.rb
CHANGED
@@ -16,10 +16,10 @@ module PacketGen
|
|
16
16
|
# @return [String]
|
17
17
|
attr_reader :string
|
18
18
|
|
19
|
-
# @param [::String] str
|
20
19
|
# @param [Class] len_type should be a {Int} subclass
|
21
|
-
|
22
|
-
|
20
|
+
# @param [::String] string
|
21
|
+
def initialize(len_type=Int8, string: '')
|
22
|
+
@string = Types::String.new.read(string)
|
23
23
|
@length = len_type.new
|
24
24
|
calc_length
|
25
25
|
end
|
@@ -61,6 +61,13 @@ module PacketGen
|
|
61
61
|
@length.to_s << @string.to_s
|
62
62
|
end
|
63
63
|
|
64
|
+
# Get human readable string
|
65
|
+
# @return [::String]
|
66
|
+
# @since 2.2.0
|
67
|
+
def to_human
|
68
|
+
@string.to_s.inspect
|
69
|
+
end
|
70
|
+
|
64
71
|
# Set length from internal string length
|
65
72
|
# @return [Integer]
|
66
73
|
def calc_length
|
@@ -16,9 +16,11 @@ module PacketGen
|
|
16
16
|
# @param [Hash] options
|
17
17
|
# @option options [Types::Int,Proc] :length_from object or proc from which
|
18
18
|
# takes length when reading
|
19
|
-
|
20
|
-
|
19
|
+
# @option options [Integer] :static_length set a static length for this string
|
20
|
+
def initialize(options={})
|
21
|
+
super()
|
21
22
|
@length_from = options[:length_from]
|
23
|
+
@static_length = options[:static_length]
|
22
24
|
end
|
23
25
|
|
24
26
|
# @param [::String] str
|
@@ -26,10 +28,16 @@ module PacketGen
|
|
26
28
|
def read(str)
|
27
29
|
s = str.to_s
|
28
30
|
s = case @length_from
|
29
|
-
when Int
|
31
|
+
when Types::Int
|
30
32
|
s[0, @length_from.to_i]
|
33
|
+
when Proc
|
34
|
+
s[0, @length_from.call]
|
31
35
|
else
|
32
|
-
|
36
|
+
if @static_length.is_a? Integer
|
37
|
+
s[0, @static_length]
|
38
|
+
else
|
39
|
+
s
|
40
|
+
end
|
33
41
|
end
|
34
42
|
self.replace s
|
35
43
|
self
|
data/lib/packetgen/types/tlv.rb
CHANGED
@@ -30,8 +30,7 @@ module PacketGen
|
|
30
30
|
define_field :length, Int8
|
31
31
|
# @!attribute value
|
32
32
|
# @return [String]
|
33
|
-
define_field :value, String
|
34
|
-
builder: ->(tlv) { String.new('', length_from: tlv[:length]) }
|
33
|
+
define_field :value, String
|
35
34
|
|
36
35
|
# @param [Hash] options
|
37
36
|
# @option options [Integer] :type
|
@@ -41,16 +40,31 @@ module PacketGen
|
|
41
40
|
# Default: {Int8}.
|
42
41
|
# @option options [Class] :l {Int} subclass for +:length+ attribute.
|
43
42
|
# Default: {Int8}.
|
43
|
+
# @option options [Class] :v {String} subclass for +:value+ attribute.
|
44
|
+
# Default: {Types::String}.
|
44
45
|
def initialize(options={})
|
45
46
|
super
|
46
|
-
self[:type] = options[:t].new(type) if options[:t]
|
47
|
-
self[:length] = options[:l].new(length) if options[:l]
|
48
|
-
self[:value] =
|
47
|
+
self[:type] = options[:t].new(self.type) if options[:t]
|
48
|
+
self[:length] = options[:l].new(self.length) if options[:l]
|
49
|
+
self[:value] = options[:v].new if options[:v]
|
49
50
|
self.type = options[:type] if options[:type]
|
50
51
|
self.value = options[:value] if options[:value]
|
51
52
|
self.length = options[:length] if options[:length]
|
52
53
|
end
|
53
54
|
|
55
|
+
# Populate object from a binary string
|
56
|
+
# @param [String] str
|
57
|
+
# @return [Fields] self
|
58
|
+
def read(str)
|
59
|
+
idx = 0
|
60
|
+
self[:type].read str[idx, self[:type].sz]
|
61
|
+
idx += self[:type].sz
|
62
|
+
self[:length].read str[idx, self[:length].sz]
|
63
|
+
idx += self[:length].sz
|
64
|
+
self[:value].read str[idx, self.length]
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
54
68
|
# @private
|
55
69
|
alias old_type= type=
|
56
70
|
|
@@ -73,13 +87,31 @@ module PacketGen
|
|
73
87
|
end
|
74
88
|
end
|
75
89
|
|
76
|
-
# Set +value
|
77
|
-
# @param [::String]
|
78
|
-
# @return [::String]
|
79
|
-
def value=(
|
80
|
-
self.
|
81
|
-
|
82
|
-
|
90
|
+
# Set +value+. May set +length+ if value is a {Types::String}.
|
91
|
+
# @param [::String,Integer] val
|
92
|
+
# @return [::String,Integer]
|
93
|
+
def value=(val)
|
94
|
+
if self[:value].respond_to? :from_human
|
95
|
+
self[:value].from_human val
|
96
|
+
elsif self[:value].is_a? Types::Int
|
97
|
+
self[:value].value = val
|
98
|
+
else
|
99
|
+
self.length = val.length if val.is_a? ::String
|
100
|
+
self[:value].read val
|
101
|
+
end
|
102
|
+
val
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get +value+
|
106
|
+
# @return [Object] depend on +value+ type
|
107
|
+
def value
|
108
|
+
if self[:value].respond_to? :to_human
|
109
|
+
self[:value].to_human
|
110
|
+
elsif self[:value].is_a? Types::Int
|
111
|
+
self[:value].to_i
|
112
|
+
else
|
113
|
+
self[:value]
|
114
|
+
end
|
83
115
|
end
|
84
116
|
|
85
117
|
# Return human readable type, if TYPES is defined
|
data/lib/packetgen/utils.rb
CHANGED
@@ -53,7 +53,8 @@ module PacketGen
|
|
53
53
|
timeout = options[:timeout] || 1
|
54
54
|
my_hwaddr = Config.instance.hwaddr(iface)
|
55
55
|
arp_pkt = Packet.gen('Eth', dst: 'ff:ff:ff:ff:ff:ff', src: my_hwaddr)
|
56
|
-
arp_pkt.add('ARP', sha:
|
56
|
+
arp_pkt.add('ARP', sha: Config.instance.hwaddr, spa: Config.instance.ipaddr,
|
57
|
+
tpa: ipaddr)
|
57
58
|
|
58
59
|
capture = Capture.new(iface: iface, timeout: timeout, max: 1,
|
59
60
|
filter: "arp src #{ipaddr} and ether dst #{my_hwaddr}")
|
@@ -97,5 +98,54 @@ module PacketGen
|
|
97
98
|
as.start(target_ip, spoofed_ip, mac: options[:mac])
|
98
99
|
as.wait
|
99
100
|
end
|
101
|
+
|
102
|
+
# Man in the middle attack. Capture all packets between two peers on
|
103
|
+
# same local network.
|
104
|
+
# @note This method is provided for test purpose.
|
105
|
+
# @param [String] target1 IP address of first peer to attack
|
106
|
+
# @param [String] target2 IP address of second peer to attack
|
107
|
+
# @param [Hash] options
|
108
|
+
# @option options [Float,Integer] :interval number of seconds between 2
|
109
|
+
# ARP packets (default: 1.0).
|
110
|
+
# @option options [String] :iface interface to use. Default to
|
111
|
+
# {PacketGen.default_iface}
|
112
|
+
# @return [void]
|
113
|
+
# @yieldparam [Packet] pkt captured packets between target1 and target2
|
114
|
+
# @yieldreturn [Packet] packet to send to target1 or 2. This may be
|
115
|
+
# modified received packet
|
116
|
+
# @example Change ID in packets
|
117
|
+
# PacketGen::Utils.mitm('192.168.0.1', '192.168.0.45') do |pkt|
|
118
|
+
# if pkt.ip.src == '192.168.0.1'
|
119
|
+
# # 192.168.0.1 -> 192.168.0.45
|
120
|
+
# pkt.ip.id = 1
|
121
|
+
# else
|
122
|
+
# # 192.168.0.45 -> 192.168.0.1
|
123
|
+
# pkt.ip.id = 2
|
124
|
+
# end
|
125
|
+
# pkt
|
126
|
+
# end
|
127
|
+
# @since 2.2.0
|
128
|
+
def self.mitm(target1, target2, options={})
|
129
|
+
options = { iface: PacketGen.default_iface }.merge(options)
|
130
|
+
|
131
|
+
spoofer = Utils::ARPSpoofer.new(options)
|
132
|
+
spoofer.add target1, target2, options
|
133
|
+
spoofer.add target2, target1, options
|
134
|
+
|
135
|
+
my_mac = Config.instance.hwaddr(options[:iface])
|
136
|
+
my_ip = Config.instance.ipaddr(options[:iface])
|
137
|
+
capture = Capture.new(iface: options[:iface],
|
138
|
+
filter: "((ip src #{target1} and not ip dst #{my_ip}) or" +
|
139
|
+
" (ip src #{target2} and not ip dst #{my_ip}) or"+
|
140
|
+
" (ip dst #{target1} and not ip src #{my_ip}) or"+
|
141
|
+
" (ip dst #{target2} and not ip src #{my_ip}))"+
|
142
|
+
" and ether dst #{my_mac}")
|
143
|
+
|
144
|
+
spoofer.start_all
|
145
|
+
capture.start do |pkt|
|
146
|
+
modified_pkt = yield pkt
|
147
|
+
modified_pkt.ip.to_w(options[:iface])
|
148
|
+
end
|
149
|
+
end
|
100
150
|
end
|
101
151
|
end
|
@@ -147,7 +147,7 @@ module PacketGen
|
|
147
147
|
@spoof_thread = Thread.new(@queue, @iface, @timeout, @interval) do |queue, iface, timeout, interval|
|
148
148
|
while timeout.nil? or timeout > 0.0 do
|
149
149
|
packets = queue.pop unless queue.empty?
|
150
|
-
send_packets_on_wire packets
|
150
|
+
send_packets_on_wire(packets) unless packets.empty?
|
151
151
|
timeout -= interval if timeout
|
152
152
|
sleep interval
|
153
153
|
end
|
@@ -156,7 +156,7 @@ module PacketGen
|
|
156
156
|
|
157
157
|
# send packets on wire
|
158
158
|
def send_packets_on_wire(packets)
|
159
|
-
packets.each { |pkt| pkt.to_w(iface) }
|
159
|
+
packets.each { |pkt| pkt.to_w(@iface) }
|
160
160
|
end
|
161
161
|
|
162
162
|
# Deactivate spoofing for given target
|
data/lib/packetgen/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packetgen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pcaprub
|
@@ -150,7 +150,11 @@ files:
|
|
150
150
|
- lib/packetgen/header/arp.rb
|
151
151
|
- lib/packetgen/header/asn1_base.rb
|
152
152
|
- lib/packetgen/header/base.rb
|
153
|
+
- lib/packetgen/header/bootp.rb
|
153
154
|
- lib/packetgen/header/crypto.rb
|
155
|
+
- lib/packetgen/header/dhcp.rb
|
156
|
+
- lib/packetgen/header/dhcp/option.rb
|
157
|
+
- lib/packetgen/header/dhcp/options.rb
|
154
158
|
- lib/packetgen/header/dns.rb
|
155
159
|
- lib/packetgen/header/dns/name.rb
|
156
160
|
- lib/packetgen/header/dns/opt.rb
|
@@ -175,6 +179,10 @@ files:
|
|
175
179
|
- lib/packetgen/header/esp.rb
|
176
180
|
- lib/packetgen/header/eth.rb
|
177
181
|
- lib/packetgen/header/gre.rb
|
182
|
+
- lib/packetgen/header/http.rb
|
183
|
+
- lib/packetgen/header/http/headers.rb
|
184
|
+
- lib/packetgen/header/http/request.rb
|
185
|
+
- lib/packetgen/header/http/response.rb
|
178
186
|
- lib/packetgen/header/icmp.rb
|
179
187
|
- lib/packetgen/header/icmpv6.rb
|
180
188
|
- lib/packetgen/header/ike.rb
|
@@ -211,6 +219,7 @@ files:
|
|
211
219
|
- lib/packetgen/proto.rb
|
212
220
|
- lib/packetgen/types.rb
|
213
221
|
- lib/packetgen/types/array.rb
|
222
|
+
- lib/packetgen/types/cstring.rb
|
214
223
|
- lib/packetgen/types/enum.rb
|
215
224
|
- lib/packetgen/types/fields.rb
|
216
225
|
- lib/packetgen/types/int.rb
|