bettercap 1.4.6 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bettercap +1 -1
  3. data/lib/bettercap.rb +1 -0
  4. data/lib/bettercap/context.rb +63 -70
  5. data/lib/bettercap/discovery/agents/base.rb +2 -2
  6. data/lib/bettercap/discovery/thread.rb +5 -4
  7. data/lib/bettercap/firewalls/base.rb +2 -4
  8. data/lib/bettercap/firewalls/{osx.rb → bsd.rb} +3 -3
  9. data/lib/bettercap/firewalls/linux.rb +2 -2
  10. data/lib/bettercap/firewalls/redirection.rb +5 -2
  11. data/lib/bettercap/logger.rb +6 -11
  12. data/lib/bettercap/memory.rb +56 -0
  13. data/lib/bettercap/monkey/em-proxy/proxy.rb +23 -0
  14. data/lib/bettercap/monkey/packetfu/pcap.rb +51 -0
  15. data/lib/bettercap/network/arp_reader.rb +2 -2
  16. data/lib/bettercap/network/network.rb +2 -2
  17. data/lib/bettercap/network/packet_queue.rb +2 -2
  18. data/lib/bettercap/network/protos/base.rb +8 -5
  19. data/lib/bettercap/network/protos/dhcp.rb +5 -124
  20. data/lib/bettercap/network/target.rb +7 -2
  21. data/lib/bettercap/options/core_options.rb +189 -0
  22. data/lib/bettercap/options/options.rb +167 -0
  23. data/lib/bettercap/options/proxy_options.rb +258 -0
  24. data/lib/bettercap/options/server_options.rb +71 -0
  25. data/lib/bettercap/options/sniff_options.rb +90 -0
  26. data/lib/bettercap/options/spoof_options.rb +71 -0
  27. data/lib/bettercap/proxy/{module.rb → http/module.rb} +8 -4
  28. data/lib/bettercap/proxy/{modules → http/modules}/injectcss.rb +2 -2
  29. data/lib/bettercap/proxy/{modules → http/modules}/injecthtml.rb +2 -2
  30. data/lib/bettercap/proxy/{modules → http/modules}/injectjs.rb +2 -2
  31. data/lib/bettercap/proxy/{proxy.rb → http/proxy.rb} +5 -2
  32. data/lib/bettercap/proxy/{request.rb → http/request.rb} +4 -0
  33. data/lib/bettercap/proxy/{response.rb → http/response.rb} +3 -0
  34. data/lib/bettercap/proxy/{ssl → http/ssl}/authority.rb +3 -1
  35. data/lib/bettercap/proxy/{ssl → http/ssl}/bettercap-ca.pem +0 -0
  36. data/lib/bettercap/proxy/{ssl → http/ssl}/server.rb +3 -1
  37. data/lib/bettercap/proxy/{sslstrip → http/sslstrip}/cookiemonitor.rb +2 -0
  38. data/lib/bettercap/proxy/{sslstrip → http/sslstrip}/lock.ico +0 -0
  39. data/lib/bettercap/proxy/{sslstrip → http/sslstrip}/strip.rb +4 -2
  40. data/lib/bettercap/proxy/{streamer.rb → http/streamer.rb} +7 -4
  41. data/lib/bettercap/proxy/stream_logger.rb +25 -9
  42. data/lib/bettercap/proxy/tcp/module.rb +75 -0
  43. data/lib/bettercap/proxy/tcp/proxy.rb +123 -0
  44. data/lib/bettercap/proxy/thread_pool.rb +1 -1
  45. data/lib/bettercap/sniffer/parsers/post.rb +1 -1
  46. data/lib/bettercap/sniffer/parsers/url.rb +1 -1
  47. data/lib/bettercap/sniffer/sniffer.rb +23 -17
  48. data/lib/bettercap/spoofers/arp.rb +21 -9
  49. data/lib/bettercap/spoofers/base.rb +12 -16
  50. data/lib/bettercap/spoofers/icmp.rb +4 -5
  51. data/lib/bettercap/spoofers/none.rb +0 -1
  52. data/lib/bettercap/version.rb +1 -1
  53. metadata +48 -19
  54. data/lib/bettercap/firewalls/openbsd.rb +0 -77
  55. data/lib/bettercap/options.rb +0 -600
@@ -0,0 +1,75 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+
4
+ BETTERCAP
5
+
6
+ Author : Simone 'evilsocket' Margaritelli
7
+ Email : evilsocket@gmail.com
8
+ Blog : http://www.evilsocket.net/
9
+
10
+ This project is released under the GPL 3 license.
11
+
12
+ =end
13
+
14
+ module BetterCap
15
+ module Proxy
16
+ module TCP
17
+
18
+ # Base class for transparent TCP proxy modules, example:
19
+ #
20
+ # class SampleModule < BetterCap::Proxy::TCP::Module
21
+ # def on_data( event )
22
+ # event.data = 'aaa'
23
+ # end
24
+ #
25
+ # def on_response( event )
26
+ # event.data = 'bbb'
27
+ # end
28
+ # end
29
+ class Module
30
+ # This callback is called when the target is sending data to the upstream server.
31
+ # +event+ is an instance of the BetterCap::Proxy::TCP::Event class.
32
+ def on_data( event ); end
33
+ # This callback is called when the upstream server is sending data to the target.
34
+ # +event+ is an instance of the BetterCap::Proxy::TCP::Event class.
35
+ def on_response( event ); end
36
+ # This callback is called when the connection is terminated.
37
+ # +event+ is an instance of the BetterCap::Proxy::TCP::Event class.
38
+ def on_finish( event ); end
39
+
40
+ # Loaded modules.
41
+ @@loaded = {}
42
+
43
+ class << self
44
+ # Called when a class inherits this base class.
45
+ def inherited(subclass)
46
+ name = subclass.name.upcase
47
+ @@loaded[name] = subclass
48
+ end
49
+
50
+ # Load +file+ as a proxy module.
51
+ def load( file )
52
+ begin
53
+ require file
54
+ rescue LoadError
55
+ raise BetterCap::Error, "Invalid TCP proxy module specified."
56
+ end
57
+
58
+ @@loaded.each do |name,mod|
59
+ @@loaded[name] = mod.new
60
+ end
61
+ end
62
+
63
+ # Execute method +even_name+ for each loaded module instance using +event+
64
+ # as its argument.
65
+ def dispatch( event_name, event )
66
+ @@loaded.each do |name,mod|
67
+ mod.send( event_name, event )
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,123 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+
4
+ BETTERCAP
5
+
6
+ Author : Simone 'evilsocket' Margaritelli
7
+ Email : evilsocket@gmail.com
8
+ Blog : http://www.evilsocket.net/
9
+
10
+ This project is released under the GPL 3 license.
11
+
12
+ =end
13
+
14
+ module BetterCap
15
+ module Proxy
16
+ module TCP
17
+
18
+ # Class used to encapsulate ( and keep references ) of a single TCP event-.
19
+ # http://stackoverflow.com/questions/161510/pass-parameter-by-reference-in-ruby
20
+ class Event
21
+ # The source IP address of this event.
22
+ attr_accessor :ip
23
+ # Source port.
24
+ attr_accessor :port
25
+ # Reference to the buffer being transmitted.
26
+ attr_accessor :data
27
+
28
+ def initialize( ip, port, data = nil )
29
+ @ip = ip
30
+ @port = port
31
+ @data = data
32
+ end
33
+ end
34
+
35
+ # Transparent TCP proxy class.
36
+ class Proxy
37
+ # Initialize the TCP proxy with given arguments.
38
+ def initialize( address, port, up_address, up_port )
39
+ @address = address
40
+ @port = port
41
+ @upstream_address = up_address
42
+ @upstream_port = up_port
43
+ @ctx = BetterCap::Context.get
44
+ @worker = nil
45
+ end
46
+
47
+ # Start the proxy.
48
+ def start
49
+ Logger.info "[#{'TCP PROXY'.green}] Starting on #{@address}:#{@port} ( -> #{@upstream_address}:#{@upstream_port} ) ..."
50
+ @worker = Thread.new &method(:worker)
51
+ # If the upstream server is in this network, we need to make sure that its MAC
52
+ # address is discovered and put in the ARP cache before we even start the proxy,
53
+ # otherwise internal connections won't be spoofed and the proxy will be useless
54
+ # until some random event will fill the ARP cache for the server.
55
+ if @ctx.ifconfig[:ip4_obj].include?( @upstream_address )
56
+ Logger.debug "[#{'TCP PROXY'.green}] Sending probe to upstream server address ..."
57
+ BetterCap::Network.get_hw_address( @ctx, @upstream_address )
58
+ # wait for the system to acknowledge the ARP cache changes.
59
+ sleep( 1 )
60
+ end
61
+ end
62
+
63
+ # Stop the proxy.
64
+ def stop
65
+ Logger.info "Stopping TCP proxy ..."
66
+ ::Proxy.stop
67
+ @worker.join
68
+ end
69
+
70
+ private
71
+
72
+ def worker
73
+ begin
74
+ up_addr = @upstream_address
75
+ up_port = @upstream_port
76
+ up_svc = BetterCap::StreamLogger.service( :tcp, @upstream_port )
77
+
78
+ ::Proxy.start(:host => @address, :port => @port) do |conn|
79
+ conn.server :srv, :host => up_addr, :port => up_port
80
+
81
+ # ip -> upstream
82
+ conn.on_data do |data|
83
+ ip, port = peer
84
+ event = Event.new( ip, port, data )
85
+
86
+ Logger.info "[#{'TCP PROXY'.green}] #{ip} #{'->'.green} #{'upstream'.yellow}:#{up_svc} ( #{event.data.bytesize} bytes )"
87
+
88
+ Module.dispatch( 'on_data', event )
89
+ event.data
90
+ end
91
+
92
+ # upstream -> ip
93
+ conn.on_response do |backend, resp|
94
+ ip, port = peer
95
+ event = Event.new( ip, port, resp )
96
+
97
+ Logger.info "[#{'TCP PROXY'.green}] #{'upstream'.yellow}:#{up_svc} #{'->'.green} #{ip} ( #{event.data.bytesize} bytes )"
98
+
99
+ Module.dispatch( 'on_response', event )
100
+ event.data
101
+ end
102
+
103
+ # termination
104
+ conn.on_finish do |backend, name|
105
+ ip, port = peer
106
+ event = Event.new( ip, port )
107
+
108
+ Logger.info "[#{'TCP PROXY'.green}] #{ip} <- #{'closed'.red} -> #{'upstream'.yellow}:#{up_svc}"
109
+
110
+ Module.dispatch( 'on_finish', event )
111
+ unbind
112
+ end
113
+ end
114
+ rescue Exception => e
115
+ Logger.error e.message
116
+ Logger.exception e
117
+ end
118
+ end
119
+ end
120
+
121
+ end
122
+ end
123
+ end
@@ -13,7 +13,7 @@ This project is released under the GPL 3 license.
13
13
 
14
14
  module BetterCap
15
15
  module Proxy
16
- # Thread pool class used by the BetterCap::Proxy::Proxy.
16
+ # Thread pool class used by the BetterCap::Proxy::*.
17
17
  # Tnx to Puma ThreadPool!
18
18
  class ThreadPool
19
19
 
@@ -19,7 +19,7 @@ class Post < Base
19
19
  s = pkt.to_s
20
20
  if s =~ /POST\s+[^\s]+\s+HTTP.+/
21
21
  begin
22
- req = BetterCap::Proxy::Request.parse(pkt.payload)
22
+ req = BetterCap::Proxy::HTTP::Request.parse(pkt.payload)
23
23
  # the packet could be incomplete
24
24
  unless req.body.nil? or req.body.empty?
25
25
  StreamLogger.log_raw( pkt, "POST", req.to_url(1000) )
@@ -20,7 +20,7 @@ class Url < Base
20
20
  if s =~ /GET\s+([^\s]+)\s+HTTP.+Host:\s+([^\s]+).+/m
21
21
  host = $2
22
22
  url = $1
23
- if not url =~ /.+\.(png|jpg|jpeg|bmp|gif|img|ttf|woff|css|js).*/i
23
+ unless url =~ /.+\.(png|jpg|jpeg|bmp|gif|img|ttf|woff|css|js).*/i
24
24
  StreamLogger.log_raw( pkt, 'GET', "http://#{host}#{url}" )
25
25
  end
26
26
  end
@@ -31,7 +31,7 @@ class Sniffer
31
31
 
32
32
  setup( ctx )
33
33
 
34
- start = Time.now.to_i
34
+ start = Time.now
35
35
  skipped = 0
36
36
  processed = 0
37
37
 
@@ -52,11 +52,11 @@ class Sniffer
52
52
  end
53
53
  end
54
54
 
55
- stop = Time.now.to_i
56
- delta = stop - start
55
+ stop = Time.now
56
+ delta = ( stop - start ) * 1000.0
57
57
  total = skipped + processed
58
58
 
59
- Logger.info "[#{'SNIFFER'.green}] #{total} packets processed in #{delta} s ( #{skipped} skipped packets, #{processed} processed packets )"
59
+ Logger.info "[#{'SNIFFER'.green}] #{total} packets processed in #{delta} ms ( #{skipped} skipped packets, #{processed} processed packets )"
60
60
  }
61
61
  end
62
62
 
@@ -64,13 +64,19 @@ class Sniffer
64
64
 
65
65
  # Return the current PCAP stream.
66
66
  def self.stream
67
- if @@ctx.options.sniffer_src.nil?
68
- @@cap.stream
67
+ if @@ctx.options.sniff.src.nil?
68
+ return @@cap.stream
69
69
  else
70
- Logger.info "[#{'SNIFFER'.green}] Reading packets from #{@@ctx.options.sniffer_src} ..."
70
+ Logger.info "[#{'SNIFFER'.green}] Reading packets from #{@@ctx.options.sniff.src} ..."
71
71
 
72
- PacketFu::PcapFile.file_to_array @@ctx.options.sniffer_src
72
+ begin
73
+ return PacketFu::PcapFile.file_to_array @@ctx.options.sniff.src
74
+ rescue Exception => e
75
+ Logger.error "Error while parsing #{@@ctx.options.sniff.src}: #{e.message}"
76
+ Logger.exception e
77
+ end
73
78
  end
79
+ return []
74
80
  end
75
81
 
76
82
  # Return true if the +pkt+ packet instance must be skipped.
@@ -81,7 +87,7 @@ class Sniffer
81
87
  # not IP packet
82
88
  return true unless pkt.is_ip?
83
89
  # skip if local packet and --local|-L was not specified.
84
- unless @@ctx.options.local
90
+ unless @@ctx.options.sniff.local
85
91
  return ( pkt.ip_saddr == @@ctx.ifconfig[:ip_saddr] or pkt.ip_daddr == @@ctx.ifconfig[:ip_saddr] )
86
92
  end
87
93
  rescue; end
@@ -103,7 +109,7 @@ class Sniffer
103
109
  def self.append_packet( p )
104
110
  begin
105
111
  @@pcap.array_to_file(
106
- filename: @@ctx.options.sniffer_pcap,
112
+ filename: @@ctx.options.sniff.output,
107
113
  array: [p],
108
114
  append: true ) unless @@pcap.nil?
109
115
  rescue Exception => e
@@ -115,20 +121,20 @@ class Sniffer
115
121
  def self.setup( ctx )
116
122
  @@ctx = ctx
117
123
 
118
- unless @@ctx.options.sniffer_pcap.nil?
124
+ unless @@ctx.options.sniff.output.nil?
119
125
  @@pcap = PacketFu::PcapFile.new
120
- Logger.info "[#{'SNIFFER'.green}] Saving packets to #{@@ctx.options.sniffer_pcap} ."
126
+ Logger.info "[#{'SNIFFER'.green}] Saving packets to #{@@ctx.options.sniff.output} ."
121
127
  end
122
128
 
123
- if @@ctx.options.custom_parser.nil?
124
- @@parsers = Parsers::Base.load_by_names @@ctx.options.parsers
129
+ if @@ctx.options.sniff.custom_parser.nil?
130
+ @@parsers = Parsers::Base.load_by_names @@ctx.options.sniff.parsers
125
131
  else
126
- @@parsers = Parsers::Base.load_custom @@ctx.options.custom_parser
132
+ @@parsers = Parsers::Base.load_custom @@ctx.options.sniff.custom_parser
127
133
  end
128
134
 
129
135
  @@cap = Capture.new(
130
- iface: @@ctx.options.iface,
131
- filter: @@ctx.options.sniffer_filter,
136
+ iface: @@ctx.options.core.iface,
137
+ filter: @@ctx.options.sniff.filter,
132
138
  start: true
133
139
  )
134
140
  end
@@ -18,7 +18,6 @@ class Arp < Base
18
18
  # Initialize the BetterCap::Spoofers::Arp object.
19
19
  def initialize
20
20
  @ctx = Context.get
21
- @gateway = nil
22
21
  @forwarding = @ctx.firewall.forwarding_enabled?
23
22
  @spoof_thread = nil
24
23
  @sniff_thread = nil
@@ -46,12 +45,12 @@ class Arp < Base
46
45
 
47
46
  # Start the ARP spoofing.
48
47
  def start
49
- Logger.debug "Starting ARP spoofer ( #{@ctx.options.half_duplex ? 'Half' : 'Full'} Duplex ) ..."
48
+ Logger.debug "Starting ARP spoofer ( #{@ctx.options.spoof.half_duplex ? 'Half' : 'Full'} Duplex ) ..."
50
49
 
51
50
  stop() if @running
52
51
  @running = true
53
52
 
54
- if @ctx.options.kill
53
+ if @ctx.options.spoof.kill
55
54
  Logger.warn "Disabling packet forwarding."
56
55
  @ctx.firewall.enable_forwarding(false) if @forwarding
57
56
  else
@@ -77,7 +76,7 @@ class Arp < Base
77
76
  Logger.debug "Restoring ARP table of #{@ctx.targets.size} targets ..."
78
77
 
79
78
  @ctx.targets.each do |target|
80
- unless target.ip.nil? or target.mac.nil?
79
+ if target.spoofable?
81
80
  5.times do
82
81
  spoof(target, true)
83
82
  sleep 0.3
@@ -96,18 +95,31 @@ class Arp < Base
96
95
  # restore its ARP cache instead.
97
96
  def spoof( target, restore = false )
98
97
  if restore
99
- send_spoofed_packet( @gateway.ip, @gateway.mac, target.ip, 'ff:ff:ff:ff:ff:ff' )
100
- send_spoofed_packet( target.ip, target.mac, @gateway.ip, 'ff:ff:ff:ff:ff:ff' ) unless @ctx.options.half_duplex
98
+ send_spoofed_packet( @ctx.gateway.ip, @ctx.gateway.mac, target.ip, 'ff:ff:ff:ff:ff:ff' )
99
+ send_spoofed_packet( target.ip, target.mac, @ctx.gateway.ip, 'ff:ff:ff:ff:ff:ff' ) unless @ctx.options.spoof.half_duplex
100
+ @ctx.targets.each do |e|
101
+ if e.spoofable? and e.ip != target.ip and e.ip != @ctx.gateway.ip
102
+ send_spoofed_packet( e.ip, e.mac, target.ip, 'ff:ff:ff:ff:ff:ff' )
103
+ end
104
+ end
101
105
  else
102
- send_spoofed_packet( @gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
103
- send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
106
+ # tell the target we're the gateway
107
+ send_spoofed_packet( @ctx.gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
108
+ # tell the gateway we're the target
109
+ send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @ctx.gateway.ip, @ctx.gateway.mac ) unless @ctx.options.spoof.half_duplex
110
+ # tell the target we're everybody else in the network :D
111
+ @ctx.targets.each do |e|
112
+ if e.spoofable? and e.ip != target.ip and e.ip != @ctx.gateway.ip
113
+ send_spoofed_packet( e.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
114
+ end
115
+ end
104
116
  end
105
117
  end
106
118
 
107
119
  # Main spoofer loop.
108
120
  def arp_spoofer
109
121
  spoof_loop(1) { |target|
110
- unless target.ip.nil? or target.mac.nil?
122
+ if target.spoofable?
111
123
  spoof(target)
112
124
  end
113
125
  }
@@ -57,7 +57,7 @@ private
57
57
  def sniff_packets( filter )
58
58
  begin
59
59
  @capture = PacketFu::Capture.new(
60
- iface: @ctx.options.iface,
60
+ iface: @ctx.options.core.iface,
61
61
  filter: filter,
62
62
  start: true
63
63
  )
@@ -67,10 +67,10 @@ private
67
67
 
68
68
  @capture.stream.each do |p|
69
69
  begin
70
- if not @running
71
- Logger.debug 'Stopping thread ...'
72
- Thread.exit
73
- break
70
+ unless @running
71
+ Logger.debug 'Stopping thread ...'
72
+ Thread.exit
73
+ break
74
74
  end
75
75
 
76
76
  pkt = PacketFu::Packet.parse p rescue nil
@@ -92,7 +92,7 @@ private
92
92
  break
93
93
  end
94
94
 
95
- # Logger.debug "Spoofing #{@ctx.targets.size} targets ..."
95
+ Logger.debug "Spoofing #{@ctx.targets.size} targets ..."
96
96
 
97
97
  update_targets!
98
98
 
@@ -106,17 +106,13 @@ private
106
106
 
107
107
  # Get the MAC address of the gateway and update it.
108
108
  def update_gateway!
109
- hw = Network.get_hw_address( @ctx, @ctx.gateway )
110
-
111
- raise BetterCap::Error, "Couldn't determine router MAC" if ( @ctx.need_gateway? and hw.nil? )
112
-
113
- @gateway = Network::Target.new( @ctx.gateway, hw )
114
-
115
- # notify the system that the gateway mac is resolved, this will prevent
116
- # the gateway ip to be unnecessarily probed from discovery agents.
117
- @ctx.gateway_mac_resolved = !hw.nil?
109
+ unless @ctx.gateway.spoofable?
110
+ hw = Network.get_hw_address( @ctx, @ctx.gateway.ip )
111
+ raise BetterCap::Error, "Couldn't determine router MAC" if ( @ctx.options.need_gateway? and hw.nil? )
112
+ @ctx.gateway.mac = hw unless hw.nil?
113
+ end
118
114
 
119
- Logger.info "[#{'GATEWAY'.green}] #{@gateway.to_s(false)}"
115
+ Logger.info "[#{'GATEWAY'.green}] #{@ctx.gateway.to_s(false)}"
120
116
  end
121
117
 
122
118
  # Update each target that needs to be updated.
@@ -79,7 +79,6 @@ class Icmp < Base
79
79
  def initialize
80
80
  @ctx = Context.get
81
81
  @forwarding = @ctx.firewall.forwarding_enabled?
82
- @gateway = nil
83
82
  @local = @ctx.ifconfig[:ip_saddr]
84
83
  @spoof_thread = nil
85
84
  @watch_thread = nil
@@ -93,12 +92,12 @@ class Icmp < Base
93
92
  # Send ICMP redirect to the +target+, redirecting the gateway ip and
94
93
  # everything in the @entries list of addresses to us.
95
94
  def send_spoofed_packet( target )
96
- ( [@gateway.ip] + @entries ).each do |address|
95
+ ( [@ctx.gateway.ip] + @entries ).each do |address|
97
96
  begin
98
97
  Logger.debug "Sending ICMP Redirect to #{target.to_s_compact} redirecting #{address} to us ..."
99
98
 
100
99
  pkt = ICMPRedirectPacket.new
101
- pkt.update!( @gateway, target, @local, address )
100
+ pkt.update!( @ctx.gateway, target, @local, address )
102
101
  @ctx.packets.push(pkt)
103
102
  rescue Exception => e
104
103
  Logger.debug "#{self.class.name} : #{e.message}"
@@ -113,7 +112,7 @@ class Icmp < Base
113
112
  stop() if @running
114
113
  @running = true
115
114
 
116
- if @ctx.options.kill
115
+ if @ctx.options.spoof.kill
117
116
  Logger.warn "Disabling packet forwarding."
118
117
  @ctx.firewall.enable_forwarding(false) if @forwarding
119
118
  else
@@ -194,7 +193,7 @@ class Icmp < Base
194
193
  # Main spoofer loop.
195
194
  def icmp_spoofer
196
195
  spoof_loop(3) { |target|
197
- unless target.ip.nil? or target.mac.nil?
196
+ if target.spoofable?
198
197
  send_spoofed_packet target
199
198
  end
200
199
  }