bettercap 1.4.2 → 1.4.3
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 +8 -4
- data/lib/bettercap/monkey/celluloid/io/udp_socket.rb +28 -0
- data/lib/bettercap/network/target.rb +12 -9
- data/lib/bettercap/options.rb +27 -2
- data/lib/bettercap/proxy/proxy.rb +15 -14
- data/lib/bettercap/proxy/response.rb +87 -68
- data/lib/bettercap/proxy/sslstrip/strip.rb +41 -25
- data/lib/bettercap/proxy/stream_logger.rb +11 -8
- data/lib/bettercap/proxy/streamer.rb +1 -1
- data/lib/bettercap/version.rb +1 -1
- metadata +3 -3
- data/CONTRIBUTING.md +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 004df8ac9f02a6075dffa69038fb4b8fd02b8e8b
|
4
|
+
data.tar.gz: 720d7f1c5bcc08ad97eb917291d9a8c8463cc892
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9a2e1722001bef5dbe99bc80e7c87b1ebfb3f2d93e85de449d272525332f04e098ad92d530b1b2e263da6e32a095c5044bb51bf543b6465e494da5b7dd249cd
|
7
|
+
data.tar.gz: eb475421115056754c756550a5379970532fc2b018a321a50bd1a3fdec16929f7cef71393ea37c722df4574ca68eec4f1a62cfc77a369b13ff6395c6bf7f9891
|
data/lib/bettercap.rb
CHANGED
@@ -28,9 +28,11 @@ Object.send :remove_const, :Config rescue nil
|
|
28
28
|
Config = RbConfig
|
29
29
|
|
30
30
|
def bettercap_autoload( path = '' )
|
31
|
-
dir
|
32
|
-
deps
|
33
|
-
files
|
31
|
+
dir = File.dirname(__FILE__) + "/bettercap/#{path}"
|
32
|
+
deps = []
|
33
|
+
files = []
|
34
|
+
monkey = []
|
35
|
+
|
34
36
|
Dir[dir+"**/*.rb"].each do |filename|
|
35
37
|
filename = filename.gsub( dir, '' ).gsub('.rb', '')
|
36
38
|
filename = "bettercap/#{path}#{filename}"
|
@@ -38,13 +40,15 @@ def bettercap_autoload( path = '' )
|
|
38
40
|
unless filename =~ /.+\/inject[a-z]+$/i
|
39
41
|
if filename.end_with?('/base')
|
40
42
|
deps << filename
|
43
|
+
elsif filename.include?('monkey')
|
44
|
+
monkey << filename
|
41
45
|
else
|
42
46
|
files << filename
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
47
|
-
( deps + files ).each do |file|
|
51
|
+
( deps + files + monkey ).each do |file|
|
48
52
|
require file
|
49
53
|
end
|
50
54
|
end
|
@@ -0,0 +1,28 @@
|
|
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
|
+
# Monkey patching fix for https://github.com/evilsocket/bettercap/issues/154
|
15
|
+
module Celluloid
|
16
|
+
module IO
|
17
|
+
class UDPSocket
|
18
|
+
def initialize(address_family = ::Socket::AF_INET)
|
19
|
+
begin
|
20
|
+
@socket = ::UDPSocket.new(address_family)
|
21
|
+
rescue Errno::EMFILE
|
22
|
+
sleep 0.5
|
23
|
+
retry
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -38,6 +38,7 @@ class Target
|
|
38
38
|
NBNS_REQUEST = "\x82\x28\x0\x0\x0\x1\x0\x0\x0\x0\x0\x0\x20\x43\x4B\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x0\x0\x21\x0\x1"
|
39
39
|
|
40
40
|
@@prefixes = nil
|
41
|
+
@@lock = Mutex.new
|
41
42
|
|
42
43
|
# Create a +Target+ object given its +ip+ and (optional) +mac+ address.
|
43
44
|
# The +ip+ argument could also be a MAC address itself, in this case the
|
@@ -143,17 +144,19 @@ private
|
|
143
144
|
|
144
145
|
# Lookup the given +mac+ address in order to find its vendor.
|
145
146
|
def self.lookup_vendor( mac )
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
147
|
+
@@lock.synchronize {
|
148
|
+
if @@prefixes.nil?
|
149
|
+
Logger.debug 'Preloading hardware vendor prefixes ...'
|
150
|
+
|
151
|
+
@@prefixes = {}
|
152
|
+
filename = File.dirname(__FILE__) + '/hw-prefixes'
|
153
|
+
File.open( filename ).each do |line|
|
154
|
+
if line =~ /^([A-F0-9]{6})\s(.+)$/
|
155
|
+
@@prefixes[$1] = $2
|
156
|
+
end
|
154
157
|
end
|
155
158
|
end
|
156
|
-
|
159
|
+
}
|
157
160
|
|
158
161
|
@@prefixes[ mac.split(':')[0,3].join('').upcase ]
|
159
162
|
end
|
data/lib/bettercap/options.rb
CHANGED
@@ -79,6 +79,8 @@ class Options
|
|
79
79
|
attr_accessor :custom_https_proxy
|
80
80
|
# Custom HTTPS transparent proxy port.
|
81
81
|
attr_accessor :custom_https_proxy_port
|
82
|
+
# Custom list of redirections.
|
83
|
+
attr_accessor :custom_redirections
|
82
84
|
# If true, BetterCap::Network::Servers::HTTPD will be enabled.
|
83
85
|
attr_accessor :httpd
|
84
86
|
# The port to bind HTTP server to.
|
@@ -145,6 +147,8 @@ class Options
|
|
145
147
|
@custom_https_proxy = nil
|
146
148
|
@custom_https_proxy_port = 8083
|
147
149
|
|
150
|
+
@custom_redirections = []
|
151
|
+
|
148
152
|
@sslstrip = true
|
149
153
|
|
150
154
|
@httpd = false
|
@@ -312,6 +316,10 @@ class Options
|
|
312
316
|
ctx.options.custom_https_proxy_port = v.to_i
|
313
317
|
end
|
314
318
|
|
319
|
+
opts.on( '--custom-redirection RULE', 'Apply a custom port redirection, the format of the rule is "PROTOCOL ORIGINAL_PORT NEW_PORT". For instance "TCP 21 2100" will redirect all TCP traffic going to port 21, to port 2100.' ) do |v|
|
320
|
+
ctx.options.parse_redirection!( v )
|
321
|
+
end
|
322
|
+
|
315
323
|
opts.on( '--httpd', 'Enable HTTP server, default to false.' ) do
|
316
324
|
ctx.options.httpd = true
|
317
325
|
end
|
@@ -448,6 +456,19 @@ class Options
|
|
448
456
|
end
|
449
457
|
end
|
450
458
|
|
459
|
+
# Parse a custom redirection rule.
|
460
|
+
def parse_redirection!(rule)
|
461
|
+
if rule =~ /^((TCP)|(UDP))\s+(\d+)\s+(\d+)$/i
|
462
|
+
@custom_redirections << {
|
463
|
+
:proto => $1.upcase,
|
464
|
+
:from => $4.to_i,
|
465
|
+
:to => $5.to_i
|
466
|
+
}
|
467
|
+
else
|
468
|
+
raise BetterCap::Error, 'Invalid custom redirection rule specified.'
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
451
472
|
# Parse a comma separated list of ports and return an array containing only
|
452
473
|
# valid ports, raise BetterCap::Error if that array is empty.
|
453
474
|
def parse_ports(value)
|
@@ -511,7 +532,7 @@ class Options
|
|
511
532
|
def get_redirections ifconfig
|
512
533
|
redirections = []
|
513
534
|
|
514
|
-
if @dnsd or @proxy
|
535
|
+
if @dnsd or ( @proxy and @sslstrip )
|
515
536
|
redirections << redir( ifconfig[:ip_saddr], 53, @dnsd_port )
|
516
537
|
redirections << redir( ifconfig[:ip_saddr], 53, @dnsd_port, 'UDP' )
|
517
538
|
end
|
@@ -540,6 +561,10 @@ class Options
|
|
540
561
|
end
|
541
562
|
end
|
542
563
|
|
564
|
+
@custom_redirections.each do |r|
|
565
|
+
redirections << redir( ifconfig[:ip_saddr], r[:from], r[:to], r[:proto] )
|
566
|
+
end
|
567
|
+
|
543
568
|
redirections
|
544
569
|
end
|
545
570
|
|
@@ -555,7 +580,7 @@ class Options
|
|
555
580
|
'https-proxy' => ( proxy_https ? on : off ),
|
556
581
|
'sslstrip' => ( ( proxy and sslstrip ) ? on : off ),
|
557
582
|
'http-server' => ( httpd ? on : off ),
|
558
|
-
'dns-server' => ( ( sslstrip or dnsd ) ? on : off )
|
583
|
+
'dns-server' => ( ( ( proxy and sslstrip ) or dnsd ) ? on : off )
|
559
584
|
}
|
560
585
|
|
561
586
|
msg = "Starting [ "
|
@@ -22,18 +22,19 @@ class Proxy
|
|
22
22
|
# use the specified +processor+ routine for each request.
|
23
23
|
# If +is_https+ is true a HTTPS proxy will be created, otherwise a HTTP one.
|
24
24
|
def initialize( address, port, is_https, processor )
|
25
|
-
@socket
|
26
|
-
@address
|
27
|
-
@port
|
28
|
-
@is_https
|
29
|
-
@type
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
33
|
-
@
|
34
|
-
@
|
35
|
-
@
|
36
|
-
@
|
25
|
+
@socket = nil
|
26
|
+
@address = address
|
27
|
+
@port = port
|
28
|
+
@is_https = is_https
|
29
|
+
@type = is_https ? 'HTTPS' : 'HTTP'
|
30
|
+
@upstream_port = is_https ? 443 : 80
|
31
|
+
@sslserver = nil
|
32
|
+
@sslcontext = nil
|
33
|
+
@server = nil
|
34
|
+
@main_thread = nil
|
35
|
+
@running = false
|
36
|
+
@streamer = Streamer.new processor
|
37
|
+
@local_ips = []
|
37
38
|
|
38
39
|
begin
|
39
40
|
@local_ips = Socket.ip_address_list.collect { |x| x.ip_address }
|
@@ -49,7 +50,7 @@ class Proxy
|
|
49
50
|
begin
|
50
51
|
client_worker client
|
51
52
|
rescue Exception => e
|
52
|
-
Logger.warn "Client worker
|
53
|
+
Logger.warn "Client worker error: #{e.message}"
|
53
54
|
Logger.exception e
|
54
55
|
end
|
55
56
|
end
|
@@ -125,7 +126,7 @@ class Proxy
|
|
125
126
|
|
126
127
|
# Handle a new +client+.
|
127
128
|
def client_worker( client )
|
128
|
-
request = Request.new @
|
129
|
+
request = Request.new @upstream_port
|
129
130
|
|
130
131
|
begin
|
131
132
|
Logger.debug 'Reading request ...'
|
@@ -15,6 +15,12 @@ module BetterCap
|
|
15
15
|
module Proxy
|
16
16
|
# HTTP response parser.
|
17
17
|
class Response
|
18
|
+
# HTTP protocol version
|
19
|
+
attr_accessor :version
|
20
|
+
# Response status code.
|
21
|
+
attr_accessor :code
|
22
|
+
# Response status message
|
23
|
+
attr_accessor :status
|
18
24
|
# Response content type.
|
19
25
|
attr_reader :content_type
|
20
26
|
# Response charset, default to UTF-8.
|
@@ -25,10 +31,6 @@ class Response
|
|
25
31
|
attr_reader :chunked
|
26
32
|
# A list of response headers.
|
27
33
|
attr_accessor :headers
|
28
|
-
# Response status code.
|
29
|
-
attr_accessor :code
|
30
|
-
# True if the parser finished to parse the headers, otherwise false.
|
31
|
-
attr_reader :headers_done
|
32
34
|
# Response body.
|
33
35
|
attr_accessor :body
|
34
36
|
|
@@ -67,12 +69,14 @@ class Response
|
|
67
69
|
|
68
70
|
# Initialize this response object state.
|
69
71
|
def initialize
|
72
|
+
@version = '1.1'
|
73
|
+
@code = 200
|
74
|
+
@status = 'OK'
|
70
75
|
@content_type = nil
|
71
76
|
@charset = 'UTF-8'
|
72
77
|
@content_length = nil
|
73
|
-
@body =
|
74
|
-
@
|
75
|
-
@headers = []
|
78
|
+
@body = nil
|
79
|
+
@headers = {}
|
76
80
|
@headers_done = false
|
77
81
|
@chunked = false
|
78
82
|
end
|
@@ -100,37 +104,53 @@ class Response
|
|
100
104
|
def <<(line)
|
101
105
|
# we already parsed the heders, collect response body
|
102
106
|
if @headers_done
|
107
|
+
@body = '' if @body.nil?
|
103
108
|
@body << line.force_encoding( @charset )
|
104
109
|
else
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
110
|
+
chomped = line.chomp
|
111
|
+
Logger.debug " RESPONSE LINE: '#{chomped}'"
|
112
|
+
|
113
|
+
# is this the first line 'HTTP/<VERSION> <CODE> <STATUS>' ?
|
114
|
+
if chomped =~ /^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/
|
115
|
+
@version = $1
|
116
|
+
@code = $2.to_i
|
117
|
+
@status = $3
|
118
|
+
|
119
|
+
# collect and fix headers
|
120
|
+
elsif chomped =~ /^([^:\s]+)\s*:\s*(.+)$/i
|
121
|
+
name = $1
|
122
|
+
value = $2
|
123
|
+
|
124
|
+
if name == 'Content-Type'
|
125
|
+
@content_type = value
|
126
|
+
if value =~ /^(.+);\s*charset=(.+)/i
|
127
|
+
@content_type = $1
|
128
|
+
@charset = $2.chomp
|
129
|
+
end
|
130
|
+
elsif name == 'Content-Length'
|
131
|
+
@content_length = value.to_i
|
132
|
+
# check if we have a chunked encoding
|
133
|
+
elsif name == 'Transfer-Encoding' and value == 'chunked'
|
134
|
+
@chunked = true
|
135
|
+
name = nil
|
136
|
+
value = nil
|
116
137
|
end
|
117
138
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
139
|
+
unless name.nil? or value.nil?
|
140
|
+
if @headers.has_key?(name)
|
141
|
+
if @headers[name].is_a?(Array)
|
142
|
+
@headers[name] << value
|
143
|
+
else
|
144
|
+
@headers[name] = [ @headers[name], value ]
|
145
|
+
end
|
146
|
+
else
|
147
|
+
@headers[name] = value
|
148
|
+
end
|
149
|
+
end
|
127
150
|
# last line, we're done with the headers
|
128
|
-
elsif
|
151
|
+
elsif chomped.empty?
|
129
152
|
@headers_done = true
|
130
|
-
|
131
153
|
end
|
132
|
-
|
133
|
-
@headers << line.chomp unless line.nil?
|
134
154
|
end
|
135
155
|
end
|
136
156
|
|
@@ -141,62 +161,61 @@ class Response
|
|
141
161
|
|
142
162
|
# Return the value of header with +name+ or an empty string.
|
143
163
|
def [](name)
|
144
|
-
@headers.
|
145
|
-
if header =~ /^#{name}:\s*(.+)$/i
|
146
|
-
return $1
|
147
|
-
end
|
148
|
-
end
|
149
|
-
""
|
164
|
+
( @headers.has_key?(name) ? @headers[name] : "" )
|
150
165
|
end
|
151
166
|
|
152
167
|
# If the header with +name+ is found, then a +value+ is assigned to it,
|
153
168
|
# otherwise it's created.
|
154
169
|
def []=(name, value)
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
else
|
161
|
-
@headers[i] = "#{name}: #{value}"
|
162
|
-
end
|
163
|
-
|
164
|
-
found = true
|
165
|
-
break
|
170
|
+
if @headers.has_key?(name)
|
171
|
+
if value.nil?
|
172
|
+
@headers.delete(name)
|
173
|
+
else
|
174
|
+
@headers[name] = value
|
166
175
|
end
|
167
|
-
|
168
|
-
|
169
|
-
unless found or value.nil?
|
170
|
-
@headers << "#{name}: #{value}"
|
176
|
+
elsif !value.nil?
|
177
|
+
@headers[name] = value
|
171
178
|
end
|
172
179
|
end
|
173
180
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
181
|
+
# Search for header +name+ and apply a gsub substitution:
|
182
|
+
# value.gsub( +search+, +replace+ )
|
183
|
+
def patch_header!( name, search, replace )
|
184
|
+
value = self[name]
|
185
|
+
unless value.empty?
|
186
|
+
patched = []
|
187
|
+
if value.is_a?(Array)
|
188
|
+
value.each do |v|
|
189
|
+
patched << v.gsub( search, replace )
|
190
|
+
end
|
191
|
+
else
|
192
|
+
patched << value.gsub( search, replace )
|
178
193
|
end
|
194
|
+
|
195
|
+
self[name] = patched
|
179
196
|
end
|
180
197
|
end
|
181
198
|
|
182
199
|
# Return a string representation of this response object, patching the
|
183
200
|
# Content-Length header if the #body was modified.
|
184
201
|
def to_s
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
202
|
+
# update content length in case the body was modified.
|
203
|
+
if @headers.has_key?('Content-Length')
|
204
|
+
@headers['Content-Length'] = @body.nil?? 0 : @body.bytesize
|
205
|
+
end
|
206
|
+
|
207
|
+
s = "HTTP/#{@version} #{@code} #{@status}\n"
|
208
|
+
@headers.each do |name,value|
|
209
|
+
if value.is_a?(Array)
|
210
|
+
value.each do |v|
|
211
|
+
s << "#{name}: #{v}\n"
|
195
212
|
end
|
213
|
+
else
|
214
|
+
s << "#{name}: #{value}\n"
|
196
215
|
end
|
197
216
|
end
|
198
|
-
|
199
|
-
|
217
|
+
s << "\n" + ( @body.nil?? "\n" : @body )
|
218
|
+
s
|
200
219
|
end
|
201
220
|
end
|
202
221
|
|
@@ -101,11 +101,12 @@ class Strip
|
|
101
101
|
HTTPS_URL_RE = /(https:\/\/[^"'\/]+)/i
|
102
102
|
|
103
103
|
# Create an instance of this object.
|
104
|
-
def initialize
|
104
|
+
def initialize( ctx )
|
105
105
|
@stripped = []
|
106
106
|
@cookies = CookieMonitor.new
|
107
107
|
@favicon = Response.from_file( File.dirname(__FILE__) + '/lock.ico', 'image/x-icon' )
|
108
|
-
@resolver = BetterCap::Network::Servers::DNSD.new
|
108
|
+
@resolver = BetterCap::Network::Servers::DNSD.new( nil, ctx.ifconfig[:ip_saddr], ctx.options.dnsd_port )
|
109
|
+
|
109
110
|
@resolver.start
|
110
111
|
end
|
111
112
|
|
@@ -162,22 +163,30 @@ class Strip
|
|
162
163
|
|
163
164
|
private
|
164
165
|
|
166
|
+
# Headers to patch both in requests and responses.
|
167
|
+
HEADERS_TO_PATCH = {
|
168
|
+
:req => {
|
169
|
+
'Accept-Encoding' => nil,
|
170
|
+
'If-None-Match' => nil,
|
171
|
+
'If-Modified-Since' => nil,
|
172
|
+
'Upgrade-Insecure-Requests' => nil,
|
173
|
+
'Pragma' => 'no-cache'
|
174
|
+
},
|
175
|
+
|
176
|
+
:res => {
|
177
|
+
'X-Frame-Options' => nil,
|
178
|
+
'X-Content-Type-Options' => nil,
|
179
|
+
'X-Xss-Protection' => nil,
|
180
|
+
'Strict-Transport-Security' => nil,
|
181
|
+
'Content-Security-Policy' => nil
|
182
|
+
}
|
183
|
+
}.freeze
|
184
|
+
|
165
185
|
# Clean some headers from +r+.
|
166
186
|
def process_headers!(r)
|
167
|
-
|
168
|
-
|
169
|
-
r[
|
170
|
-
r['If-None-Match'] = nil
|
171
|
-
r['If-Modified-Since'] = nil
|
172
|
-
r['Upgrade-Insecure-Requests'] = nil
|
173
|
-
r['Pragma'] = 'no-cache'
|
174
|
-
# clean response
|
175
|
-
else
|
176
|
-
r['X-Frame-Options'] = nil
|
177
|
-
r['X-Content-Type-Options'] = nil
|
178
|
-
r['X-Xss-Protection'] = nil
|
179
|
-
r['Strict-Transport-Security'] = nil
|
180
|
-
r['Content-Security-Policy'] = nil
|
187
|
+
what = r.is_a?(BetterCap::Proxy::Request) ? :req : :res
|
188
|
+
HEADERS_TO_PATCH[what].each do |key,value|
|
189
|
+
r[key] = value;
|
181
190
|
end
|
182
191
|
end
|
183
192
|
|
@@ -247,7 +256,6 @@ class Strip
|
|
247
256
|
# no cookies set, just a normal http -> https redirect
|
248
257
|
if response['Set-Cookie'].empty?
|
249
258
|
Logger.info "[#{'SSLSTRIP'.green} #{request.client}] Found redirect to HTTPS '#{original}' -> '#{stripped}'."
|
250
|
-
|
251
259
|
# The request will be retried on port 443 if MAX_REDIRECTS is not reached.
|
252
260
|
request.port = 443
|
253
261
|
# retry the request if possible
|
@@ -257,11 +265,9 @@ class Strip
|
|
257
265
|
Logger.info "[#{'SSLSTRIP'.green} #{request.client}] Found redirect to HTTPS ( with cookies ) '#{original}' -> '#{stripped}'."
|
258
266
|
# we know this session, do not kill it!
|
259
267
|
@cookies.add!( request )
|
260
|
-
# remove the 'secure'
|
261
|
-
|
262
|
-
|
263
|
-
end
|
264
|
-
|
268
|
+
# remove the 'secure' flag from every cookie.
|
269
|
+
# ref: https://www.owasp.org/index.php/SecureFlag
|
270
|
+
response.patch_header!( 'Set-Cookie', /secure/, '' )
|
265
271
|
# do not retry request
|
266
272
|
return false
|
267
273
|
end
|
@@ -276,6 +282,10 @@ class Strip
|
|
276
282
|
begin
|
277
283
|
response.body.scan( HTTPS_URL_RE ).uniq.each do |link|
|
278
284
|
if link[0].include?('.')
|
285
|
+
# yeah, some idiots are using \ instead of / as a path -.-
|
286
|
+
if link[0].end_with?('\\')
|
287
|
+
link[0][-1] = '/'
|
288
|
+
end
|
279
289
|
links << StrippedObject.process( link[0] )
|
280
290
|
end
|
281
291
|
end
|
@@ -296,9 +306,15 @@ class Strip
|
|
296
306
|
private
|
297
307
|
|
298
308
|
def add_stripped_object( o )
|
299
|
-
|
300
|
-
|
301
|
-
|
309
|
+
begin
|
310
|
+
stripped_hostname = o.stripped_hostname
|
311
|
+
original_address = IPSocket.getaddress( o.original_hostname )
|
312
|
+
@stripped << o
|
313
|
+
# make sure we're able to resolve the stripped domain
|
314
|
+
@resolver.add_rule( stripped_hostname, original_address )
|
315
|
+
rescue Exception => e
|
316
|
+
Logger.exception(e)
|
317
|
+
end
|
302
318
|
end
|
303
319
|
end
|
304
320
|
|
@@ -27,6 +27,7 @@ class StreamLogger
|
|
27
27
|
}
|
28
28
|
|
29
29
|
@@services = nil
|
30
|
+
@@lock = Mutex.new
|
30
31
|
|
31
32
|
# Search for the +addr+ IP address inside the list of collected targets and return
|
32
33
|
# its compact string representation ( @see BetterCap::Target#to_s_compact ).
|
@@ -47,15 +48,17 @@ class StreamLogger
|
|
47
48
|
|
48
49
|
# Given +proto+ and +port+ return the network service name if possible.
|
49
50
|
def self.service( proto, port )
|
50
|
-
|
51
|
-
@@services
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
@@lock.synchronize {
|
52
|
+
if @@services.nil?
|
53
|
+
@@services = { :tcp => {}, :udp => {} }
|
54
|
+
filename = File.dirname(__FILE__) + '/../network/services'
|
55
|
+
File.open( filename ).each do |line|
|
56
|
+
if line =~ /([^\s]+)\s+(\d+)\/([a-z]+).*/i
|
57
|
+
@@services[$3.to_sym][$2.to_i] = $1
|
58
|
+
end
|
56
59
|
end
|
57
60
|
end
|
58
|
-
|
61
|
+
}
|
59
62
|
|
60
63
|
if @@services.has_key?(proto) and @@services[proto].has_key?(port)
|
61
64
|
@@services[proto][port]
|
@@ -146,7 +149,7 @@ class StreamLogger
|
|
146
149
|
def self.log_http( request, response )
|
147
150
|
response_s = "( #{response.content_type} )"
|
148
151
|
request_s = request.to_url( request.post?? nil : @@MAX_REQ_SIZE )
|
149
|
-
code = response.code[0]
|
152
|
+
code = response.code.to_s[0]
|
150
153
|
|
151
154
|
if @@CODE_COLORS.has_key? code
|
152
155
|
response_s += " [#{response.code}]".send( @@CODE_COLORS[ code ] )
|
@@ -20,7 +20,7 @@ class Streamer
|
|
20
20
|
def initialize( processor )
|
21
21
|
@processor = processor
|
22
22
|
@ctx = Context.get
|
23
|
-
@sslstrip = SSLStrip::Strip.new if @ctx.options.sslstrip
|
23
|
+
@sslstrip = SSLStrip::Strip.new( @ctx ) if @ctx.options.sslstrip
|
24
24
|
end
|
25
25
|
|
26
26
|
# Return true if the +request+ was stripped.
|
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.3'
|
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.3
|
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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -115,7 +115,6 @@ executables:
|
|
115
115
|
extensions: []
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
|
-
- CONTRIBUTING.md
|
119
118
|
- LICENSE.md
|
120
119
|
- README.md
|
121
120
|
- bin/bettercap
|
@@ -134,6 +133,7 @@ files:
|
|
134
133
|
- lib/bettercap/firewalls/redirection.rb
|
135
134
|
- lib/bettercap/loader.rb
|
136
135
|
- lib/bettercap/logger.rb
|
136
|
+
- lib/bettercap/monkey/celluloid/io/udp_socket.rb
|
137
137
|
- lib/bettercap/monkey/packetfu/utils.rb
|
138
138
|
- lib/bettercap/network/arp_reader.rb
|
139
139
|
- lib/bettercap/network/hw-prefixes
|
data/CONTRIBUTING.md
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
# How To Contribute
|
2
|
-
|
3
|
-
As any other open source projects, there're many ways you can contribute to bettercap depending on your skills as a developer or will to help as a user, but first
|
4
|
-
of all let me thank you for your help! <3
|
5
|
-
|
6
|
-
### Submitting Issues
|
7
|
-
|
8
|
-
If you find bugs or inconsistencies while using bettercap, you can create an **Issue** using the [GitHub Issue tracker](https://github.com/evilsocket/bettercap/issues), but before doing that please make sure that:
|
9
|
-
|
10
|
-
* You are using a relatively new Ruby version ( >= 1.9 ) : `ruby -v`.
|
11
|
-
* Your GEM environment is configured properly and updated : `sudo gem update`.
|
12
|
-
* You are using the latest version of bettercap : `bettercap --check-updates`.
|
13
|
-
* The bug you're reporting is actually related to bettercap and not to one of the other GEMs.
|
14
|
-
|
15
|
-
Once you've gone through this list, open an issue and please give us as much as informations as possible in order for us to fix the bug as soon as possible:
|
16
|
-
|
17
|
-
* Your OS version.
|
18
|
-
* Ruby version you're using.
|
19
|
-
* Full output of the error ( exception backtrace, error message, etc ).
|
20
|
-
* Your network configuration: `ifconfig -a`
|
21
|
-
|
22
|
-
Also, you should attach to the issue a debug log that you can generate with:
|
23
|
-
|
24
|
-
[sudo|rvmsudo] bettercap [arguments you are using for testing] --debug --log=debug.log
|
25
|
-
|
26
|
-
Wait for the error to happen then close bettercap and paste the **debug.log** file inside the issue.
|
27
|
-
|
28
|
-
### Pull Requests
|
29
|
-
|
30
|
-
If you know how to code in Ruby and have ideas to improve bettercap, you're very welcome to send us pull requests, we'll be happy to merge them whenever they comply to the following rules:
|
31
|
-
|
32
|
-
* You have at least manually tested your code, ideally you've created actual tests for it.
|
33
|
-
* Respect our coding standard, 2 spaces indentation and modular code.
|
34
|
-
* There're no conflicts with the current dev branch.
|
35
|
-
* Your commit messages are enough explanatory to us.
|
36
|
-
|
37
|
-
There're plenty of things you can to do improve the software:
|
38
|
-
|
39
|
-
* Implement a new proxy module and push it to the [dedicated repository](https://github.com/evilsocket/bettercap-proxy-modules).
|
40
|
-
* Implement a new [Spoofer module](https://github.com/evilsocket/bettercap/blob/master/lib/bettercap/spoofers/arp.rb).
|
41
|
-
* Implement a new [Sniffer credentials parser](https://github.com/evilsocket/bettercap/blob/master/lib/bettercap/sniffer/parsers/post.rb).
|
42
|
-
* Fix, extend or improve the core.
|