bettercap 1.1.4 → 1.1.5
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 +6 -1
- data/bin/bettercap +19 -19
- data/lib/bettercap.rb +4 -0
- data/lib/bettercap/banner +2 -1
- data/lib/bettercap/context.rb +6 -5
- data/lib/bettercap/discovery/icmp.rb +1 -1
- data/lib/bettercap/firewalls/linux.rb +0 -4
- data/lib/bettercap/network.rb +0 -5
- data/lib/bettercap/proxy/proxy.rb +13 -6
- data/lib/bettercap/proxy/response.rb +11 -7
- data/lib/bettercap/sniffer/parsers/https.rb +22 -16
- data/lib/bettercap/sniffer/sniffer.rb +11 -1
- data/lib/bettercap/spoofers/arp.rb +4 -1
- data/lib/bettercap/version.rb +1 -1
- metadata +2 -4
- data/lib/bettercap/discovery/syn.rb +0 -45
- data/test_https_proxy.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 724a91086ccfab829ec09e9ad381a9025dfd8380
|
4
|
+
data.tar.gz: 59292cebacc9ed01bb85d2ce3ac5a63151fd58fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7eb4990aeac1e69a85b87ea3dcd6e09f29701efc6932668919e5ecf1ba2de0a3d2c8da97b33350a7246947eef0adf5247fbc0f44c56b1520816c95a225198720
|
7
|
+
data.tar.gz: b0f32c69b41f4308b1bcbe7c9119c2b307579fbe9aaa7f2abc5d29b8c8376ada977e0ca6a8a41caff9a531da8e7b83cb97d4dcb71cf4ba23f6175d23a8a35f1c
|
data/.gitignore
CHANGED
data/bin/bettercap
CHANGED
@@ -23,6 +23,10 @@ begin
|
|
23
23
|
OptionParser.new do |opts|
|
24
24
|
opts.banner = "Usage: #{$0} [options]"
|
25
25
|
opts.version = BetterCap::VERSION
|
26
|
+
|
27
|
+
opts.on( '-G', '--gateway ADDRESS', 'Manually specify the gateway address, if not specified the current gateway will be retrieved and used. ' ) do |v|
|
28
|
+
ctx.options[:gateway] = v
|
29
|
+
end
|
26
30
|
|
27
31
|
opts.on( '-I', '--interface IFACE', 'Network interface name - default: ' + ctx.options[:iface].to_s ) do |v|
|
28
32
|
ctx.options[:iface] = v
|
@@ -53,6 +57,11 @@ begin
|
|
53
57
|
ctx.options[:sniffer] = true
|
54
58
|
end
|
55
59
|
|
60
|
+
opts.on( '--sniffer-source FILE', 'Load packets from the specified PCAP file instead of the interface ( will enable sniffer ).' ) do |v|
|
61
|
+
ctx.options[:sniffer] = true
|
62
|
+
ctx.options[:sniffer_src] = File.expand_path v
|
63
|
+
end
|
64
|
+
|
56
65
|
opts.on( '--sniffer-pcap FILE', 'Save all packets to the specified PCAP file ( will enable sniffer ).' ) do |v|
|
57
66
|
ctx.options[:sniffer] = true
|
58
67
|
ctx.options[:sniffer_pcap] = File.expand_path v
|
@@ -131,23 +140,7 @@ begin
|
|
131
140
|
|
132
141
|
opts.on('-h', '--help', 'Display the available options.') do
|
133
142
|
puts opts
|
134
|
-
puts "\
|
135
|
-
puts " - Sniffer / Credentials Harvester\n".bold
|
136
|
-
puts " Default sniffer mode, all parsers enabled:\n\n"
|
137
|
-
puts " sudo bettercap -X\n".bold
|
138
|
-
puts " Enable sniffer and load only specified parsers:\n\n"
|
139
|
-
puts " sudo bettercap -X -P \"FTP,HTTPAUTH,MAIL,NTLMSS\"\n".bold
|
140
|
-
puts " Enable sniffer + all parsers and parse local traffic as well:\n\n"
|
141
|
-
puts " sudo bettercap -X -L\n".bold
|
142
|
-
puts " - Transparent Proxy\n".bold
|
143
|
-
puts " Enable proxy on default ( 8080 ) port with no modules ( quite useless ):\n\n"
|
144
|
-
puts " sudo bettercap --proxy\n".bold
|
145
|
-
puts " Enable proxy and use a custom port:\n\n"
|
146
|
-
puts " sudo bettercap --proxy --proxy-port=8081\n".bold
|
147
|
-
puts " Enable proxy and load the module example_proxy_module.rb:\n\n"
|
148
|
-
puts " sudo bettercap --proxy --proxy-module=example_proxy_module.rb\n".bold
|
149
|
-
puts " Disable spoofer and enable proxy ( stand alone proxy mode ):\n\n"
|
150
|
-
puts " sudo bettercap -S NONE --proxy".bold
|
143
|
+
puts "\nFor examples & instructions please visit " + "http://bettercap.org/features/".bold
|
151
144
|
exit
|
152
145
|
end
|
153
146
|
end.parse!
|
@@ -168,10 +161,18 @@ begin
|
|
168
161
|
|
169
162
|
Logger.logfile = ctx.options[:logfile]
|
170
163
|
|
164
|
+
|
165
|
+
if !ctx.options[:gateway].nil?
|
166
|
+
raise BetterCap::Error, "Invalid gateway" if !Network.is_ip?(ctx.options[:gateway])
|
167
|
+
ctx.gateway = ctx.options[:gateway]
|
168
|
+
Logger.info("Targetting manual gateway #{ctx.gateway}")
|
169
|
+
end
|
170
|
+
|
171
171
|
ctx.update_network
|
172
172
|
|
173
173
|
if ctx.options[:target].nil?
|
174
|
-
Logger.info "Targeting the whole subnet #{ctx.network.to_range} ..."
|
174
|
+
Logger.info( "Targeting the whole subnet #{ctx.network.to_range} ..." ) unless \
|
175
|
+
ctx.options[:spoofer] == 'NONE' or ctx.options[:spoofer] == 'none'
|
175
176
|
|
176
177
|
ctx.start_discovery_thread
|
177
178
|
else
|
@@ -192,7 +193,6 @@ begin
|
|
192
193
|
ctx.spoofer=Array.new
|
193
194
|
spoofer_modules_names=ctx.options[:spoofer].split(",")
|
194
195
|
spoofer_modules_names.each do |module_name|
|
195
|
-
Logger.info "Loading module : #{module_name}"
|
196
196
|
ctx.spoofer << SpooferFactory.get_by_name( module_name )
|
197
197
|
ctx.spoofer.last.start
|
198
198
|
end
|
data/lib/bettercap.rb
CHANGED
data/lib/bettercap/banner
CHANGED
data/lib/bettercap/context.rb
CHANGED
@@ -36,6 +36,7 @@ class Context
|
|
36
36
|
end
|
37
37
|
|
38
38
|
@options = {
|
39
|
+
gateway: nil,
|
39
40
|
iface: iface,
|
40
41
|
spoofer: 'ARP',
|
41
42
|
half_duplex: false,
|
@@ -47,6 +48,7 @@ class Context
|
|
47
48
|
sniffer: false,
|
48
49
|
sniffer_pcap: nil,
|
49
50
|
sniffer_filter: nil,
|
51
|
+
sniffer_src: nil,
|
50
52
|
parsers: ['*'],
|
51
53
|
local: false,
|
52
54
|
|
@@ -113,7 +115,7 @@ class Context
|
|
113
115
|
@firewall = FirewallFactory.get_firewall
|
114
116
|
@ifconfig = PacketFu::Utils.ifconfig @options[:iface]
|
115
117
|
@network = @ifconfig[:ip4_obj]
|
116
|
-
@gateway = Network.get_gateway
|
118
|
+
@gateway = Network.get_gateway if @gateway.nil?
|
117
119
|
|
118
120
|
raise BetterCap::Error, "Could not determine IPv4 address of '#{@options[:iface]}' interface." unless !@network.nil?
|
119
121
|
|
@@ -124,7 +126,7 @@ class Context
|
|
124
126
|
def start_discovery_thread
|
125
127
|
@discovery_running = true
|
126
128
|
@discovery_thread = Thread.new {
|
127
|
-
Logger.info 'Network discovery thread started.'
|
129
|
+
Logger.info( 'Network discovery thread started.' ) unless @options[:arpcache]
|
128
130
|
|
129
131
|
while @discovery_running
|
130
132
|
empty_list = false
|
@@ -158,11 +160,10 @@ class Context
|
|
158
160
|
@discovery_running = false
|
159
161
|
|
160
162
|
if @discovery_thread != nil
|
161
|
-
Logger.info 'Stopping network discovery thread ...'
|
163
|
+
Logger.info( 'Stopping network discovery thread ...' ) unless @options[:arpcache]
|
162
164
|
|
163
|
-
# I doubt this will ever raise an exception
|
164
165
|
begin
|
165
|
-
@discovery_thread.
|
166
|
+
@discovery_thread.exit
|
166
167
|
rescue
|
167
168
|
end
|
168
169
|
end
|
@@ -22,7 +22,7 @@ class IcmpAgent
|
|
22
22
|
if RUBY_PLATFORM =~ /darwin/
|
23
23
|
ping = Shell.execute("ping -i #{timeout} -c 2 255.255.255.255")
|
24
24
|
elsif RUBY_PLATFORM =~ /linux/
|
25
|
-
ping = Shell.execute("ping -i #{timeout} -c 2 -b 255.255.255.255")
|
25
|
+
ping = Shell.execute("ping -i #{timeout} -c 2 -b 255.255.255.255 2> /dev/null")
|
26
26
|
end
|
27
27
|
}
|
28
28
|
end
|
@@ -26,10 +26,6 @@ class LinuxFirewall < IFirewall
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def add_port_redirection( iface, proto, from, addr, to )
|
29
|
-
# clear nat
|
30
|
-
shell.execute('iptables -t nat -F')
|
31
|
-
# clear
|
32
|
-
shell.execute('iptables -F')
|
33
29
|
# post route
|
34
30
|
shell.execute('iptables -t nat -I POSTROUTING -s 0/0 -j MASQUERADE')
|
35
31
|
# accept all
|
data/lib/bettercap/network.rb
CHANGED
@@ -18,7 +18,6 @@ require 'bettercap/target'
|
|
18
18
|
require 'bettercap/factories/firewall_factory'
|
19
19
|
require 'bettercap/discovery/icmp'
|
20
20
|
require 'bettercap/discovery/udp'
|
21
|
-
require 'bettercap/discovery/syn'
|
22
21
|
require 'bettercap/discovery/arp'
|
23
22
|
|
24
23
|
class Network
|
@@ -70,10 +69,8 @@ class Network
|
|
70
69
|
if ctx.options[:arpcache] == false
|
71
70
|
icmp = IcmpAgent.new timeout
|
72
71
|
udp = UdpAgent.new ctx.ifconfig, ctx.gateway, ctx.ifconfig[:ip_saddr]
|
73
|
-
syn = SynAgent.new ctx.ifconfig, ctx.gateway, ctx.ifconfig[:ip_saddr]
|
74
72
|
arp = ArpAgent.new ctx.ifconfig, ctx.gateway, ctx.ifconfig[:ip_saddr]
|
75
73
|
|
76
|
-
syn.wait
|
77
74
|
icmp.wait
|
78
75
|
arp.wait
|
79
76
|
udp.wait
|
@@ -85,8 +82,6 @@ class Network
|
|
85
82
|
end
|
86
83
|
|
87
84
|
=begin
|
88
|
-
FIXME:
|
89
|
-
|
90
85
|
Apparently on Mac OSX the gem pcaprub ( or libpcap itself ) has
|
91
86
|
a bug, so we can't use 'PacketFu::Utils::arp' since the funtion
|
92
87
|
it's using:
|
@@ -121,13 +121,14 @@ class Proxy
|
|
121
121
|
total_size = opts[:request].content_length unless opts[:request].content_length.nil?
|
122
122
|
end
|
123
123
|
|
124
|
-
buff =
|
124
|
+
buff = ''
|
125
125
|
read = 0
|
126
126
|
|
127
|
+
|
127
128
|
if total_size
|
128
|
-
chunk_size = 1024
|
129
|
-
else
|
130
129
|
chunk_size = [ 1024, total_size ].min
|
130
|
+
else
|
131
|
+
chunk_size = 1024
|
131
132
|
end
|
132
133
|
|
133
134
|
if chunk_size > 0
|
@@ -154,11 +155,17 @@ class Proxy
|
|
154
155
|
|
155
156
|
def html_streaming( request, response, from, to )
|
156
157
|
buff = ''
|
157
|
-
loop do
|
158
|
-
from.read 1024, buff
|
159
158
|
|
160
|
-
|
159
|
+
if response.content_length.nil?
|
160
|
+
loop do
|
161
|
+
from.read 1024, buff
|
162
|
+
|
163
|
+
break unless buff.size > 0
|
161
164
|
|
165
|
+
response << buff
|
166
|
+
end
|
167
|
+
else
|
168
|
+
from.read response.content_length, buff
|
162
169
|
response << buff
|
163
170
|
end
|
164
171
|
|
@@ -13,11 +13,12 @@ This project is released under the GPL 3 license.
|
|
13
13
|
module Proxy
|
14
14
|
|
15
15
|
class Response
|
16
|
-
attr_reader :content_type, :content_length, :headers, :code, :headers_done
|
16
|
+
attr_reader :content_type, :charset, :content_length, :headers, :code, :headers_done
|
17
17
|
attr_accessor :body
|
18
18
|
|
19
19
|
def initialize
|
20
20
|
@content_type = nil
|
21
|
+
@charset = 'UTF-8'
|
21
22
|
@content_length = nil
|
22
23
|
@body = ''
|
23
24
|
@code = nil
|
@@ -43,22 +44,25 @@ class Response
|
|
43
44
|
def <<(line)
|
44
45
|
# we already parsed the heders, collect response body
|
45
46
|
if @headers_done
|
46
|
-
@body += line
|
47
|
+
@body += line.force_encoding( @charset )
|
47
48
|
else
|
48
49
|
# parse the response status
|
49
50
|
if @code.nil? and line =~ /^HTTP\/[\d\.]+\s+(.+)/
|
50
51
|
@code = $1.chomp
|
51
52
|
|
52
|
-
|
53
|
-
elsif line =~ /^Content-Type
|
53
|
+
# parse the content type
|
54
|
+
elsif line =~ /^Content-Type:\s*([^;]+).*/i
|
54
55
|
@content_type = $1.chomp
|
56
|
+
if line =~ /^.+;\s*charset=(.+)/i
|
57
|
+
@charset = $1.chomp
|
58
|
+
end
|
55
59
|
|
56
|
-
|
60
|
+
# parse content length
|
57
61
|
elsif line =~ /^Content-Length:\s+(\d+)\s*$/i
|
58
62
|
@content_length = $1.to_i
|
59
63
|
|
60
|
-
|
61
|
-
elsif line.chomp
|
64
|
+
# last line, we're done with the headers
|
65
|
+
elsif line.chomp.empty?
|
62
66
|
@headers_done = true
|
63
67
|
|
64
68
|
end
|
@@ -14,23 +14,29 @@ require 'colorize'
|
|
14
14
|
require 'resolv'
|
15
15
|
|
16
16
|
class HttpsParser < BaseParser
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
17
|
+
@@prev = nil
|
18
|
+
|
19
|
+
def on_packet( pkt )
|
20
|
+
begin
|
21
|
+
if pkt.tcp_dst == 443
|
22
|
+
# the DNS resolution could take a while and block other parsers.
|
23
|
+
Thread.new do
|
24
|
+
begin
|
25
|
+
hostname = Resolv.getname pkt.ip_daddr
|
26
|
+
rescue
|
27
|
+
hostname = pkt.ip_daddr.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
if @@prev.nil? or @@prev != hostname
|
31
|
+
Logger.write "[#{pkt.ip_saddr}:#{pkt.tcp_src} > #{pkt.ip_daddr}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
|
32
|
+
'[HTTPS] '.green +
|
33
|
+
"https://#{hostname}/".yellow
|
34
|
+
|
35
|
+
@@prev = hostname
|
32
36
|
end
|
33
|
-
rescue
|
34
37
|
end
|
38
|
+
end
|
39
|
+
rescue
|
35
40
|
end
|
41
|
+
end
|
36
42
|
end
|
@@ -27,7 +27,7 @@ class Sniffer
|
|
27
27
|
|
28
28
|
setup( ctx )
|
29
29
|
|
30
|
-
|
30
|
+
self.stream.each do |p|
|
31
31
|
begin
|
32
32
|
parsed = Packet.parse p
|
33
33
|
rescue Exception => e
|
@@ -44,6 +44,16 @@ class Sniffer
|
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
+
def self.stream
|
48
|
+
if @@ctx.options[:sniffer_src].nil?
|
49
|
+
@@cap.stream
|
50
|
+
else
|
51
|
+
Logger.info "Reading packets from #{@@ctx.options[:sniffer_src]} ..."
|
52
|
+
|
53
|
+
PcapFile.file_to_array @@ctx.options[:sniffer_src]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
47
57
|
def self.skip_packet?( pkt )
|
48
58
|
!@@ctx.options[:local] and
|
49
59
|
( pkt.ip_saddr == @@ctx.ifconfig[:ip_saddr] or
|
@@ -147,7 +147,10 @@ class ArpSpoofer < ISpoofer
|
|
147
147
|
@ctx.firewall.enable_forwarding( @forwarding )
|
148
148
|
|
149
149
|
@running = false
|
150
|
-
|
150
|
+
begin
|
151
|
+
@spoof_thread.exit
|
152
|
+
rescue
|
153
|
+
end
|
151
154
|
|
152
155
|
Logger.info "Restoring ARP table of #{@ctx.targets.size} targets ..."
|
153
156
|
|
data/lib/bettercap/version.rb
CHANGED
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.1.
|
4
|
+
version: 1.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simone Margaritelli
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -91,7 +91,6 @@ files:
|
|
91
91
|
- lib/bettercap/discovery/arp.rb
|
92
92
|
- lib/bettercap/discovery/base.rb
|
93
93
|
- lib/bettercap/discovery/icmp.rb
|
94
|
-
- lib/bettercap/discovery/syn.rb
|
95
94
|
- lib/bettercap/discovery/udp.rb
|
96
95
|
- lib/bettercap/error.rb
|
97
96
|
- lib/bettercap/factories/firewall_factory.rb
|
@@ -142,7 +141,6 @@ files:
|
|
142
141
|
- test/sniffer/parsers/url_parser_test.rb
|
143
142
|
- test/target_test.rb
|
144
143
|
- test/test_helper.rb
|
145
|
-
- test_https_proxy.rb
|
146
144
|
homepage: http://github.com/evilsocket/bettercap
|
147
145
|
licenses:
|
148
146
|
- GPL3
|
@@ -1,45 +0,0 @@
|
|
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/discovery/base'
|
13
|
-
|
14
|
-
# Send SYN probes trying to filling the ARP table.
|
15
|
-
class SynAgent < BaseAgent
|
16
|
-
private
|
17
|
-
|
18
|
-
def send_probe( ip )
|
19
|
-
pkt = PacketFu::TCPPacket.new
|
20
|
-
pkt.ip_v = 4
|
21
|
-
pkt.ip_hl = 5
|
22
|
-
pkt.ip_tos = 0
|
23
|
-
pkt.ip_len = 20
|
24
|
-
pkt.ip_frag = 0
|
25
|
-
pkt.ip_ttl = 115
|
26
|
-
pkt.ip_proto = 6 # TCP
|
27
|
-
pkt.ip_saddr = @local_ip
|
28
|
-
pkt.ip_daddr = ip
|
29
|
-
pkt.payload = "\xC\x0\xF\xF\xE\xE"
|
30
|
-
pkt.tcp_flags.ack = 0
|
31
|
-
pkt.tcp_flags.fin = 0
|
32
|
-
pkt.tcp_flags.psh = 0
|
33
|
-
pkt.tcp_flags.rst = 0
|
34
|
-
pkt.tcp_flags.syn = 1
|
35
|
-
pkt.tcp_flags.urg = 0
|
36
|
-
pkt.tcp_ecn = 0
|
37
|
-
pkt.tcp_win = 8192
|
38
|
-
pkt.tcp_hlen = 5
|
39
|
-
pkt.tcp_dst = rand(1024..65535)
|
40
|
-
pkt.recalc
|
41
|
-
|
42
|
-
pkt.to_w( @ifconfig[:iface] )
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
data/test_https_proxy.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
require 'socket'
|
3
|
-
|
4
|
-
sock = TCPSocket.new( '172.20.10.2', 8083 )
|
5
|
-
|
6
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
7
|
-
|
8
|
-
# we need this? :P ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER)
|
9
|
-
|
10
|
-
socket = OpenSSL::SSL::SSLSocket.new(sock, ctx).tap do |socket|
|
11
|
-
socket.sync_close = true
|
12
|
-
socket.connect
|
13
|
-
|
14
|
-
socket.write "GET / HTTP/1.1\n" +
|
15
|
-
"Host: www.facebook.com\n" +
|
16
|
-
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\n" +
|
17
|
-
"Accept-encoding: gzip, deflate, sdch\n" +
|
18
|
-
"Accept-language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4,la;q=0.2\n" +
|
19
|
-
"Cache-control: max-age=0\n" +
|
20
|
-
"User-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36\n" +
|
21
|
-
"\n\n"
|
22
|
-
|
23
|
-
while line = socket.gets # Read lines from socket
|
24
|
-
puts line # and print them
|
25
|
-
end
|
26
|
-
|
27
|
-
socket.close
|
28
|
-
end
|
29
|
-
|