bettercap 1.4.3 → 1.4.4
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/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
|