bettercap 1.2.0 → 1.2.1
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/README.md +1 -1
- data/bin/bettercap +14 -3
- data/lib/bettercap.rb +2 -0
- data/lib/bettercap/arp_reader.rb +78 -0
- data/lib/bettercap/context.rb +1 -3
- data/lib/bettercap/discovery/agents/arp.rb +1 -64
- data/lib/bettercap/factories/parser.rb +1 -1
- data/lib/bettercap/factories/spoofer.rb +1 -1
- data/lib/bettercap/loader.rb +23 -0
- data/lib/bettercap/network.rb +5 -5
- data/lib/bettercap/options.rb +16 -1
- data/lib/bettercap/packet_queue.rb +50 -46
- data/lib/bettercap/proxy/module.rb +19 -2
- data/lib/bettercap/sniffer/sniffer.rb +1 -1
- data/lib/bettercap/spoofers/arp.rb +10 -32
- data/lib/bettercap/spoofers/base.rb +6 -8
- data/lib/bettercap/spoofers/icmp.rb +10 -5
- data/lib/bettercap/target.rb +6 -2
- data/lib/bettercap/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14c3df538db89404723e56bde2c2a8a92446d8f9
|
4
|
+
data.tar.gz: 7b48c98712b2c2b879f6e7bfc67529627ba1668d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c35e2b12effaa67a2236427922d0f7af6d200f177304e3d2f09adb3b38db1b7c7cc6b0c7f9f6ee7f70a3d8155a99f8dedcc0351f099871819357277338e54148
|
7
|
+
data.tar.gz: 1477fda71236367e364c3e392f2b44b1edbef1d19f3a792231d0a32bc613500a9b6dea86881d699a92b1afcfe5a58a61c604963df3ad16eecc99abb2a60d3d09
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
http://www.bettercap.org/
|
4
4
|
|
5
|
-
[](http://badge.fury.io/rb/bettercap) [](https://codeclimate.com/github/evilsocket/bettercap)
|
5
|
+
[](http://badge.fury.io/rb/bettercap) [](https://codeclimate.com/github/evilsocket/bettercap) [](https://gitter.im/evilsocket/bettercap-proxy-modules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
6
6
|
---
|
7
7
|
|
8
8
|
**bettercap** is a complete, modular, portable and easily extensible **MITM** tool and framework with every kind of diagnostic
|
data/bin/bettercap
CHANGED
@@ -18,6 +18,9 @@ begin
|
|
18
18
|
puts BetterCap::BANNER.green.bold
|
19
19
|
puts "\n\n\n"
|
20
20
|
|
21
|
+
# We need this in order to report unhandled exceptions.
|
22
|
+
original_argv = ARGV.clone
|
23
|
+
|
21
24
|
# Create global context, parse command line arguments and perform basic
|
22
25
|
# error checking.
|
23
26
|
ctx = BetterCap::Options.parse!
|
@@ -78,9 +81,17 @@ rescue BetterCap::Error => e
|
|
78
81
|
BetterCap::Logger.error e.message
|
79
82
|
|
80
83
|
rescue Exception => e
|
81
|
-
|
82
|
-
BetterCap::Logger.error
|
83
|
-
|
84
|
+
puts "\n\n"
|
85
|
+
BetterCap::Logger.error "Oooops, seems like something weird occurred, please copy paste the following output " \
|
86
|
+
"and open a new issue on https://github.com/evilsocket/bettercap/issues :\n"
|
87
|
+
|
88
|
+
BetterCap::Logger.error "Platform : #{RUBY_PLATFORM}"
|
89
|
+
BetterCap::Logger.error "Ruby Version : #{RUBY_VERSION}"
|
90
|
+
BetterCap::Logger.error "BetterCap Version : #{BetterCap::VERSION}"
|
91
|
+
BetterCap::Logger.error "Command Line : #{original_argv.join(" ")}"
|
92
|
+
BetterCap::Logger.error "Exception : #{e.class}"
|
93
|
+
BetterCap::Logger.error "Message : #{e.message}"
|
94
|
+
BetterCap::Logger.error "Backtrace :\n\n #{e.backtrace.join("\n ")}\n"
|
84
95
|
|
85
96
|
ensure
|
86
97
|
# Make sure all the messages on the logger queue are printed.
|
data/lib/bettercap.rb
CHANGED
@@ -26,7 +26,9 @@ Config = RbConfig
|
|
26
26
|
|
27
27
|
require 'bettercap/update_checker'
|
28
28
|
require 'bettercap/error'
|
29
|
+
require 'bettercap/loader'
|
29
30
|
require 'bettercap/options'
|
31
|
+
require 'bettercap/arp_reader'
|
30
32
|
require 'bettercap/packet_queue'
|
31
33
|
require 'bettercap/discovery/thread'
|
32
34
|
require 'bettercap/discovery/agents/base'
|
@@ -0,0 +1,78 @@
|
|
1
|
+
=begin
|
2
|
+
BETTERCAP
|
3
|
+
Author : Simone 'evilsocket' Margaritelli
|
4
|
+
Email : evilsocket@gmail.com
|
5
|
+
Blog : http://www.evilsocket.net/
|
6
|
+
This project is released under the GPL 3 license.
|
7
|
+
=end
|
8
|
+
require 'bettercap/error'
|
9
|
+
|
10
|
+
module BetterCap
|
11
|
+
# This class is responsible for reading the computer ARP table.
|
12
|
+
class ArpReader
|
13
|
+
# Parse the current ARP cache and return a list of BetterCap::Target
|
14
|
+
# objects which are found inside it, using the +ctx+ BetterCap::Context
|
15
|
+
# instance.
|
16
|
+
def self.parse( ctx )
|
17
|
+
targets = []
|
18
|
+
self.parse_cache do |ip,mac|
|
19
|
+
if ip != ctx.gateway and ip != ctx.ifconfig[:ip_saddr]
|
20
|
+
if ctx.options.ignore_ip?(ip)
|
21
|
+
Logger.debug "Ignoring #{ip} ..."
|
22
|
+
else
|
23
|
+
# reuse Target object if it's already a known address
|
24
|
+
known = ctx.find_target ip, mac
|
25
|
+
if known.nil?
|
26
|
+
targets << Target.new( ip, mac )
|
27
|
+
else
|
28
|
+
targets << known
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
targets
|
34
|
+
end
|
35
|
+
|
36
|
+
# Parse the ARP cache searching for the given IP +address+ and return its
|
37
|
+
# MAC if found, otherwise nil.
|
38
|
+
def self.find_address( address )
|
39
|
+
self.parse_cache do |ip,mac|
|
40
|
+
if ip == address
|
41
|
+
return mac
|
42
|
+
end
|
43
|
+
end
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# Parse the ARP cache searching for the given MAC +address+ and return its
|
48
|
+
# IP if found, otherwise nil.
|
49
|
+
def self.find_mac( address )
|
50
|
+
self.parse_cache do |ip,mac|
|
51
|
+
if mac == address
|
52
|
+
return ip
|
53
|
+
end
|
54
|
+
end
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def self.parse_cache
|
61
|
+
iface = Context.get.ifconfig[:iface]
|
62
|
+
Shell.arp.split("\n").each do |line|
|
63
|
+
m = self.parse_cache_line(iface,line)
|
64
|
+
unless m.nil?
|
65
|
+
ip = m[1]
|
66
|
+
hw = Target.normalized_mac( m[2] )
|
67
|
+
if hw != 'FF:FF:FF:FF:FF:FF'
|
68
|
+
yield( ip, hw )
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.parse_cache_line( iface, line )
|
75
|
+
/[^\s]+\s+\(([0-9\.]+)\)\s+at\s+([a-f0-9:]+).+#{iface}.*/i.match(line)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/bettercap/context.rb
CHANGED
@@ -127,10 +127,8 @@ class Context
|
|
127
127
|
# is needed in order to run proxy modules.
|
128
128
|
def create_proxies
|
129
129
|
if @options.has_proxy_module?
|
130
|
-
require @options.proxy_module
|
131
|
-
|
132
130
|
Proxy::Module.register_modules
|
133
|
-
|
131
|
+
|
134
132
|
raise BetterCap::Error, "#{@options.proxy_module} is not a valid bettercap proxy module." if Proxy::Module.modules.empty?
|
135
133
|
end
|
136
134
|
|
@@ -14,73 +14,10 @@ This project is released under the GPL 3 license.
|
|
14
14
|
module BetterCap
|
15
15
|
module Discovery
|
16
16
|
module Agents
|
17
|
-
# Class responsible
|
18
|
-
# possible IP on the network.
|
17
|
+
# Class responsible of sending ARP probes to each possible IP on the network.
|
19
18
|
class Arp < Discovery::Agents::Base
|
20
|
-
# Parse the current ARP cache and return a list of BetterCap::Target
|
21
|
-
# objects which are found inside it, using the +ctx+ BetterCap::Context
|
22
|
-
# instance.
|
23
|
-
def self.parse( ctx )
|
24
|
-
targets = []
|
25
|
-
self.parse_cache do |ip,mac|
|
26
|
-
if ip != ctx.gateway and ip != ctx.ifconfig[:ip_saddr]
|
27
|
-
if ctx.options.ignore_ip?(ip)
|
28
|
-
Logger.debug "Ignoring #{ip} ..."
|
29
|
-
else
|
30
|
-
# reuse Target object if it's already a known address
|
31
|
-
known = ctx.find_target ip, mac
|
32
|
-
if known.nil?
|
33
|
-
targets << Target.new( ip, mac )
|
34
|
-
else
|
35
|
-
targets << known
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
targets
|
41
|
-
end
|
42
|
-
|
43
|
-
# Parse the ARP cache searching for the given IP +address+ and return its
|
44
|
-
# MAC if found, otherwise nil.
|
45
|
-
def self.find_address( address )
|
46
|
-
self.parse_cache do |ip,mac|
|
47
|
-
if ip == address
|
48
|
-
return mac
|
49
|
-
end
|
50
|
-
end
|
51
|
-
nil
|
52
|
-
end
|
53
|
-
|
54
|
-
# Parse the ARP cache searching for the given MAC +address+ and return its
|
55
|
-
# IP if found, otherwise nil.
|
56
|
-
def self.find_mac( address )
|
57
|
-
self.parse_cache do |ip,mac|
|
58
|
-
if mac == address
|
59
|
-
return ip
|
60
|
-
end
|
61
|
-
end
|
62
|
-
nil
|
63
|
-
end
|
64
|
-
|
65
19
|
private
|
66
20
|
|
67
|
-
def self.parse_cache
|
68
|
-
Shell.arp.split("\n").each do |line|
|
69
|
-
m = self.parse_cache_line(line)
|
70
|
-
unless m.nil?
|
71
|
-
ip = m[1]
|
72
|
-
hw = Target.normalized_mac( m[2] )
|
73
|
-
if hw != 'FF:FF:FF:FF:FF:FF'
|
74
|
-
yield( ip, hw )
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def self.parse_cache_line( line )
|
81
|
-
/[^\s]+\s+\(([0-9\.]+)\)\s+at\s+([a-f0-9:]+).+#{Context.get.ifconfig[:iface]}.*/i.match(line)
|
82
|
-
end
|
83
|
-
|
84
21
|
def get_probe( ip )
|
85
22
|
pkt = PacketFu::ARPPacket.new
|
86
23
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
=begin
|
2
|
+
BETTERCAP
|
3
|
+
Author : Simone 'evilsocket' Margaritelli
|
4
|
+
Email : evilsocket@gmail.com
|
5
|
+
Blog : http://www.evilsocket.net/
|
6
|
+
This project is released under the GPL 3 license.
|
7
|
+
=end
|
8
|
+
require 'bettercap/error'
|
9
|
+
|
10
|
+
module BetterCap
|
11
|
+
# This class is responsible for dynamically loading modules.
|
12
|
+
class Loader
|
13
|
+
# Dynamically load a class given its +name+.
|
14
|
+
# @see https://github.com/evilsocket/bettercap/issues/88
|
15
|
+
def self.load(name)
|
16
|
+
root = Kernel
|
17
|
+
name.split('::').each do |part|
|
18
|
+
root = root.const_get(part)
|
19
|
+
end
|
20
|
+
root
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/bettercap/network.rb
CHANGED
@@ -70,16 +70,16 @@ class << self
|
|
70
70
|
Logger.debug 'Using current ARP cache.'
|
71
71
|
end
|
72
72
|
|
73
|
-
|
73
|
+
ArpReader.parse ctx
|
74
74
|
end
|
75
75
|
|
76
76
|
# Return the IP address associated with the +mac+ hardware address using the
|
77
77
|
# given BetterCap::Context ( +ctx+ ).
|
78
78
|
def get_ip_address( ctx, mac )
|
79
|
-
ip =
|
79
|
+
ip = ArpReader.find_mac( mac )
|
80
80
|
if ip.nil?
|
81
81
|
start_agents( ctx )
|
82
|
-
ip =
|
82
|
+
ip = ArpReader.find_mac( mac )
|
83
83
|
end
|
84
84
|
ip
|
85
85
|
end
|
@@ -88,7 +88,7 @@ class << self
|
|
88
88
|
# the +iface+ network interface.
|
89
89
|
# The resolution will be performed for the specified number of +attempts+.
|
90
90
|
def get_hw_address( iface, ip_address, attempts = 2 )
|
91
|
-
hw_address =
|
91
|
+
hw_address = ArpReader.find_address( ip_address )
|
92
92
|
|
93
93
|
if hw_address.nil?
|
94
94
|
attempts.times do
|
@@ -135,7 +135,7 @@ class << self
|
|
135
135
|
|
136
136
|
def start_agents( ctx )
|
137
137
|
[ 'Icmp', 'Udp', 'Arp' ].each do |name|
|
138
|
-
|
138
|
+
BetterCap::Loader.load("BetterCap::Discovery::Agents::#{name}").new(ctx)
|
139
139
|
end
|
140
140
|
ctx.packets.wait_empty( ctx.timeout )
|
141
141
|
end
|
data/lib/bettercap/options.rb
CHANGED
@@ -80,6 +80,9 @@ class Options
|
|
80
80
|
attr_accessor :check_updates
|
81
81
|
# If true, targets NBNS hostname resolution won't be performed.
|
82
82
|
attr_accessor :no_target_nbns
|
83
|
+
# If true, bettercap won't forward packets for any target, causing
|
84
|
+
# connections to be killed.
|
85
|
+
attr_accessor :kill
|
83
86
|
|
84
87
|
# Create a BetterCap::Options class instance using the specified network interface.
|
85
88
|
def initialize( iface )
|
@@ -93,6 +96,7 @@ class Options
|
|
93
96
|
@debug = false
|
94
97
|
@arpcache = false
|
95
98
|
@no_target_nbns = false
|
99
|
+
@kill = false
|
96
100
|
|
97
101
|
@ignore = nil
|
98
102
|
|
@@ -131,8 +135,11 @@ class Options
|
|
131
135
|
ctx = Context.get
|
132
136
|
|
133
137
|
OptionParser.new do |opts|
|
134
|
-
opts.banner = "Usage: #{$0} [options]"
|
135
138
|
opts.version = BetterCap::VERSION
|
139
|
+
opts.banner = "Usage: bettercap [options]"
|
140
|
+
opts.separator ""
|
141
|
+
opts.separator "Specific options:"
|
142
|
+
opts.separator ""
|
136
143
|
|
137
144
|
opts.on( '-G', '--gateway ADDRESS', 'Manually specify the gateway address, if not specified the current gateway will be retrieved and used. ' ) do |v|
|
138
145
|
ctx.options.gateway = v
|
@@ -246,6 +253,10 @@ class Options
|
|
246
253
|
opts.on( '--proxy-module MODULE', 'Ruby proxy module to load.' ) do |v|
|
247
254
|
ctx.options.proxy = true
|
248
255
|
ctx.options.proxy_module = File.expand_path v
|
256
|
+
|
257
|
+
require ctx.options.proxy_module
|
258
|
+
|
259
|
+
Proxy::Module.register_options(opts)
|
249
260
|
end
|
250
261
|
|
251
262
|
opts.on( '--custom-proxy ADDRESS', 'Use a custom HTTP upstream proxy instead of the builtin one.' ) do |v|
|
@@ -278,6 +289,10 @@ class Options
|
|
278
289
|
ctx.options.httpd_path = v
|
279
290
|
end
|
280
291
|
|
292
|
+
opts.on( '--kill', 'Instead of forwarding packets, this switch will make targets connections to be killed.' ) do
|
293
|
+
ctx.options.kill = true
|
294
|
+
end
|
295
|
+
|
281
296
|
opts.on( '--check-updates', 'Will check if any update is available and then exit.' ) do
|
282
297
|
ctx.options.check_updates = true
|
283
298
|
end
|
@@ -17,53 +17,9 @@ class PacketQueue
|
|
17
17
|
@nworkers = nworkers
|
18
18
|
@running = true
|
19
19
|
@injector = PacketFu::Inject.new(:iface => iface)
|
20
|
+
@udp = UDPSocket.new
|
20
21
|
@queue = Queue.new
|
21
|
-
@workers = (0...nworkers).map {
|
22
|
-
::Thread.new {
|
23
|
-
Logger.debug "PacketQueue worker started."
|
24
|
-
|
25
|
-
while @running
|
26
|
-
begin
|
27
|
-
packet = @queue.pop
|
28
|
-
# nil packet pushed to signal stopping
|
29
|
-
if packet.nil?
|
30
|
-
Logger.debug "Got nil packet, PacketQueue stopping ..."
|
31
|
-
break
|
32
|
-
|
33
|
-
# [ ip, port, data ] pushed by Discovery::Agents::Udp
|
34
|
-
elsif packet.is_a?(Array)
|
35
|
-
ip, port, data = packet
|
36
|
-
Logger.debug "Sending UDP data packet to #{ip}:#{port} ..."
|
37
|
-
|
38
|
-
# TODO: Maybe just create one globally?
|
39
|
-
sd = UDPSocket.new
|
40
|
-
sd.send( data, 0, ip, port )
|
41
|
-
sd = nil
|
42
|
-
|
43
|
-
# PacketFu packet
|
44
|
-
else
|
45
|
-
Logger.debug "Sending #{packet.class.name} packet ..."
|
46
|
-
|
47
|
-
# Use a global PacketFu::Inject object.
|
48
|
-
@injector.array = [packet.headers[0].to_s]
|
49
|
-
@injector.inject
|
50
|
-
end
|
51
|
-
rescue Exception => e
|
52
|
-
Logger.debug "#{self.class.name} ( #{packet.class.name} ) : #{e.message}"
|
53
|
-
|
54
|
-
# If we've got an error message such as:
|
55
|
-
# (cannot open BPF device) /dev/bpf0: Too many open files
|
56
|
-
# We want to retry to probe this ip in a while.
|
57
|
-
if e.message.include? 'Too many open files'
|
58
|
-
Logger.debug "Repushing #{self.class.name} to the packet queue ..."
|
59
|
-
push(packet)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
Logger.debug "PacketQueue worker stopped."
|
65
|
-
}
|
66
|
-
}
|
22
|
+
@workers = (0...nworkers).map { ::Thread.new { worker } }
|
67
23
|
end
|
68
24
|
|
69
25
|
# Push a packet to the queue.
|
@@ -88,5 +44,53 @@ class PacketQueue
|
|
88
44
|
@nworkers.times { push(nil) }
|
89
45
|
@workers.map(&:join)
|
90
46
|
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def dispatch_udp_packet(packet)
|
51
|
+
ip, port, data = packet
|
52
|
+
Logger.debug "Sending UDP data packet to #{ip}:#{port} ..."
|
53
|
+
@udp.send( data, 0, ip, port )
|
54
|
+
end
|
55
|
+
|
56
|
+
def dispatch_raw_packet(packet)
|
57
|
+
Logger.debug "Sending #{packet.class.name} packet ..."
|
58
|
+
@injector.array = [packet.headers[0].to_s]
|
59
|
+
@injector.inject
|
60
|
+
end
|
61
|
+
|
62
|
+
def worker
|
63
|
+
Logger.debug "PacketQueue worker started."
|
64
|
+
|
65
|
+
while @running
|
66
|
+
begin
|
67
|
+
packet = @queue.pop
|
68
|
+
case packet
|
69
|
+
# nil packet pushed to signal stopping
|
70
|
+
when nil
|
71
|
+
Logger.debug "Got nil packet, PacketQueue stopping ..."
|
72
|
+
break
|
73
|
+
# [ ip, port, data ] pushed by Discovery::Agents::Udp
|
74
|
+
when Array
|
75
|
+
dispatch_udp_packet(packet)
|
76
|
+
# PacketFu raw packet
|
77
|
+
when Object
|
78
|
+
dispatch_raw_packet(packet)
|
79
|
+
end
|
80
|
+
rescue Exception => e
|
81
|
+
Logger.debug "#{self.class.name} ( #{packet.class.name} ) : #{e.message}"
|
82
|
+
|
83
|
+
# If we've got an error message such as:
|
84
|
+
# (cannot open BPF device) /dev/bpf0: Too many open files
|
85
|
+
# We want to retry to probe this ip in a while.
|
86
|
+
if e.message.include? 'Too many open files'
|
87
|
+
Logger.debug "Repushing #{self.class.name} to the packet queue ..."
|
88
|
+
push(packet)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
Logger.debug "PacketQueue worker stopped."
|
94
|
+
end
|
91
95
|
end
|
92
96
|
end
|
@@ -26,13 +26,30 @@ class Module
|
|
26
26
|
true
|
27
27
|
end
|
28
28
|
|
29
|
+
# Register custom options for each available module.
|
30
|
+
def self.register_options(opts)
|
31
|
+
self.each_module do |const|
|
32
|
+
if const.respond_to?(:on_options)
|
33
|
+
const.on_options(opts)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
29
38
|
# Register available proxy modules into the system.
|
30
39
|
def self.register_modules
|
40
|
+
self.each_module do |const|
|
41
|
+
Logger.debug "Registering module #{const}"
|
42
|
+
@@modules << const.new
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def self.each_module
|
31
49
|
Object.constants.each do |klass|
|
32
50
|
const = Kernel.const_get(klass)
|
33
51
|
if const.respond_to?(:superclass) and const.superclass == self
|
34
|
-
|
35
|
-
@@modules << const.new
|
52
|
+
yield const
|
36
53
|
end
|
37
54
|
end
|
38
55
|
end
|
@@ -51,12 +51,17 @@ class Arp < Base
|
|
51
51
|
|
52
52
|
# Start the ARP spoofing.
|
53
53
|
def start
|
54
|
-
Logger.
|
54
|
+
Logger.debug "Starting ARP spoofer ( #{@ctx.options.half_duplex ? 'Half' : 'Full'} Duplex ) ..."
|
55
55
|
|
56
56
|
stop() if @running
|
57
57
|
@running = true
|
58
58
|
|
59
|
-
@ctx.
|
59
|
+
if @ctx.options.kill
|
60
|
+
Logger.warn "Disabling packet forwarding."
|
61
|
+
@ctx.firewall.enable_forwarding(false) if @forwarding
|
62
|
+
else
|
63
|
+
@ctx.firewall.enable_forwarding(true) unless @forwarding
|
64
|
+
end
|
60
65
|
|
61
66
|
@sniff_thread = Thread.new { arp_watcher }
|
62
67
|
@spoof_thread = Thread.new { arp_spoofer }
|
@@ -66,8 +71,7 @@ class Arp < Base
|
|
66
71
|
def stop
|
67
72
|
raise 'ARP spoofer is not running' unless @running
|
68
73
|
|
69
|
-
Logger.
|
70
|
-
|
74
|
+
Logger.debug 'Stopping ARP spoofer ...'
|
71
75
|
Logger.debug "Resetting packet forwarding to #{@forwarding} ..."
|
72
76
|
@ctx.firewall.enable_forwarding( @forwarding )
|
73
77
|
|
@@ -77,7 +81,7 @@ class Arp < Base
|
|
77
81
|
rescue
|
78
82
|
end
|
79
83
|
|
80
|
-
Logger.
|
84
|
+
Logger.debug "Restoring ARP table of #{@ctx.targets.size} targets ..."
|
81
85
|
|
82
86
|
@ctx.targets.each do |target|
|
83
87
|
unless target.ip.nil? or target.mac.nil?
|
@@ -91,32 +95,6 @@ class Arp < Base
|
|
91
95
|
|
92
96
|
private
|
93
97
|
|
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
98
|
def arp_spoofer
|
121
99
|
spoof_loop(1) { |target|
|
122
100
|
unless target.ip.nil? or target.mac.nil?
|
@@ -127,7 +105,7 @@ class Arp < Base
|
|
127
105
|
end
|
128
106
|
|
129
107
|
def arp_watcher
|
130
|
-
Logger.
|
108
|
+
Logger.debug 'ARP watcher started ...'
|
131
109
|
|
132
110
|
sniff_packets('arp') { |pkt|
|
133
111
|
# we're only interested in 'who-has' packets
|
@@ -88,14 +88,11 @@ private
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def update_gateway!
|
91
|
-
Logger.info "Getting gateway #{@ctx.gateway} MAC address ..."
|
92
|
-
|
93
91
|
hw = Network.get_hw_address( @ctx.ifconfig, @ctx.gateway )
|
94
92
|
raise BetterCap::Error, "Couldn't determine router MAC" if hw.nil?
|
95
|
-
|
96
93
|
@gateway = Target.new( @ctx.gateway, hw )
|
97
94
|
|
98
|
-
Logger.info "
|
95
|
+
Logger.info "[#{'GATEWAY'.green}] #{@gateway.to_s(false)}"
|
99
96
|
end
|
100
97
|
|
101
98
|
def update_targets!
|
@@ -104,21 +101,22 @@ private
|
|
104
101
|
if target.mac.nil?
|
105
102
|
hw = Network.get_hw_address( @ctx.ifconfig, target.ip )
|
106
103
|
if hw.nil?
|
107
|
-
Logger.warn "Couldn't determine target #{ip} MAC!"
|
104
|
+
Logger.warn "Couldn't determine target #{ip} MAC address!"
|
108
105
|
next
|
109
106
|
else
|
110
|
-
Logger.info " Target MAC : #{hw}"
|
111
107
|
target.mac = hw
|
108
|
+
Logger.info "[#{'TARGET'.green}] #{target.to_s(false)}"
|
112
109
|
end
|
113
110
|
# target was specified by MAC address
|
114
111
|
elsif target.ip_refresh
|
115
112
|
ip = Network.get_ip_address( @ctx, target.mac )
|
116
113
|
if ip.nil?
|
117
|
-
Logger.warn "Couldn't determine target #{target.mac} IP!"
|
114
|
+
Logger.warn "Couldn't determine target #{target.mac} IP address!"
|
118
115
|
next
|
119
116
|
else
|
120
|
-
|
117
|
+
doprint = ( target.ip.nil? or target.ip != ip )
|
121
118
|
target.ip = ip
|
119
|
+
Logger.info("[#{'TARGET'.green}] #{target.to_s(false)}") if doprint
|
122
120
|
end
|
123
121
|
end
|
124
122
|
end
|
@@ -112,12 +112,18 @@ class Icmp < Base
|
|
112
112
|
|
113
113
|
# Start the ICMP redirect spoofing.
|
114
114
|
def start
|
115
|
-
Logger.
|
115
|
+
Logger.debug "Starting ICMP redirect spoofer ..."
|
116
116
|
|
117
117
|
stop() if @running
|
118
118
|
@running = true
|
119
119
|
|
120
|
-
@ctx.
|
120
|
+
if @ctx.options.kill
|
121
|
+
Logger.warn "Disabling packet forwarding."
|
122
|
+
@ctx.firewall.enable_forwarding(false) if @forwarding
|
123
|
+
else
|
124
|
+
@ctx.firewall.enable_forwarding(true) unless @forwarding
|
125
|
+
end
|
126
|
+
|
121
127
|
@ctx.firewall.enable_send_redirects(false)
|
122
128
|
|
123
129
|
@spoof_thread = Thread.new { icmp_spoofer }
|
@@ -128,8 +134,7 @@ class Icmp < Base
|
|
128
134
|
def stop
|
129
135
|
raise 'ICMP redirect spoofer is not running' unless @running
|
130
136
|
|
131
|
-
Logger.
|
132
|
-
|
137
|
+
Logger.debug 'Stopping ICMP redirect spoofer ...'
|
133
138
|
Logger.debug "Resetting packet forwarding to #{@forwarding} ..."
|
134
139
|
@ctx.firewall.enable_forwarding( @forwarding )
|
135
140
|
|
@@ -156,7 +161,7 @@ class Icmp < Base
|
|
156
161
|
end
|
157
162
|
|
158
163
|
def dns_watcher
|
159
|
-
Logger.
|
164
|
+
Logger.debug 'DNS watcher started ...'
|
160
165
|
|
161
166
|
sniff_packets('udp and port 53') { |pkt|
|
162
167
|
next unless is_interesting_packet?(pkt)
|
data/lib/bettercap/target.rb
CHANGED
@@ -72,8 +72,12 @@ class Target
|
|
72
72
|
end
|
73
73
|
|
74
74
|
# Return a verbose string representation of this object.
|
75
|
-
def to_s
|
76
|
-
|
75
|
+
def to_s(padding=true)
|
76
|
+
if padding
|
77
|
+
s = sprintf( '%-15s : %-17s', if @ip.nil? then '???' else @ip end, @mac )
|
78
|
+
else
|
79
|
+
s = sprintf( '%s : %s', if @ip.nil? then '???' else @ip end, @mac )
|
80
|
+
end
|
77
81
|
s += " / #{@hostname}" unless @hostname.nil?
|
78
82
|
s += if @vendor.nil? then " ( ??? )" else " ( #{@vendor} )" end
|
79
83
|
s
|
data/lib/bettercap/version.rb
CHANGED
@@ -11,7 +11,7 @@ This project is released under the GPL 3 license.
|
|
11
11
|
=end
|
12
12
|
module BetterCap
|
13
13
|
# Current version of bettercap.
|
14
|
-
VERSION = '1.2.
|
14
|
+
VERSION = '1.2.1'
|
15
15
|
# Program banner.
|
16
16
|
BANNER = File.read( File.dirname(__FILE__) + '/banner' ).gsub( '#VERSION#', "v#{VERSION}")
|
17
17
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bettercap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simone Margaritelli
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -91,6 +91,7 @@ files:
|
|
91
91
|
- LICENSE.md
|
92
92
|
- README.md
|
93
93
|
- TODO.md
|
94
|
+
- lib/bettercap/arp_reader.rb
|
94
95
|
- lib/bettercap/banner
|
95
96
|
- lib/bettercap/context.rb
|
96
97
|
- lib/bettercap/discovery/agents/arp.rb
|
@@ -108,6 +109,7 @@ files:
|
|
108
109
|
- lib/bettercap/firewalls/redirection.rb
|
109
110
|
- lib/bettercap/httpd/server.rb
|
110
111
|
- lib/bettercap/hw-prefixes
|
112
|
+
- lib/bettercap/loader.rb
|
111
113
|
- lib/bettercap/logger.rb
|
112
114
|
- lib/bettercap/monkey/packetfu/utils.rb
|
113
115
|
- lib/bettercap/network.rb
|