bettercap 1.1.1 → 1.1.2
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/.gitignore +5 -0
- data/LICENSE.md +596 -0
- data/README.md +18 -15
- data/TODO.md +22 -0
- data/bettercap.gemspec +1 -6
- data/bin/bettercap +35 -46
- data/lib/bettercap.rb +39 -0
- data/lib/bettercap/base/ifirewall.rb +11 -5
- data/lib/bettercap/base/ispoofer.rb +9 -3
- data/lib/bettercap/context.rb +48 -38
- data/lib/bettercap/discovery/arp.rb +18 -2
- data/lib/bettercap/discovery/base.rb +64 -0
- data/lib/bettercap/discovery/icmp.rb +3 -3
- data/lib/bettercap/discovery/syn.rb +6 -49
- data/lib/bettercap/discovery/udp.rb +23 -58
- data/lib/bettercap/factories/firewall_factory.rb +1 -1
- data/lib/bettercap/factories/parser_factory.rb +27 -25
- data/lib/bettercap/factories/spoofer_factory.rb +19 -13
- data/lib/bettercap/firewalls/linux.rb +3 -11
- data/lib/bettercap/firewalls/osx.rb +5 -18
- data/lib/bettercap/httpd/server.rb +4 -4
- data/lib/bettercap/network.rb +87 -74
- data/lib/bettercap/proxy/module.rb +1 -1
- data/lib/bettercap/proxy/proxy.rb +3 -3
- data/lib/bettercap/proxy/request.rb +5 -5
- data/lib/bettercap/proxy/response.rb +2 -2
- data/lib/bettercap/sniffer/parsers/base.rb +13 -13
- data/lib/bettercap/sniffer/sniffer.rb +19 -2
- data/lib/bettercap/spoofers/arp.rb +5 -5
- data/lib/bettercap/target.rb +18 -18
- data/lib/bettercap/version.rb +1 -1
- metadata +10 -7
- data/LICENSE +0 -225
- data/example_proxy_module.rb +0 -21
@@ -12,9 +12,11 @@ This project is released under the GPL 3 license.
|
|
12
12
|
require 'bettercap/logger'
|
13
13
|
require 'bettercap/shell'
|
14
14
|
require 'bettercap/target'
|
15
|
+
require 'bettercap/discovery/base'
|
15
16
|
|
16
17
|
# Parse the ARP table searching for new hosts.
|
17
|
-
class ArpAgent
|
18
|
+
class ArpAgent < BaseAgent
|
19
|
+
|
18
20
|
def self.parse( ctx )
|
19
21
|
arp = Shell.arp
|
20
22
|
targets = []
|
@@ -24,7 +26,7 @@ class ArpAgent
|
|
24
26
|
arp.split("\n").each do |line|
|
25
27
|
m = /[^\s]+\s+\(([0-9\.]+)\)\s+at\s+([a-f0-9:]+).+#{ctx.ifconfig[:iface]}.*/i.match(line)
|
26
28
|
if !m.nil?
|
27
|
-
if m[1] != ctx.gateway and m[1] != ctx.
|
29
|
+
if m[1] != ctx.gateway and m[1] != ctx.ifconfig[:ip_saddr] and m[2] != 'ff:ff:ff:ff:ff:ff'
|
28
30
|
target = Target.new( m[1], m[2] )
|
29
31
|
targets << target
|
30
32
|
Logger.debug "FOUND #{target}"
|
@@ -34,4 +36,18 @@ class ArpAgent
|
|
34
36
|
|
35
37
|
targets
|
36
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def send_probe( ip )
|
43
|
+
pkt = PacketFu::ARPPacket.new
|
44
|
+
|
45
|
+
pkt.eth_saddr = pkt.arp_saddr_mac = @ifconfig[:eth_saddr]
|
46
|
+
pkt.eth_daddr = 'ff:ff:ff:ff:ff:ff'
|
47
|
+
pkt.arp_daddr_mac = '00:00:00:00:00:00'
|
48
|
+
pkt.arp_saddr_ip = @ifconfig[:ip_saddr]
|
49
|
+
pkt.arp_daddr_ip = ip
|
50
|
+
|
51
|
+
pkt.to_w( @ifconfig[:iface] )
|
52
|
+
end
|
37
53
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
BETTERCAP
|
4
|
+
|
5
|
+
Author : Simone 'evilsocket' Margaritelli
|
6
|
+
Email : evilsocket@gmail.com
|
7
|
+
Blog : http://www.evilsocket.net/
|
8
|
+
|
9
|
+
This project is released under the GPL 3 license.
|
10
|
+
|
11
|
+
=end
|
12
|
+
require 'bettercap/logger'
|
13
|
+
|
14
|
+
# Base class for discovery agents.
|
15
|
+
class BaseAgent
|
16
|
+
def initialize( ifconfig, gw_ip, local_ip )
|
17
|
+
@local_ip = local_ip
|
18
|
+
@ifconfig = ifconfig
|
19
|
+
@queue = Queue.new
|
20
|
+
|
21
|
+
net = ip = @ifconfig[:ip4_obj]
|
22
|
+
|
23
|
+
# loop each ip in our subnet and push it to the queue
|
24
|
+
while net.include?ip
|
25
|
+
# rescanning the gateway could cause an issue when the
|
26
|
+
# gateway itself has multiple interfaces ( LAN, WAN ... )
|
27
|
+
if ip != gw_ip and ip != local_ip
|
28
|
+
@queue.push ip
|
29
|
+
end
|
30
|
+
|
31
|
+
ip = ip.succ
|
32
|
+
end
|
33
|
+
|
34
|
+
# spawn the workers! ( tnx to https://blog.engineyard.com/2014/ruby-thread-pool )
|
35
|
+
@workers = (0...4).map do
|
36
|
+
Thread.new do
|
37
|
+
begin
|
38
|
+
while ip = @queue.pop(true)
|
39
|
+
Logger.debug "#{self.class.name} : Probing #{ip} ..."
|
40
|
+
|
41
|
+
send_probe ip.to_s
|
42
|
+
end
|
43
|
+
rescue Exception => e
|
44
|
+
Logger.debug "#{self.class.name} : #{ip} -> #{e.message}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def wait
|
51
|
+
begin
|
52
|
+
@workers.map(&:join)
|
53
|
+
rescue Exception => e
|
54
|
+
Logger.debug "#{self.class.name}.wait: #{e.message}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def send_probe( ip )
|
61
|
+
Logger.warn "#{self.class.name} not implemented!"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -16,15 +16,15 @@ require 'bettercap/factories/firewall_factory'
|
|
16
16
|
# Send a broadcast ping trying to filling the ARP table.
|
17
17
|
class IcmpAgent
|
18
18
|
def initialize( timeout = 5 )
|
19
|
-
@thread = Thread.new
|
19
|
+
@thread = Thread.new {
|
20
20
|
FirewallFactory.get_firewall.enable_icmp_bcast(true)
|
21
21
|
|
22
22
|
if RUBY_PLATFORM =~ /darwin/
|
23
23
|
ping = Shell.execute("ping -i #{timeout} -c 2 255.255.255.255")
|
24
|
-
elsif RUBY_PLATFORM =~ /linux/
|
24
|
+
elsif RUBY_PLATFORM =~ /linux/
|
25
25
|
ping = Shell.execute("ping -i #{timeout} -c 2 -b 255.255.255.255")
|
26
26
|
end
|
27
|
-
|
27
|
+
}
|
28
28
|
end
|
29
29
|
|
30
30
|
def wait
|
@@ -9,57 +9,13 @@ Blog : http://www.evilsocket.net/
|
|
9
9
|
This project is released under the GPL 3 license.
|
10
10
|
|
11
11
|
=end
|
12
|
-
require 'bettercap/
|
12
|
+
require 'bettercap/discovery/base'
|
13
13
|
|
14
14
|
# Send SYN probes trying to filling the ARP table.
|
15
|
-
class SynAgent
|
16
|
-
def initialize( ifconfig, gw_ip, local_ip )
|
17
|
-
@local_ip = local_ip
|
18
|
-
@ifconfig = ifconfig
|
19
|
-
@queue = Queue.new
|
20
|
-
|
21
|
-
net = ip = @ifconfig[:ip4_obj]
|
22
|
-
|
23
|
-
# loop each ip in our subnet and push it to the queue
|
24
|
-
while net.include?ip
|
25
|
-
# rescanning the gateway could cause an issue when the
|
26
|
-
# gateway itself has multiple interfaces ( LAN, WAN ... )
|
27
|
-
if ip != gw_ip and ip != local_ip
|
28
|
-
@queue.push ip
|
29
|
-
end
|
30
|
-
|
31
|
-
ip = ip.succ
|
32
|
-
end
|
33
|
-
|
34
|
-
# spawn the workers! ( tnx to https://blog.engineyard.com/2014/ruby-thread-pool )
|
35
|
-
@workers = (0...4).map do
|
36
|
-
Thread.new do
|
37
|
-
begin
|
38
|
-
while ip = @queue.pop(true)
|
39
|
-
Logger.debug "SYN Probing #{ip} ..."
|
40
|
-
|
41
|
-
pkt = get_packet ip.to_s
|
42
|
-
|
43
|
-
pkt.to_w( @ifconfig[:iface] )
|
44
|
-
end
|
45
|
-
rescue Exception => e
|
46
|
-
Logger.debug "#{ip} -> #{e.message}"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def wait
|
53
|
-
begin
|
54
|
-
@workers.map(&:join)
|
55
|
-
rescue Exception => e
|
56
|
-
Logger.debug "SynAgent.wait: #{e.message}"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
15
|
+
class SynAgent < BaseAgent
|
60
16
|
private
|
61
17
|
|
62
|
-
def
|
18
|
+
def send_probe( ip )
|
63
19
|
pkt = PacketFu::TCPPacket.new
|
64
20
|
pkt.ip_v = 4
|
65
21
|
pkt.ip_hl = 5
|
@@ -69,7 +25,7 @@ class SynAgent
|
|
69
25
|
pkt.ip_ttl = 115
|
70
26
|
pkt.ip_proto = 6 # TCP
|
71
27
|
pkt.ip_saddr = @local_ip
|
72
|
-
pkt.ip_daddr =
|
28
|
+
pkt.ip_daddr = ip
|
73
29
|
pkt.payload = "\xC\x0\xF\xF\xE\xE"
|
74
30
|
pkt.tcp_flags.ack = 0
|
75
31
|
pkt.tcp_flags.fin = 0
|
@@ -82,7 +38,8 @@ class SynAgent
|
|
82
38
|
pkt.tcp_hlen = 5
|
83
39
|
pkt.tcp_dst = rand(1024..65535)
|
84
40
|
pkt.recalc
|
85
|
-
|
41
|
+
|
42
|
+
pkt.to_w( @ifconfig[:iface] )
|
86
43
|
end
|
87
44
|
end
|
88
45
|
|
@@ -9,66 +9,31 @@ Blog : http://www.evilsocket.net/
|
|
9
9
|
This project is released under the GPL 3 license.
|
10
10
|
|
11
11
|
=end
|
12
|
-
require 'bettercap/
|
12
|
+
require 'bettercap/discovery/base'
|
13
13
|
|
14
14
|
# Send UDP probes trying to filling the ARP table.
|
15
|
-
class UdpAgent
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
# rescanning the gateway could cause an issue when the
|
38
|
-
# gateway itself has multiple interfaces ( LAN, WAN ... )
|
39
|
-
if ip != gw_ip and ip != local_ip
|
40
|
-
@queue.push ip
|
41
|
-
end
|
42
|
-
|
43
|
-
ip = ip.succ
|
44
|
-
end
|
45
|
-
|
46
|
-
# spawn the workers! ( tnx to https://blog.engineyard.com/2014/ruby-thread-pool )
|
47
|
-
@workers = (0...4).map do
|
48
|
-
Thread.new do
|
49
|
-
begin
|
50
|
-
while ip = @queue.pop(true)
|
51
|
-
Logger.debug "Probing #{ip} ..."
|
52
|
-
|
53
|
-
# send netbios udp packet, just to fill ARP table
|
54
|
-
sd = UDPSocket.new
|
55
|
-
sd.send( @message, 0, ip.to_s, @port )
|
56
|
-
sd = nil
|
57
|
-
# TODO: Parse response for hostname?
|
58
|
-
end
|
59
|
-
rescue Exception => e
|
60
|
-
Logger.debug "#{ip} -> #{e.message}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def wait
|
67
|
-
begin
|
68
|
-
@workers.map(&:join)
|
69
|
-
rescue Exception => e
|
70
|
-
Logger.debug "UdpAgent.wait: #{e.message}"
|
71
|
-
end
|
15
|
+
class UdpAgent < BaseAgent
|
16
|
+
private
|
17
|
+
|
18
|
+
def send_probe( ip )
|
19
|
+
port = 137
|
20
|
+
message =
|
21
|
+
"\x82\x28\x00\x00\x00" +
|
22
|
+
"\x01\x00\x00\x00\x00" +
|
23
|
+
"\x00\x00\x20\x43\x4B" +
|
24
|
+
"\x41\x41\x41\x41\x41" +
|
25
|
+
"\x41\x41\x41\x41\x41" +
|
26
|
+
"\x41\x41\x41\x41\x41" +
|
27
|
+
"\x41\x41\x41\x41\x41" +
|
28
|
+
"\x41\x41\x41\x41\x41" +
|
29
|
+
"\x41\x41\x41\x41\x41" +
|
30
|
+
"\x00\x00\x21\x00\x01"
|
31
|
+
|
32
|
+
# send netbios udp packet, just to fill ARP table
|
33
|
+
sd = UDPSocket.new
|
34
|
+
sd.send( message, 0, ip.to_s, port )
|
35
|
+
sd = nil
|
36
|
+
# TODO: Parse response for hostname?
|
72
37
|
end
|
73
38
|
end
|
74
39
|
|
@@ -16,38 +16,40 @@ require 'bettercap/logger'
|
|
16
16
|
class ParserFactory
|
17
17
|
@@path = File.dirname(__FILE__) + '/../sniffer/parsers/'
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
class << self
|
20
|
+
def available
|
21
|
+
avail = []
|
22
|
+
Dir.foreach( @@path ) do |file|
|
23
|
+
if file =~ /.rb/ and file != 'base.rb'
|
24
|
+
avail << file.gsub('.rb','').upcase
|
25
|
+
end
|
24
26
|
end
|
27
|
+
avail
|
25
28
|
end
|
26
|
-
avail
|
27
|
-
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
def from_cmdline(v)
|
31
|
+
avail = available
|
32
|
+
list = v.split(',').collect(&:strip).collect(&:upcase).reject{ |c| c.empty? }
|
33
|
+
list.each do |parser|
|
34
|
+
raise BetterCap::Error, "Invalid parser name '#{parser}'." unless avail.include?(parser) or parser == '*'
|
35
|
+
end
|
36
|
+
list
|
34
37
|
end
|
35
|
-
list
|
36
|
-
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
def ParserFactory.load_by_names(parsers)
|
40
|
+
loaded = []
|
41
|
+
Dir.foreach( @@path ) do |file|
|
42
|
+
cname = file.gsub('.rb','').upcase
|
43
|
+
if file =~ /.rb/ and file != 'base.rb' and ( parsers.include?(cname) or parsers == ['*'] )
|
44
|
+
Logger.debug "Loading parser #{cname} ..."
|
45
|
+
|
46
|
+
require_relative "#{@@path}#{file}"
|
47
|
+
|
48
|
+
loaded << Kernel.const_get("#{cname.capitalize}Parser").new
|
49
|
+
end
|
48
50
|
end
|
51
|
+
loaded
|
49
52
|
end
|
50
|
-
loaded
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
@@ -12,25 +12,31 @@ This project is released under the GPL 3 license.
|
|
12
12
|
require 'bettercap/error'
|
13
13
|
|
14
14
|
class SpooferFactory
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
class << self
|
16
|
+
def available
|
17
|
+
avail = []
|
18
|
+
Dir.foreach( File.dirname(__FILE__) + '/../spoofers/') do |file|
|
19
|
+
if file =~ /.rb/
|
20
|
+
avail << file.gsub('.rb','').upcase
|
21
|
+
end
|
20
22
|
end
|
23
|
+
avail
|
21
24
|
end
|
22
|
-
avail
|
23
|
-
end
|
24
25
|
|
25
|
-
|
26
|
-
|
26
|
+
def get_by_name(name)
|
27
|
+
raise BetterCap::Error, "Invalid spoofer name '#{name}'!" unless available? name
|
28
|
+
|
29
|
+
name.downcase!
|
27
30
|
|
28
|
-
|
31
|
+
require_relative "../spoofers/#{name}"
|
29
32
|
|
30
|
-
|
33
|
+
Kernel.const_get("#{name.capitalize}Spoofer").new
|
34
|
+
end
|
31
35
|
|
32
|
-
|
36
|
+
private
|
33
37
|
|
34
|
-
|
38
|
+
def available?(name)
|
39
|
+
available.include?(name)
|
40
|
+
end
|
35
41
|
end
|
36
42
|
end
|
@@ -14,23 +14,15 @@ require 'bettercap/shell'
|
|
14
14
|
|
15
15
|
class LinuxFirewall < IFirewall
|
16
16
|
def enable_forwarding(enabled)
|
17
|
-
|
18
|
-
Shell.execute('echo 1 > /proc/sys/net/ipv4/ip_forward')
|
19
|
-
else
|
20
|
-
Shell.execute('echo 0 > /proc/sys/net/ipv4/ip_forward')
|
21
|
-
end
|
17
|
+
Shell.execute("echo #{enabled ? 1 : 0} > /proc/sys/net/ipv4/ip_forward")
|
22
18
|
end
|
23
19
|
|
24
20
|
def forwarding_enabled?
|
25
21
|
Shell.execute('cat /proc/sys/net/ipv4/ip_forward').strip == '1'
|
26
22
|
end
|
27
|
-
|
23
|
+
|
28
24
|
def enable_icmp_bcast(enabled)
|
29
|
-
|
30
|
-
Shell.execute('echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts')
|
31
|
-
else
|
32
|
-
Shell.execute('echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts')
|
33
|
-
end
|
25
|
+
Shell.execute("echo #{enabled ? 0 : 1} > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts")
|
34
26
|
end
|
35
27
|
|
36
28
|
def add_port_redirection( iface, proto, from, addr, to )
|
@@ -14,34 +14,21 @@ require 'bettercap/shell'
|
|
14
14
|
|
15
15
|
class OSXFirewall < IFirewall
|
16
16
|
def enable_forwarding(enabled)
|
17
|
-
|
18
|
-
Shell.execute('sysctl -w net.inet.ip.forwarding=1')
|
19
|
-
else
|
20
|
-
Shell.execute('sysctl -w net.inet.ip.forwarding=0')
|
21
|
-
end
|
17
|
+
Shell.execute("sysctl -w net.inet.ip.forwarding=#{enabled ? 1 : 0}")
|
22
18
|
end
|
23
19
|
|
24
20
|
def enable_icmp_bcast(enabled)
|
25
|
-
|
26
|
-
Shell.execute('sysctl -w net.inet.icmp.bmcastecho=1')
|
27
|
-
else
|
28
|
-
Shell.execute('sysctl -w net.inet.icmp.bmcastecho=0')
|
29
|
-
end
|
21
|
+
Shell.execute("sysctl -w net.inet.icmp.bmcastecho=#{enabled ? 1 : 0}")
|
30
22
|
end
|
31
23
|
|
32
|
-
def forwarding_enabled?
|
24
|
+
def forwarding_enabled?
|
33
25
|
Shell.execute('sysctl net.inet.ip.forwarding').strip.split(' ')[1] == '1'
|
34
26
|
end
|
35
27
|
|
36
28
|
def enable(enabled)
|
37
29
|
begin
|
38
|
-
|
39
|
-
|
40
|
-
else
|
41
|
-
Shell.execute('pfctl -d >/dev/null 2>&1')
|
42
|
-
end
|
43
|
-
rescue
|
44
|
-
end
|
30
|
+
Shell.execute("pfctl -#{enabled ? ?e : ?d} >/dev/null 2>&1")
|
31
|
+
rescue; end
|
45
32
|
end
|
46
33
|
|
47
34
|
def add_port_redirection( iface, proto, from, addr, to )
|