packetgen 2.1.2 → 2.1.3

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.
@@ -60,7 +60,8 @@ module PacketGen
60
60
  # * +:builder+ to give a builder/constructor lambda to create field. The lambda
61
61
  # takes one argument: {Fields} subclass object owning field,
62
62
  # * +:optional+ to define this field as optional. This option takes a lambda
63
- # parameter used to say if this field is present or not.
63
+ # parameter used to say if this field is present or not,
64
+ # * +:enum+ to define Hash enumeration for an {Enum} type.
64
65
  # For example:
65
66
  # # 32-bit integer field defaulting to 1
66
67
  # define_field :type, PacketGen::Types::Int32, default: 1
@@ -71,6 +72,8 @@ module PacketGen
71
72
  # define_field :body_size, PacketGen::Type::Int16
72
73
  # # String field which length is taken from body_size field
73
74
  # define_field :body, PacketGen::Type::String, builder: ->(obj) { PacketGen::Type::String.new('', length_from: obj[:body_size]) }
75
+ # # 16-bit enumeration type. As :default not specified, default to first value of enum
76
+ # define_field :type_class, PacketGen::Type::Int16Enum, enum: { 'class1' => 1, 'class2' => 2}
74
77
  #
75
78
  # {.define_field_before} and {.define_field_after} are also defined to relatively
76
79
  # create a field from anoher one (for example, when adding a field in a subclass).
@@ -133,16 +136,21 @@ module PacketGen
133
136
  # @option options [Lambda] :optional define this field as optional. Given lambda
134
137
  # is used to known if this field is present or not. Parameter to this lambda is
135
138
  # the being defined Field object.
139
+ # @option options [Hash] :enum mandatory option for an {Enum} type.
140
+ # Define enumeration: hash's keys are +String+, and values are +Integer+.
136
141
  # @return [void]
137
142
  def self.define_field(name, type, options={})
138
143
  define = []
139
- if type < Types::Int
144
+ if type < Types::Enum
145
+ define << "def #{name}; self[:#{name}].to_i; end"
146
+ define << "def #{name}=(val) self[:#{name}].value = val; end"
147
+ elsif type < Types::Int
140
148
  define << "def #{name}; self[:#{name}].to_i; end"
141
149
  define << "def #{name}=(val) self[:#{name}].read val; end"
142
150
  elsif type.instance_methods.include? :to_human and
143
- type.instance_methods.include? :from_human
144
- define << "def #{name}; self[:#{name}].to_human; end"
145
- define << "def #{name}=(val) self[:#{name}].from_human val; end"
151
+ type.instance_methods.include? :from_human
152
+ define << "def #{name}; self[:#{name}].to_human; end"
153
+ define << "def #{name}=(val) self[:#{name}].from_human val; end"
146
154
  else
147
155
  define << "def #{name}; self[:#{name}]; end\n"
148
156
  define << "def #{name}=(val) self[:#{name}].read val; end"
@@ -151,8 +159,11 @@ module PacketGen
151
159
  define.delete(1) if type.instance_methods.include? "#{name}=".to_sym
152
160
  define.delete(0) if type.instance_methods.include? name
153
161
  class_eval define.join("\n")
154
- @field_defs[name] = [type, options.delete(:default), options.delete(:builder),
155
- options.delete(:optional), options]
162
+ @field_defs[name] = [type, options.delete(:default),
163
+ options.delete(:builder),
164
+ options.delete(:optional),
165
+ options.delete(:enum),
166
+ options]
156
167
  @ordered_fields << name
157
168
  end
158
169
 
@@ -285,14 +296,23 @@ module PacketGen
285
296
  default = ary[1].is_a?(Proc) ? ary[1].call : ary[1]
286
297
  @fields[field] = if ary[2]
287
298
  ary[2].call(self)
288
- elsif !ary[4].empty?
299
+ elsif ary[4]
289
300
  ary[0].new(ary[4])
301
+ elsif !ary[5].empty?
302
+ ary[0].new(ary[5])
290
303
  else
291
304
  ary[0].new
292
305
  end
293
306
 
294
307
  value = options[field] || default
295
- if ary[0] < Types::Int
308
+ if ary[0] < Types::Enum
309
+ case value
310
+ when ::String
311
+ @fields[field].value = value
312
+ else
313
+ @fields[field].read(value)
314
+ end
315
+ elsif ary[0] < Types::Int
296
316
  @fields[field].read(value)
297
317
  elsif ary[0] <= Types::String
298
318
  @fields[field].read(value)
@@ -36,6 +36,10 @@ module PacketGen
36
36
  @default = default
37
37
  end
38
38
 
39
+ # @abstract
40
+ # Read an Int from a binary string or an integer
41
+ # @param [Integer, String] value
42
+ # @return [self]
39
43
  def read(value)
40
44
  @value = if value.is_a?(Integer)
41
45
  value.to_i
@@ -0,0 +1,101 @@
1
+ # This file is part of PacketGen
2
+ # See https://github.com/sdaubert/packetgen for more informations
3
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
+ # This program is published under MIT license.
5
+ require_relative 'config'
6
+ require_relative 'utils/arp_spoofer'
7
+
8
+ module PacketGen
9
+
10
+ # Collection of some network utilities.
11
+ #
12
+ # This module is not enabled by default. You need to:
13
+ # require 'packetgen/utils'
14
+ # @author Sylvain Daubert
15
+ # @since 2.1.3
16
+ module Utils
17
+
18
+ # Get local ARP cache
19
+ # @return [Hash] key: IP address, value: array containing MAC address and
20
+ # interface name
21
+ def self.arp_cache
22
+ raw_cache = %x(/usr/sbin/arp -an)
23
+
24
+ cache = {}
25
+ raw_cache.split(/\n/).each do |line|
26
+ match = line.match(/\((\d+\.\d+\.\d+\.\d+)\) at (([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})(?: \[ether\])? on (\w+)/)
27
+ if match
28
+ cache[match[1]] = [match[2], match[4]]
29
+ end
30
+ end
31
+
32
+ cache
33
+ end
34
+
35
+ # Get MAC address from an IP address, or nil if this IP address is unknown
36
+ # on local network.
37
+ # @param [String] ipaddr dotted-octet IP address
38
+ # @param [Hash] options
39
+ # @option options [String] :iface interface name. Default to
40
+ # {PacketGen.default_iface}
41
+ # @option options [Boolean] :no_cache if +true+, do not query local ARP
42
+ # cache and always send an ARP request on wire. Default to +false+
43
+ # @option options [Integer] :timeout timeout in seconds before stopping
44
+ # request. Default to 2.
45
+ # @return [String,nil]
46
+ def self.arp(ipaddr, options={})
47
+ unless options[:no_cache]
48
+ local_cache = self.arp_cache
49
+ return local_cache[ipaddr].first if local_cache.has_key? ipaddr
50
+ end
51
+
52
+ iface = options[:iface] || PacketGen.default_iface
53
+ timeout = options[:timeout] || 1
54
+ my_hwaddr = Config.instance.hwaddr(iface)
55
+ arp_pkt = Packet.gen('Eth', dst: 'ff:ff:ff:ff:ff:ff', src: my_hwaddr)
56
+ arp_pkt.add('ARP', sha: @config.hwaddr, spa: @config.ipaddr, tpa: ipaddr)
57
+
58
+ capture = Capture.new(iface: iface, timeout: timeout, max: 1,
59
+ filter: "arp src #{ipaddr} and ether dst #{my_hwaddr}")
60
+ cap_thread = Thread.new do
61
+ capture.start
62
+ end
63
+
64
+ arp_pkt.to_w(iface)
65
+ cap_thread.join
66
+
67
+ if capture.packets.size > 0
68
+ capture.packets.each do |pkt|
69
+ if pkt.arp.spa.to_s == ipaddr
70
+ break pkt.arp.sha.to_s
71
+ end
72
+ end
73
+ else
74
+ nil
75
+ end
76
+ end
77
+
78
+ # Do ARP spoofing on given IP address. Call to this method blocks.
79
+ # @note This method is provided for test purpose.
80
+ # For more control, see {ARPSpoofer} class.
81
+ # @param [String] target_ip target IP address
82
+ # @param [String] spoofed_ip IP address to spoofed_ip
83
+ # @param [Hash] options
84
+ # @option options [String] :mac MAC address used to poison target
85
+ # ARP cache. Default to local MAC address.
86
+ # @option options [Integer,nil] :for_seconds number of seconds to do ARP spoofing.
87
+ # If not defined, spoof forever.
88
+ # @option options [Float,Integer] :interval number of seconds between 2
89
+ # ARP packets (default: 1.0).
90
+ # @option options [String] :iface interface to use. Default to
91
+ # {PacketGen.default_iface}
92
+ # @return [void]
93
+ def self.arp_spoof(target_ip, spoofed_ip, options={})
94
+ interval = options[:interval] || 1.0
95
+ as = ARPSpoofer.new(for_seconds: options[:for_seconds], interval: interval,
96
+ iface: options[:iface])
97
+ as.start(target_ip, spoofed_ip, mac: options[:mac])
98
+ as.wait
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,191 @@
1
+ # This file is part of PacketGen
2
+ # See https://github.com/sdaubert/packetgen for more informations
3
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
+ # This program is published under MIT license.
5
+ require 'thread'
6
+
7
+ module PacketGen
8
+ module Utils
9
+
10
+ # @note This class is provided for test purpose.
11
+ # Utility class to make ARP spoofing.
12
+ # spoofer = PacketGen::Utils::ARPSpoofer.new
13
+ # # start an ARP spoof: send forged ARP packets to target to spoof spoofed_ip
14
+ # spoofer.start target_ip, spoofed_ip
15
+ # # start another ARP spoof. Say to target2 spoofed_ip has given MAC address
16
+ # spoofer.start target2_ip, spoofed_ip, mac: '00:00:00:00:00:01'
17
+ # # stop spoofing on target2
18
+ # spoofer.stop target2_ip
19
+ # # stop all spoofings
20
+ # spoofer.stop_all
21
+ # @author Sylvain Daubert
22
+ # @since 2.1.3
23
+ class ARPSpoofer
24
+
25
+ # @param [Integer,Float,nil] timeout spoof will happen for this amount
26
+ # of time
27
+ # @param [Integer,Float] interval time between 2 ARP packets
28
+ # @param [String,nil] iface network interface on which do spoofing.
29
+ # Defaults to {PacketGen.default_iface}
30
+ def initialize(timeout: nil, interval: 1.0, iface: nil)
31
+ @timeout = timeout
32
+ @timeout = @timeout.to_f if @timeout
33
+ @interval = interval
34
+ @iface = iface || PacketGen.default_iface
35
+ @targets = {}
36
+ @arp_packets = {}
37
+ @spoof_thread = nil
38
+ @queue = Queue.new
39
+ end
40
+
41
+ # Add a target to spoofer, without starting attack. Spoofing should
42
+ # be enabled with {#start_all}.
43
+ # @param [String] target_ip target IP address
44
+ # @param [String] spoofed_ip spoofed IP address
45
+ # @param [Hash] options
46
+ # @option options [String] :mac attacker's MAC address. Defaults to
47
+ # local MAC address.
48
+ # @option options [String] :target_mac target MAC address. If not given,
49
+ # an ARP request is made to get it.
50
+ # @return [void]
51
+ def add(target_ip, spoofed_ip, options={})
52
+ @targets[target_ip] = options.merge({spoofed_ip: spoofed_ip, active: false})
53
+ end
54
+
55
+ # Remove target from spoofer.
56
+ # @param [String] target_ip target IP address
57
+ # @return [void]
58
+ def remove(target_ip)
59
+ @targets.delete target_ip
60
+ @arp_packets.delete target_ip
61
+ end
62
+
63
+ # Get registered targets (all targets, registered with {#add} and {#start})
64
+ # @return [Array<String>] list of target IP addresses
65
+ def registered_targets
66
+ @targets.keys
67
+ end
68
+
69
+ # Get active targets (registered with {#start}, or all after using
70
+ # {#start_all})
71
+ # @return [Array<String>] list of target IP addresses
72
+ def active_targets
73
+ @arp_packets.keys
74
+ end
75
+
76
+ # Start spoofing on given target
77
+ # @param [String] target_ip target IP address
78
+ # @param [String] spoofed_ip spoofed IP address
79
+ # @param [Hash] options
80
+ # @option options [String] :mac attacker's MAC address. Defaults to
81
+ # local MAC address.
82
+ # @option options [String] :target_mac target MAC address. If not given,
83
+ # an ARP request is made to get it.
84
+ # @return [void]
85
+ def start(target_ip, spoofed_ip, options={})
86
+ add target_ip, spoofed_ip, options
87
+ activate target_ip
88
+ end
89
+
90
+ # Stop spoofing on given target
91
+ # @param [String] target_ip target IP address
92
+ # @return [void]
93
+ def stop(target_ip)
94
+ deactivate target_ip
95
+ remove target_ip
96
+ end
97
+
98
+ # Start spoofing on all targets added with {#add}.
99
+ # @return [void]
100
+ def start_all
101
+ @targets.each do |target_ip, _|
102
+ activate target_ip
103
+ end
104
+ end
105
+
106
+ # Stop spoofing on all targets.
107
+ # @return [void]
108
+ def stop_all
109
+ @targets.each do |target_ip, _|
110
+ deactivate target_ip
111
+ end
112
+ end
113
+
114
+ # Say if spoofing on given target is active or not
115
+ # @param [String] target_ip target IP address
116
+ # @return [Boolean,nil]
117
+ def active?(target_ip)
118
+ if @targets.has_key?(target_ip)
119
+ @targets[target_ip][:active]
120
+ else
121
+ nil
122
+ end
123
+ end
124
+
125
+ # Wait for spoofing to finish. Wait forever if no +timeout+ options
126
+ # was set on {#initialize}.
127
+ def wait
128
+ @spoof_thread.join
129
+ end
130
+
131
+ private
132
+
133
+ # Activate spoofing for given target
134
+ # @param [String] target_ip
135
+ # @return [void]
136
+ def activate(target_ip)
137
+ @arp_packets[target_ip] = make_arp_packet(target_ip)
138
+ @queue << @arp_packets.values
139
+ unless @spoof_thread
140
+ create_spoof_thread
141
+ end
142
+ @targets[target_ip][:active] = true
143
+ end
144
+
145
+ # Create spoof thread
146
+ def create_spoof_thread
147
+ @spoof_thread = Thread.new(@queue, @iface, @timeout, @interval) do |queue, iface, timeout, interval|
148
+ while timeout.nil? or timeout > 0.0 do
149
+ packets = queue.pop unless queue.empty?
150
+ send_packets_on_wire packets
151
+ timeout -= interval if timeout
152
+ sleep interval
153
+ end
154
+ end
155
+ end
156
+
157
+ # send packets on wire
158
+ def send_packets_on_wire(packets)
159
+ packets.each { |pkt| pkt.to_w(iface) }
160
+ end
161
+
162
+ # Deactivate spoofing for given target
163
+ # @param [String] target_ip
164
+ # @return [void]
165
+ def deactivate(target_ip)
166
+ @arp_packets.delete target_ip
167
+ if @arp_packets.empty?
168
+ @spoof_thread.kill
169
+ @spoof_thread = nil
170
+ else
171
+ @queue << @arp_packets.values
172
+ end
173
+ @targets[target_ip][:active] = false
174
+ end
175
+
176
+ # Create ARP packet to spoof given target
177
+ # @param [String] target_ip
178
+ # @return [Packet]
179
+ def make_arp_packet(target_ip)
180
+ params = @targets[target_ip]
181
+ mac = params[:mac] || Config.instance.hwaddr(@iface)
182
+
183
+ target_mac = params[:target_mac] || Utils.arp(target_ip)
184
+
185
+ Packet.gen('Eth', dst: target_mac, src: mac).
186
+ add('ARP', op: 'reply', sha: mac, spa: params[:spoofed_ip],
187
+ tha: target_mac, tpa: target_ip)
188
+ end
189
+ end
190
+ end
191
+ end
@@ -8,5 +8,5 @@
8
8
  # @author Sylvain Daubert
9
9
  module PacketGen
10
10
  # PacketGen version
11
- VERSION = '2.1.2'
11
+ VERSION = '2.1.3'
12
12
  end
data/packetgen.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency 'rasn1', '~>0.3', '>= 0.3.1'
29
29
 
30
30
  spec.add_development_dependency 'bundler', '~> 1.7'
31
- spec.add_development_dependency 'rake', '~> 10.0'
31
+ spec.add_development_dependency 'rake', '~> 12.0'
32
32
  spec.add_development_dependency 'rspec', '~> 3.0'
33
33
  spec.add_development_dependency 'simplecov', '~> 0.12'
34
34
  spec.add_development_dependency 'yard', '~> 0.9'
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.1.2
4
+ version: 2.1.3
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-09-30 00:00:00.000000000 Z
11
+ date: 2017-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pcaprub
@@ -78,14 +78,14 @@ dependencies:
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '10.0'
81
+ version: '12.0'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '10.0'
88
+ version: '12.0'
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: rspec
91
91
  requirement: !ruby/object:Gem::Requirement
@@ -206,12 +206,15 @@ files:
206
206
  - lib/packetgen/proto.rb
207
207
  - lib/packetgen/types.rb
208
208
  - lib/packetgen/types/array.rb
209
+ - lib/packetgen/types/enum.rb
209
210
  - lib/packetgen/types/fields.rb
210
211
  - lib/packetgen/types/int.rb
211
212
  - lib/packetgen/types/int_string.rb
212
213
  - lib/packetgen/types/oui.rb
213
214
  - lib/packetgen/types/string.rb
214
215
  - lib/packetgen/types/tlv.rb
216
+ - lib/packetgen/utils.rb
217
+ - lib/packetgen/utils/arp_spoofer.rb
215
218
  - lib/packetgen/version.rb
216
219
  - packetgen.gemspec
217
220
  homepage: https://github.com/sdaubert/packetgen