packetgen 2.1.2 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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