bettercap 1.1.10 → 1.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.md +8 -7
  3. data/bin/bettercap +8 -2
  4. data/lib/bettercap.rb +5 -4
  5. data/lib/bettercap/context.rb +54 -8
  6. data/lib/bettercap/discovery/agents/arp.rb +17 -4
  7. data/lib/bettercap/discovery/agents/base.rb +16 -52
  8. data/lib/bettercap/discovery/agents/icmp.rb +25 -17
  9. data/lib/bettercap/discovery/agents/udp.rb +9 -20
  10. data/lib/bettercap/discovery/{discovery.rb → thread.rb} +10 -9
  11. data/lib/bettercap/error.rb +1 -2
  12. data/lib/bettercap/factories/{firewall_factory.rb → firewall.rb} +11 -7
  13. data/lib/bettercap/factories/{parser_factory.rb → parser.rb} +13 -3
  14. data/lib/bettercap/factories/{spoofer_factory.rb → spoofer.rb} +10 -3
  15. data/lib/bettercap/firewalls/base.rb +76 -0
  16. data/lib/bettercap/firewalls/linux.rb +26 -16
  17. data/lib/bettercap/firewalls/osx.rb +22 -13
  18. data/lib/bettercap/firewalls/redirection.rb +15 -1
  19. data/lib/bettercap/httpd/server.rb +5 -0
  20. data/lib/bettercap/logger.rb +29 -8
  21. data/lib/bettercap/network.rb +105 -105
  22. data/lib/bettercap/options.rb +99 -41
  23. data/lib/bettercap/packet_queue.rb +92 -0
  24. data/lib/bettercap/proxy/certstore.rb +49 -43
  25. data/lib/bettercap/proxy/module.rb +4 -2
  26. data/lib/bettercap/proxy/proxy.rb +7 -2
  27. data/lib/bettercap/proxy/request.rb +28 -16
  28. data/lib/bettercap/proxy/response.rb +23 -2
  29. data/lib/bettercap/proxy/stream_logger.rb +6 -0
  30. data/lib/bettercap/proxy/streamer.rb +13 -5
  31. data/lib/bettercap/proxy/thread_pool.rb +6 -14
  32. data/lib/bettercap/shell.rb +5 -3
  33. data/lib/bettercap/sniffer/parsers/base.rb +7 -1
  34. data/lib/bettercap/sniffer/parsers/custom.rb +6 -1
  35. data/lib/bettercap/sniffer/parsers/ftp.rb +8 -5
  36. data/lib/bettercap/sniffer/parsers/httpauth.rb +4 -1
  37. data/lib/bettercap/sniffer/parsers/https.rb +4 -1
  38. data/lib/bettercap/sniffer/parsers/irc.rb +8 -5
  39. data/lib/bettercap/sniffer/parsers/mail.rb +8 -5
  40. data/lib/bettercap/sniffer/parsers/ntlmss.rb +21 -18
  41. data/lib/bettercap/sniffer/parsers/post.rb +4 -1
  42. data/lib/bettercap/sniffer/parsers/url.rb +4 -1
  43. data/lib/bettercap/sniffer/sniffer.rb +7 -3
  44. data/lib/bettercap/spoofers/arp.rb +69 -94
  45. data/lib/bettercap/spoofers/base.rb +132 -0
  46. data/lib/bettercap/spoofers/icmp.rb +200 -0
  47. data/lib/bettercap/spoofers/none.rb +8 -2
  48. data/lib/bettercap/target.rb +117 -90
  49. data/lib/bettercap/update_checker.rb +6 -0
  50. data/lib/bettercap/version.rb +3 -1
  51. metadata +24 -8
  52. data/lib/bettercap/base/ifirewall.rb +0 -46
  53. data/lib/bettercap/base/ispoofer.rb +0 -32
@@ -11,9 +11,10 @@ This project is released under the GPL 3 license.
11
11
  =end
12
12
  require 'thread'
13
13
 
14
- # Tnx to Puma ThreadPool!
15
14
  module BetterCap
16
15
  module Proxy
16
+ # Thread pool class used by the BetterCap::Proxy::Proxy.
17
+ # Tnx to Puma ThreadPool!
17
18
  class ThreadPool
18
19
 
19
20
  # Maintain a minimum of +min+ and maximum of +max+ threads
@@ -21,7 +22,6 @@ class ThreadPool
21
22
  #
22
23
  # The block passed is the work that will be performed in each
23
24
  # thread.
24
- #
25
25
  def initialize(min, max, *extra, &block)
26
26
  @not_empty = ConditionVariable.new
27
27
  @not_full = ConditionVariable.new
@@ -46,12 +46,10 @@ class ThreadPool
46
46
  @mutex.synchronize do
47
47
  @min.times { spawn_thread }
48
48
  end
49
-
50
- @clean_thread_locals = false
51
49
  end
52
50
 
53
- attr_reader :spawned, :trim_requested
54
- attr_accessor :clean_thread_locals
51
+ # Number of spawned threads in the pool.
52
+ attr_reader :spawned
55
53
 
56
54
  # How many objects have yet to be processed by the pool?
57
55
  #
@@ -104,12 +102,6 @@ class ThreadPool
104
102
 
105
103
  break unless continue
106
104
 
107
- if @clean_thread_locals
108
- Thread.current.keys.each do |key|
109
- Thread.current[key] = nil unless key == :__recursive_key__
110
- end
111
- end
112
-
113
105
  begin
114
106
  block.call(work, *extra)
115
107
  rescue Exception
@@ -184,7 +176,7 @@ class ThreadPool
184
176
 
185
177
  # Tell all threads in the pool to exit and wait for them to finish.
186
178
  #
187
- def shutdown
179
+ def shutdown( join_threads = true )
188
180
  threads = @mutex.synchronize do
189
181
  @shutdown = true
190
182
  @not_empty.broadcast
@@ -193,7 +185,7 @@ class ThreadPool
193
185
  @workers.dup
194
186
  end
195
187
 
196
- threads.each(&:join)
188
+ threads.each(&:join) if join_threads
197
189
 
198
190
  @spawned = 0
199
191
  @workers = []
@@ -8,10 +8,11 @@ This project is released under the GPL 3 license.
8
8
  require 'bettercap/error'
9
9
 
10
10
  module BetterCap
11
+ # Class responsible of executing various shell commands.
11
12
  module Shell
12
13
  class << self
13
-
14
- #return the output of command
14
+ # Execute +command+ and return its output.
15
+ # Raise +BetterCap::Error+ if the return code is not 0.
15
16
  def execute(command)
16
17
  r = ''
17
18
  10.times do
@@ -29,14 +30,15 @@ module Shell
29
30
  r
30
31
  end
31
32
 
33
+ # Get the +iface+ network interface configuration.
32
34
  def ifconfig(iface = '')
33
35
  self.execute( "LANG=en && ifconfig #{iface}" )
34
36
  end
35
37
 
38
+ # Get the ARP table cached on this computer.
36
39
  def arp
37
40
  self.execute( 'LANG=en && arp -a -n' )
38
41
  end
39
-
40
42
  end
41
43
  end
42
44
  end
@@ -10,12 +10,17 @@ This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
12
  module BetterCap
13
- class BaseParser
13
+ module Parsers
14
+ # Base class for BetterCap::Parsers.
15
+ class Base
16
+ # Initialize this parser.
14
17
  def initialize
15
18
  @filters = []
16
19
  @name = 'BASE'
17
20
  end
18
21
 
22
+ # This method will be called from the BetterCap::Sniffer for each
23
+ # incoming packet ( +pkt ) and will apply the parser filter to it.
19
24
  def on_packet( pkt )
20
25
  s = pkt.to_s
21
26
  @filters.each do |filter|
@@ -26,3 +31,4 @@ class BaseParser
26
31
  end
27
32
  end
28
33
  end
34
+ end
@@ -12,10 +12,15 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
14
  module BetterCap
15
- class CustomParser < BaseParser
15
+ module Parsers
16
+ # Parser used when the "--custom-parser EXPRESSION" command line
17
+ # argument is specified.
18
+ class Custom < Base
19
+ # Initialize the parser given the +filter+ Regexp object.
16
20
  def initialize( filter )
17
21
  @filters = [ filter ]
18
22
  @name = 'DATA'
19
23
  end
20
24
  end
21
25
  end
26
+ end
@@ -12,10 +12,13 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
14
  module BetterCap
15
- class FtpParser < BaseParser
16
- def initialize
17
- @filters = [ /(USER|PASS)\s+.+/ ]
18
- @name = 'FTP'
19
- end
15
+ module Parsers
16
+ # FTP authentication parser.
17
+ class Ftp < Base
18
+ def initialize
19
+ @filters = [ /(USER|PASS)\s+.+/ ]
20
+ @name = 'FTP'
21
+ end
22
+ end
20
23
  end
21
24
  end
@@ -14,7 +14,9 @@ require 'colorize'
14
14
  require 'base64'
15
15
 
16
16
  module BetterCap
17
- class HttpauthParser < BaseParser
17
+ module Parsers
18
+ # HTTP basic and digest authentication parser.
19
+ class Httpauth < Base
18
20
  def on_packet( pkt )
19
21
  lines = pkt.to_s.split("\n")
20
22
  hostname = nil
@@ -41,3 +43,4 @@ class HttpauthParser < BaseParser
41
43
  end
42
44
  end
43
45
  end
46
+ end
@@ -14,7 +14,9 @@ require 'colorize'
14
14
  require 'resolv'
15
15
 
16
16
  module BetterCap
17
- class HttpsParser < BaseParser
17
+ module Parsers
18
+ # HTTPS connections parser.
19
+ class Https < Base
18
20
  @@prev = nil
19
21
 
20
22
  def on_packet( pkt )
@@ -39,3 +41,4 @@ class HttpsParser < BaseParser
39
41
  end
40
42
  end
41
43
  end
44
+ end
@@ -12,10 +12,13 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
14
  module BetterCap
15
- class IrcParser < BaseParser
16
- def initialize
17
- @filters = [ /NICK\s+.+/, /NS IDENTIFY\s+.+/, /nickserv :identify\s+.+/ ]
18
- @name = 'IRC'
19
- end
15
+ module Parsers
16
+ # IRC protocol parser.
17
+ class Irc < Base
18
+ def initialize
19
+ @filters = [ /NICK\s+.+/, /NS IDENTIFY\s+.+/, /nickserv :identify\s+.+/ ]
20
+ @name = 'IRC'
21
+ end
22
+ end
20
23
  end
21
24
  end
@@ -12,10 +12,13 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/sniffer/parsers/base'
13
13
 
14
14
  module BetterCap
15
- class MailParser < BaseParser
16
- def initialize
17
- @filters = [ /(\d+ )?(auth|authenticate) ([a-z\-_0-9]+)/i ]
18
- @name = 'MAIL'
19
- end
15
+ module Parsers
16
+ # POP/IMAP authentication parser.
17
+ class Mail < Base
18
+ def initialize
19
+ @filters = [ /(\d+ )?(auth|authenticate) ([a-z\-_0-9]+)/i ]
20
+ @name = 'MAIL'
21
+ end
22
+ end
20
23
  end
21
24
  end
@@ -13,26 +13,29 @@ require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
15
  module BetterCap
16
- class NtlmssParser < BaseParser
17
- def bin2hex( data )
18
- hex = ''
19
- data.each_byte do |byte|
20
- if /[[:print:]]/ === byte.chr
21
- hex += byte.chr
22
- else
23
- hex += "\\x" + byte.to_s(16)
24
- end
25
- end
26
-
27
- hex
16
+ module Parsers
17
+ # NTLMSS traffic parser.
18
+ class Ntlmss < Base
19
+ # Convert binary +data+ into human readable hexadecimal representation.
20
+ def bin2hex( data )
21
+ hex = ''
22
+ data.each_byte do |byte|
23
+ if /[[:print:]]/ === byte.chr
24
+ hex += byte.chr
25
+ else
26
+ hex += "\\x" + byte.to_s(16)
27
+ end
28
28
  end
29
+ hex
30
+ end
29
31
 
30
- def on_packet( pkt )
31
- s = pkt.to_s
32
- if s =~ /NTLMSSP\x00\x03\x00\x00\x00.+/
33
- # TODO: Parse NTLMSSP packet.
34
- StreamLogger.log_raw( pkt, 'NTLMSS', bin2hex( pkt.payload ) )
35
- end
32
+ def on_packet( pkt )
33
+ s = pkt.to_s
34
+ if s =~ /NTLMSSP\x00\x03\x00\x00\x00.+/
35
+ # TODO: Parse NTLMSSP packet.
36
+ StreamLogger.log_raw( pkt, 'NTLMSS', bin2hex( pkt.payload ) )
36
37
  end
38
+ end
39
+ end
37
40
  end
38
41
  end
@@ -13,7 +13,9 @@ require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
15
  module BetterCap
16
- class PostParser < BaseParser
16
+ module Parsers
17
+ # HTTP POST requests parser.
18
+ class Post < Base
17
19
  def on_packet( pkt )
18
20
  s = pkt.to_s
19
21
  if s =~ /POST\s+[^\s]+\s+HTTP.+/
@@ -22,3 +24,4 @@ class PostParser < BaseParser
22
24
  end
23
25
  end
24
26
  end
27
+ end
@@ -13,7 +13,9 @@ require 'bettercap/sniffer/parsers/base'
13
13
  require 'colorize'
14
14
 
15
15
  module BetterCap
16
- class UrlParser < BaseParser
16
+ module Parsers
17
+ # HTTP GET requests parser.
18
+ class Url < Base
17
19
  def on_packet( pkt )
18
20
  s = pkt.to_s
19
21
  if s =~ /GET\s+([^\s]+)\s+HTTP.+Host:\s+([^\s]+).+/m
@@ -26,3 +28,4 @@ class UrlParser < BaseParser
26
28
  end
27
29
  end
28
30
  end
31
+ end
@@ -10,11 +10,12 @@ This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
12
  require 'bettercap/logger'
13
- require 'bettercap/factories/parser_factory'
14
13
  require 'colorize'
15
14
  require 'packetfu'
16
15
 
17
16
  module BetterCap
17
+ # Class responsible of loading BetterCap::Parsers instances and performing
18
+ # network packet sniffing and dumping.
18
19
  class Sniffer
19
20
  include PacketFu
20
21
 
@@ -23,6 +24,9 @@ class Sniffer
23
24
  @@pcap = nil
24
25
  @@cap = nil
25
26
 
27
+ # Start a new thread that will sniff packets from the network and pass
28
+ # each one of them to the BetterCap::Parsers instances loaded inside the
29
+ # +ctx+ BetterCap::Context instance.
26
30
  def self.start( ctx )
27
31
  Thread.new do
28
32
  Logger.info 'Starting sniffer ...'
@@ -94,9 +98,9 @@ class Sniffer
94
98
  end
95
99
 
96
100
  if @@ctx.options.custom_parser.nil?
97
- @@parsers = ParserFactory.load_by_names @@ctx.options.parsers
101
+ @@parsers = Factories::Parser.load_by_names @@ctx.options.parsers
98
102
  else
99
- @@parsers = ParserFactory.load_custom @@ctx.options.custom_parser
103
+ @@parsers = Factories::Parser.load_custom @@ctx.options.custom_parser
100
104
  end
101
105
 
102
106
  @@cap = Capture.new(
@@ -9,15 +9,18 @@ Blog : http://www.evilsocket.net/
9
9
  This project is released under the GPL 3 license.
10
10
 
11
11
  =end
12
+ require 'bettercap/spoofers/base'
12
13
  require 'bettercap/error'
13
14
  require 'bettercap/context'
14
- require 'bettercap/base/ispoofer'
15
15
  require 'bettercap/network'
16
16
  require 'bettercap/logger'
17
17
  require 'colorize'
18
18
 
19
19
  module BetterCap
20
- class ArpSpoofer < ISpoofer
20
+ module Spoofers
21
+ # This class is responsible of performing ARP spoofing on the network.
22
+ class Arp < Base
23
+ # Initialize the BetterCap::Spoofers::Arp object.
21
24
  def initialize
22
25
  @ctx = Context.get
23
26
  @gateway = nil
@@ -27,16 +30,12 @@ class ArpSpoofer < ISpoofer
27
30
  @capture = nil
28
31
  @running = false
29
32
 
30
- Logger.info "Getting gateway #{@ctx.gateway} MAC address ..."
31
-
32
- hw = Network.get_hw_address( @ctx.ifconfig, @ctx.gateway )
33
- raise BetterCap::Error, "Couldn't determine router MAC" if hw.nil?
34
-
35
- @gateway = Target.new( @ctx.gateway, hw )
36
-
37
- Logger.info " #{@gateway}"
33
+ update_gateway!
38
34
  end
39
35
 
36
+ # Send a spoofed ARP reply to the target identified by the +daddr+ IP address
37
+ # and +dmac+ MAC address, spoofing the +saddr+ IP address and +smac+ MAC
38
+ # address as the source device.
40
39
  def send_spoofed_packet( saddr, smac, daddr, dmac )
41
40
  pkt = PacketFu::ARPPacket.new
42
41
  pkt.eth_saddr = smac
@@ -47,9 +46,10 @@ class ArpSpoofer < ISpoofer
47
46
  pkt.arp_daddr_ip = daddr
48
47
  pkt.arp_opcode = 2
49
48
 
50
- pkt.to_w(@ctx.ifconfig[:iface])
49
+ @ctx.packets.push(pkt)
51
50
  end
52
51
 
52
+ # Start the ARP spoofing.
53
53
  def start
54
54
  Logger.info "Starting ARP spoofer ( #{@ctx.options.half_duplex ? 'Half' : 'Full'} Duplex ) ..."
55
55
 
@@ -58,89 +58,11 @@ class ArpSpoofer < ISpoofer
58
58
 
59
59
  @ctx.firewall.enable_forwarding(true) unless @forwarding
60
60
 
61
- @sniff_thread = Thread.new do
62
- Logger.info 'ARP watcher started ...'
63
- begin
64
- @capture = PacketFu::Capture.new(
65
- iface: @ctx.options.iface,
66
- filter: 'arp',
67
- start: true
68
- )
69
- rescue Exception => e
70
- Logger.error e.message
71
- end
72
-
73
- @capture.stream.each do |p|
74
- begin
75
- pkt = PacketFu::Packet.parse p
76
- # we're only interested in 'who-has' packets
77
- if pkt.arp_opcode == 1 and pkt.arp_dst_mac.to_s == '00:00:00:00:00:00'
78
- is_from_us = ( pkt.arp_src_ip.to_s == @ctx.ifconfig[:ip_saddr] )
79
- unless is_from_us
80
- Logger.info "[ARP] #{pkt.arp_src_ip.to_s} is asking who #{pkt.arp_dst_ip.to_s} is."
81
-
82
- send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.ifconfig[:eth_saddr], pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
83
- end
84
- end
85
- rescue Exception => e
86
- Logger.error e.message
87
- end
88
- end
89
- end
90
-
91
- @spoof_thread = Thread.new do
92
- prev_size = @ctx.targets.size
93
- loop do
94
- if not @running
95
- Logger.debug 'Stopping spoofing thread ...'
96
- Thread.exit
97
- break
98
- end
99
-
100
- size = @ctx.targets.size
101
-
102
- if size > prev_size
103
- Logger.warn "Aquired #{size - prev_size} new targets."
104
- elsif size < prev_size
105
- Logger.warn "Lost #{prev_size - size} targets."
106
- end
107
-
108
- Logger.debug "Spoofing #{@ctx.targets.size} targets ..."
109
-
110
- @ctx.targets.each do |target|
111
- # targets could change, update mac addresses if needed
112
- if target.mac.nil?
113
- hw = Network.get_hw_address( @ctx.ifconfig, target.ip )
114
- if hw.nil?
115
- Logger.warn "Couldn't determine target #{ip} MAC!"
116
- next
117
- else
118
- Logger.info " Target MAC : #{hw}"
119
- target.mac = hw
120
- end
121
- # target was specified by MAC address
122
- elsif target.ip_refresh
123
- ip = Network.get_ip_address( @ctx, target.mac )
124
- if ip.nil?
125
- Logger.warn "Couldn't determine target #{target.mac} IP!"
126
- next
127
- else
128
- Logger.info "Target #{target.mac} IP : #{ip}" if target.ip.nil? or target.ip != ip
129
- target.ip = ip
130
- end
131
- end
132
-
133
- send_spoofed_packet( @gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
134
- send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
135
- end
136
-
137
- prev_size = @ctx.targets.size
138
-
139
- sleep(1)
140
- end
141
- end
61
+ @sniff_thread = Thread.new { arp_watcher }
62
+ @spoof_thread = Thread.new { arp_spoofer }
142
63
  end
143
64
 
65
+ # Stop the ARP spoofing, reset firewall state and restore targets ARP table.
144
66
  def stop
145
67
  raise 'ARP spoofer is not running' unless @running
146
68
 
@@ -158,14 +80,67 @@ class ArpSpoofer < ISpoofer
158
80
  Logger.info "Restoring ARP table of #{@ctx.targets.size} targets ..."
159
81
 
160
82
  @ctx.targets.each do |target|
161
- unless target.mac.nil?
83
+ unless target.ip.nil? or target.mac.nil?
162
84
  begin
163
85
  send_spoofed_packet( @gateway.ip, @gateway.mac, target.ip, target.mac )
164
86
  send_spoofed_packet( target.ip, target.mac, @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
165
87
  rescue; end
166
88
  end
167
89
  end
168
- sleep 1
169
90
  end
91
+
92
+ private
93
+
94
+ def update_targets!
95
+ @ctx.targets.each do |target|
96
+ # targets could change, update mac addresses if needed
97
+ if target.mac.nil?
98
+ hw = Network.get_hw_address( @ctx.ifconfig, target.ip )
99
+ if hw.nil?
100
+ Logger.warn "Couldn't determine target #{ip} MAC!"
101
+ next
102
+ else
103
+ Logger.info " Target MAC : #{hw}"
104
+ target.mac = hw
105
+ end
106
+ # target was specified by MAC address
107
+ elsif target.ip_refresh
108
+ ip = Network.get_ip_address( @ctx, target.mac )
109
+ if ip.nil?
110
+ Logger.warn "Couldn't determine target #{target.mac} IP!"
111
+ next
112
+ else
113
+ Logger.info "Target #{target.mac} IP : #{ip}" if target.ip.nil? or target.ip != ip
114
+ target.ip = ip
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ def arp_spoofer
121
+ spoof_loop(1) { |target|
122
+ unless target.ip.nil? or target.mac.nil?
123
+ send_spoofed_packet( @gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
124
+ send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
125
+ end
126
+ }
127
+ end
128
+
129
+ def arp_watcher
130
+ Logger.info 'ARP watcher started ...'
131
+
132
+ sniff_packets('arp') { |pkt|
133
+ # we're only interested in 'who-has' packets
134
+ if pkt.arp_opcode == 1 and pkt.arp_dst_mac.to_s == '00:00:00:00:00:00'
135
+ is_from_us = ( pkt.arp_src_ip.to_s == @ctx.ifconfig[:ip_saddr] )
136
+ unless is_from_us
137
+ Logger.info "[ARP] #{pkt.arp_src_ip.to_s} is asking who #{pkt.arp_dst_ip.to_s} is."
138
+
139
+ send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.ifconfig[:eth_saddr], pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
140
+ end
141
+ end
142
+ }
143
+ end
144
+ end
170
145
  end
171
146
  end