bettercap 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 )
|