ssrf_proxy 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE.md +22 -0
- data/README.md +222 -0
- data/bin/console +24 -0
- data/bin/setup +6 -0
- data/bin/ssrf-proxy +170 -153
- data/lib/ssrf_proxy/http.rb +911 -1227
- data/lib/ssrf_proxy/server.rb +298 -118
- data/lib/ssrf_proxy/version.rb +12 -4
- data/lib/ssrf_proxy.rb +37 -10
- metadata +162 -39
- data/bin/ssrf-scan +0 -452
data/lib/ssrf_proxy/http.rb
CHANGED
@@ -1,1306 +1,990 @@
|
|
1
|
-
|
1
|
+
# coding: utf-8
|
2
2
|
#
|
3
|
-
# Copyright (c) 2015 Brendan Coles <bcoles@gmail.com>
|
3
|
+
# Copyright (c) 2015-2016 Brendan Coles <bcoles@gmail.com>
|
4
4
|
# SSRF Proxy - https://github.com/bcoles/ssrf_proxy
|
5
|
-
# See the file 'LICENSE' for copying permission
|
5
|
+
# See the file 'LICENSE.md' for copying permission
|
6
6
|
#
|
7
7
|
|
8
|
-
require "ssrf_proxy"
|
9
|
-
|
10
8
|
module SSRFProxy
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
9
|
+
#
|
10
|
+
# SSRFProxy::HTTP object takes information required to connect
|
11
|
+
# to a HTTP server vulnerable to SSRF and issue arbitrary HTTP
|
12
|
+
# requests via the SSRF.
|
13
|
+
#
|
14
|
+
# Once configured, the #send_uri method can be used to tunnel
|
15
|
+
# HTTP requests through the server.
|
16
|
+
#
|
17
|
+
# Several request modification options can be used to format
|
18
|
+
# the HTTP request appropriately for the SSRF vector and
|
19
|
+
# the destination web server accessed via the SSRF.
|
20
|
+
#
|
21
|
+
# Several response modification options can be used to infer
|
22
|
+
# information about the response from the destination server
|
23
|
+
# and format the response such that the vulnerable intermediary
|
24
|
+
# server is mostly transparent to the client initiating the
|
25
|
+
# HTTP request.
|
26
|
+
#
|
27
|
+
# Refer to the wiki for more information about configuring the
|
28
|
+
# SSRF, requestion modification, and response modification:
|
29
|
+
# https://github.com/bcoles/ssrf_proxy/wiki/Configuration
|
30
|
+
#
|
31
|
+
class HTTP
|
32
|
+
#
|
33
|
+
# SSRFProxy::HTTP errors
|
34
|
+
#
|
35
|
+
module Error
|
36
|
+
# SSRFProxy::HTTP custom errors
|
37
|
+
class Error < StandardError; end
|
38
|
+
exceptions = %w(
|
39
|
+
NoUrlPlaceholder
|
40
|
+
InvalidSsrfRequest
|
41
|
+
InvalidSsrfRequestMethod
|
42
|
+
InvalidUpstreamProxy
|
43
|
+
InvalidIpEncoding
|
44
|
+
InvalidClientRequest
|
45
|
+
ConnectionTimeout )
|
46
|
+
exceptions.each { |e| const_set(e, Class.new(Error)) }
|
47
|
+
end
|
26
48
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
49
|
+
#
|
50
|
+
# SSRFProxy::HTTP accepts SSRF connection information,
|
51
|
+
# and configuration options for request modificaiton
|
52
|
+
# and response modification.
|
53
|
+
#
|
54
|
+
# @param [String] url SSRF URL with 'xxURLxx' placeholder
|
55
|
+
# @param [Hash] opts SSRF and HTTP connection options:
|
56
|
+
# @option opts [String] proxy
|
57
|
+
# @option opts [String] method
|
58
|
+
# @option opts [String] post_data
|
59
|
+
# @option opts [String] rules
|
60
|
+
# @option opts [String] ip_encoding
|
61
|
+
# @option opts [Regex] match
|
62
|
+
# @option opts [String] strip
|
63
|
+
# @option opts [Boolean] decode_html
|
64
|
+
# @option opts [Boolean] guess_status
|
65
|
+
# @option opts [Boolean] guess_mime
|
66
|
+
# @option opts [Boolean] ask_password
|
67
|
+
# @option opts [Boolean] forward_cookies
|
68
|
+
# @option opts [Boolean] body_to_uri
|
69
|
+
# @option opts [Boolean] auth_to_uri
|
70
|
+
# @option opts [Boolean] cookies_to_uri
|
71
|
+
# @option opts [String] cookie
|
72
|
+
# @option opts [Integer] timeout
|
73
|
+
# @option opts [String] user_agent
|
74
|
+
# @option opts [Boolean] insecure
|
75
|
+
#
|
76
|
+
# @example SSRF with default options
|
77
|
+
# SSRFProxy::HTTP.new('http://example.local/index.php?url=xxURLxx')
|
78
|
+
#
|
79
|
+
# @raise [SSRFProxy::HTTP::Error::InvalidSsrfRequest]
|
80
|
+
# Invalid SSRF request specified.
|
81
|
+
#
|
82
|
+
def initialize(url = '', opts = {})
|
83
|
+
@detect_waf = true
|
84
|
+
@logger = ::Logger.new(STDOUT).tap do |log|
|
85
|
+
log.progname = 'ssrf-proxy'
|
86
|
+
log.level = ::Logger::WARN
|
87
|
+
log.datetime_format = '%Y-%m-%d %H:%M:%S '
|
88
|
+
end
|
89
|
+
begin
|
90
|
+
@ssrf_url = URI.parse(url.to_s)
|
91
|
+
rescue URI::InvalidURIError
|
92
|
+
raise SSRFProxy::HTTP::Error::InvalidSsrfRequest.new,
|
93
|
+
'Invalid SSRF request specified.'
|
94
|
+
end
|
95
|
+
if @ssrf_url.scheme.nil? || @ssrf_url.host.nil? || @ssrf_url.port.nil?
|
96
|
+
raise SSRFProxy::HTTP::Error::InvalidSsrfRequest.new,
|
97
|
+
'Invalid SSRF request specified.'
|
98
|
+
end
|
99
|
+
if @ssrf_url.scheme !~ /\Ahttps?\z/
|
100
|
+
raise SSRFProxy::HTTP::Error::InvalidSsrfRequest.new,
|
101
|
+
'Invalid SSRF request specified. Scheme must be http(s).'
|
102
|
+
end
|
103
|
+
parse_options(opts)
|
104
|
+
end
|
31
105
|
|
32
|
-
|
33
|
-
|
34
|
-
|
106
|
+
#
|
107
|
+
# Parse initialization configuration options
|
108
|
+
#
|
109
|
+
# @param [Hash] opts Options for SSRF and HTTP connection options
|
110
|
+
#
|
111
|
+
# @raise [SSRFProxy::HTTP::Error::InvalidUpstreamProxy]
|
112
|
+
# Invalid upstream proxy specified.
|
113
|
+
# @raise [SSRFProxy::HTTP::Error::InvalidSsrfRequestMethod]
|
114
|
+
# Invalid SSRF request method specified.
|
115
|
+
# Method must be GET/HEAD/DELETE/POST/PUT.
|
116
|
+
# @raise [SSRFProxy::HTTP::Error::NoUrlPlaceholder]
|
117
|
+
# 'xxURLxx' URL placeholder must be specified in the
|
118
|
+
# SSRF request URL or body.
|
119
|
+
# @raise [SSRFProxy::HTTP::Error::InvalidIpEncoding]
|
120
|
+
# Invalid IP encoding method specified.
|
121
|
+
#
|
122
|
+
def parse_options(opts = {})
|
123
|
+
# SSRF configuration options
|
124
|
+
@upstream_proxy = nil
|
125
|
+
@method = 'GET'
|
126
|
+
@post_data = ''
|
127
|
+
@rules = []
|
128
|
+
opts.each do |option, value|
|
129
|
+
next if value.eql?('')
|
130
|
+
case option
|
131
|
+
when 'proxy'
|
132
|
+
begin
|
133
|
+
@upstream_proxy = URI.parse(value)
|
134
|
+
rescue URI::InvalidURIError
|
135
|
+
raise SSRFProxy::HTTP::Error::InvalidUpstreamProxy.new,
|
136
|
+
'Invalid upstream proxy specified.'
|
137
|
+
end
|
138
|
+
if @upstream_proxy.host.nil? || @upstream_proxy.port.nil?
|
139
|
+
raise SSRFProxy::HTTP::Error::InvalidUpstreamProxy.new,
|
140
|
+
'Invalid upstream proxy specified.'
|
141
|
+
end
|
142
|
+
if @upstream_proxy.scheme !~ /\A(socks|https?)\z/
|
143
|
+
raise SSRFProxy::HTTP::Error::InvalidUpstreamProxy.new,
|
144
|
+
'Unsupported upstream proxy specified. Scheme must be http(s) or socks.'
|
145
|
+
end
|
146
|
+
when 'method'
|
147
|
+
case value.to_s.downcase
|
148
|
+
when 'get'
|
149
|
+
@method = 'GET'
|
150
|
+
when 'head'
|
151
|
+
@method = 'HEAD'
|
152
|
+
when 'delete'
|
153
|
+
@method = 'DELETE'
|
154
|
+
when 'post'
|
155
|
+
@method = 'POST'
|
156
|
+
when 'put'
|
157
|
+
@method = 'PUT'
|
158
|
+
else
|
159
|
+
raise SSRFProxy::HTTP::Error::InvalidSsrfRequestMethod.new,
|
160
|
+
'Invalid SSRF request method specified. Method must be GET/HEAD/DELETE/POST/PUT.'
|
161
|
+
end
|
162
|
+
when 'post_data'
|
163
|
+
@post_data = value.to_s
|
164
|
+
when 'rules'
|
165
|
+
@rules = value.to_s.split(/,/)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
if @ssrf_url.request_uri !~ /xxURLxx/ && @post_data.to_s !~ /xxURLxx/
|
169
|
+
raise SSRFProxy::HTTP::Error::NoUrlPlaceholder.new,
|
170
|
+
"You must specify a URL placeholder with 'xxURLxx' in the SSRF request"
|
171
|
+
end
|
35
172
|
|
36
|
-
|
37
|
-
|
38
|
-
|
173
|
+
# client request modification
|
174
|
+
@ip_encoding = nil
|
175
|
+
@forward_cookies = false
|
176
|
+
@body_to_uri = false
|
177
|
+
@auth_to_uri = false
|
178
|
+
@cookies_to_uri = false
|
179
|
+
opts.each do |option, value|
|
180
|
+
next if value.eql?('')
|
181
|
+
case option
|
182
|
+
when 'ip_encoding'
|
183
|
+
if value.to_s !~ /\A[a-z0-9_]+\z/i
|
184
|
+
raise SSRFProxy::HTTP::Error::InvalidIpEncoding.new,
|
185
|
+
'Invalid IP encoding method specified.'
|
186
|
+
end
|
187
|
+
@ip_encoding = value.to_s
|
188
|
+
when 'forward_cookies'
|
189
|
+
@forward_cookies = true if value
|
190
|
+
when 'body_to_uri'
|
191
|
+
@body_to_uri = true if value
|
192
|
+
when 'auth_to_uri'
|
193
|
+
@auth_to_uri = true if value
|
194
|
+
when 'cookies_to_uri'
|
195
|
+
@cookies_to_uri = true if value
|
196
|
+
end
|
197
|
+
end
|
39
198
|
|
40
|
-
|
41
|
-
|
199
|
+
# SSRF connection options
|
200
|
+
@cookie = nil
|
201
|
+
@timeout = 10
|
202
|
+
@user_agent = 'Mozilla/5.0'
|
203
|
+
@insecure = false
|
204
|
+
opts.each do |option, value|
|
205
|
+
next if value.eql?('')
|
206
|
+
case option
|
207
|
+
when 'cookie'
|
208
|
+
@cookie = value.to_s
|
209
|
+
when 'timeout'
|
210
|
+
@timeout = value.to_i
|
211
|
+
when 'user_agent'
|
212
|
+
@user_agent = value.to_s
|
213
|
+
when 'insecure'
|
214
|
+
@insecure = true if value
|
215
|
+
end
|
216
|
+
end
|
42
217
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
218
|
+
# HTTP response modification options
|
219
|
+
@match_regex = '\\A(.*)\\z'
|
220
|
+
@strip = []
|
221
|
+
@decode_html = false
|
222
|
+
@guess_status = false
|
223
|
+
@guess_mime = false
|
224
|
+
@ask_password = false
|
225
|
+
opts.each do |option, value|
|
226
|
+
next if value.eql?('')
|
227
|
+
case option
|
228
|
+
when 'match'
|
229
|
+
@match_regex = value.to_s
|
230
|
+
when 'strip'
|
231
|
+
@strip = value.to_s.split(/,/)
|
232
|
+
when 'decode_html'
|
233
|
+
@decode_html = true if value
|
234
|
+
when 'guess_status'
|
235
|
+
@guess_status = true if value
|
236
|
+
when 'guess_mime'
|
237
|
+
@guess_mime = true if value
|
238
|
+
when 'ask_password'
|
239
|
+
@ask_password = true if value
|
240
|
+
end
|
241
|
+
end
|
49
242
|
end
|
50
|
-
end
|
51
243
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
InvalidSsrfRequest
|
61
|
-
InvalidRequestMethod
|
62
|
-
InvalidUpstreamProxy
|
63
|
-
InvalidIpEncoding
|
64
|
-
InvalidHttpRequest
|
65
|
-
InvalidUriRequest )
|
66
|
-
exceptions.each { |e| const_set(e, Class.new(Error)) }
|
67
|
-
end
|
244
|
+
#
|
245
|
+
# Logger accessor
|
246
|
+
#
|
247
|
+
# @return [Logger] class logger object
|
248
|
+
#
|
249
|
+
def logger
|
250
|
+
@logger
|
251
|
+
end
|
68
252
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
# - 'method' => String
|
77
|
-
# - 'post_data' => String
|
78
|
-
# - 'rules' => String
|
79
|
-
# - 'ip_encoding' => String
|
80
|
-
# - 'match' => Regex
|
81
|
-
# - 'strip' => String
|
82
|
-
# - 'guess_status' => Boolean
|
83
|
-
# - 'guess_mime' => Boolean
|
84
|
-
# - 'forward_cookies'=> Boolean
|
85
|
-
# - 'body_to_uri' => Boolean
|
86
|
-
# - 'auth_to_uri' => Boolean
|
87
|
-
# - 'cookie' => String
|
88
|
-
# - 'timeout' => Integer
|
89
|
-
# - 'user_agent' => String
|
90
|
-
#
|
91
|
-
def initialize(url='', opts={})
|
92
|
-
@logger = ::Logger.new(STDOUT).tap do |log|
|
93
|
-
log.progname = 'ssrf-proxy'
|
94
|
-
log.level = ::Logger::WARN
|
95
|
-
log.datetime_format = '%Y-%m-%d %H:%M:%S '
|
253
|
+
#
|
254
|
+
# URL accessor
|
255
|
+
#
|
256
|
+
# @return [String] SSRF URL
|
257
|
+
#
|
258
|
+
def url
|
259
|
+
@ssrf_url
|
96
260
|
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
261
|
+
|
262
|
+
#
|
263
|
+
# Host accessor
|
264
|
+
#
|
265
|
+
# @return [String] SSRF host
|
266
|
+
#
|
267
|
+
def host
|
268
|
+
@ssrf_url.host
|
102
269
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
270
|
+
|
271
|
+
#
|
272
|
+
# Port accessor
|
273
|
+
#
|
274
|
+
# @return [String] SSRF host port
|
275
|
+
#
|
276
|
+
def port
|
277
|
+
@ssrf_url.port
|
106
278
|
end
|
107
|
-
|
108
|
-
|
109
|
-
|
279
|
+
|
280
|
+
#
|
281
|
+
# Upstream proxy accessor
|
282
|
+
#
|
283
|
+
# @return [URI] upstream HTTP proxy
|
284
|
+
#
|
285
|
+
def proxy
|
286
|
+
@upstream_proxy
|
110
287
|
end
|
111
288
|
|
112
|
-
#
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
@
|
117
|
-
|
118
|
-
@
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
289
|
+
#
|
290
|
+
# Parse a HTTP request as a string, then send the requested URL
|
291
|
+
# and HTTP headers to send_uri
|
292
|
+
#
|
293
|
+
# @param [String] request raw HTTP request
|
294
|
+
#
|
295
|
+
# @raise [SSRFProxy::HTTP::Error::InvalidClientRequest]
|
296
|
+
# An invalid client HTTP request was supplied.
|
297
|
+
#
|
298
|
+
# @return [Hash] HTTP response hash (version, code, message, headers, body, etc)
|
299
|
+
#
|
300
|
+
def send_request(request)
|
301
|
+
if request.to_s !~ /\A(GET|HEAD|DELETE|POST|PUT) /
|
302
|
+
logger.warn("Client request method is not supported")
|
303
|
+
raise SSRFProxy::HTTP::Error::InvalidClientRequest,
|
304
|
+
'Client request method is not supported'
|
305
|
+
end
|
306
|
+
if request.to_s !~ %r{\A(GET|HEAD|DELETE|POST|PUT) https?://}
|
307
|
+
if request.to_s =~ /^Host: ([^\s]+)\r?\n/
|
308
|
+
logger.info("Using host header: #{$1}")
|
309
|
+
else
|
310
|
+
logger.warn('No host specified')
|
311
|
+
raise SSRFProxy::HTTP::Error::InvalidClientRequest,
|
312
|
+
'No host specified'
|
313
|
+
end
|
314
|
+
end
|
315
|
+
# parse client request
|
316
|
+
begin
|
317
|
+
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
|
318
|
+
req.parse(StringIO.new(request))
|
319
|
+
rescue
|
320
|
+
logger.info('Received malformed client HTTP request.')
|
321
|
+
raise SSRFProxy::HTTP::Error::InvalidClientRequest,
|
322
|
+
'Received malformed client HTTP request.'
|
323
|
+
end
|
324
|
+
if req.to_s =~ /^Upgrade: WebSocket/
|
325
|
+
logger.warn("WebSocket tunneling is not supported: #{req.host}:#{req.port}")
|
326
|
+
raise SSRFProxy::HTTP::Error::InvalidClientRequest,
|
327
|
+
"WebSocket tunneling is not supported: #{req.host}:#{req.port}"
|
328
|
+
end
|
329
|
+
uri = req.request_uri
|
330
|
+
if uri.nil?
|
331
|
+
raise SSRFProxy::HTTP::Error::InvalidClientRequest,
|
332
|
+
'URI is nil'
|
333
|
+
end
|
334
|
+
|
335
|
+
# parse request body and move to uri
|
336
|
+
if @body_to_uri && !req.body.nil?
|
337
|
+
logger.debug("Parsing request body: #{req.body}")
|
125
338
|
begin
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
339
|
+
if req.query_string.nil?
|
340
|
+
uri = "#{uri}?#{req.body}"
|
341
|
+
else
|
342
|
+
uri = "#{uri}&#{req.body}"
|
343
|
+
end
|
344
|
+
logger.info("Added request body to URI: #{req.body}")
|
345
|
+
rescue
|
346
|
+
logger.warn('Could not parse client request body')
|
130
347
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
348
|
+
end
|
349
|
+
|
350
|
+
# move basic authentication credentials to uri
|
351
|
+
if @auth_to_uri && !req.header.nil?
|
352
|
+
req.header['authorization'].each do |header|
|
353
|
+
logger.debug("Parsing basic authentication header: #{header}")
|
354
|
+
next unless header.split(' ').first =~ /^basic$/i
|
355
|
+
begin
|
356
|
+
creds = header.split(' ')[1]
|
357
|
+
user = Base64.decode64(creds).chomp
|
358
|
+
uri = uri.to_s.gsub!(%r{:(//)}, "://#{user}@")
|
359
|
+
logger.info("Using basic authentication credentials: #{user}")
|
360
|
+
rescue
|
361
|
+
logger.warn "Could not parse request authorization header: #{header}"
|
362
|
+
end
|
363
|
+
break
|
134
364
|
end
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
365
|
+
end
|
366
|
+
|
367
|
+
# copy cookies to uri
|
368
|
+
cookies = []
|
369
|
+
if @cookies_to_uri && !req.cookies.nil? && !req.cookies.empty?
|
370
|
+
logger.debug("Parsing request cookies: #{req.cookies.join('; ')}")
|
371
|
+
cookies = []
|
372
|
+
begin
|
373
|
+
req.cookies.each do |c|
|
374
|
+
cookies << c.to_s.gsub(/;\z/, '').to_s unless c.nil?
|
375
|
+
end
|
376
|
+
query_string = uri.to_s.split('?')[1..-1]
|
377
|
+
if query_string.empty?
|
378
|
+
s = '?'
|
379
|
+
else
|
380
|
+
s = '&'
|
381
|
+
end
|
382
|
+
uri = "#{uri}#{s}#{cookies.join('&')}"
|
383
|
+
logger.info("Added cookies to URI: #{cookies.join('&')}")
|
384
|
+
rescue => e
|
385
|
+
logger.warn "Could not parse request coookies: #{e}"
|
139
386
|
end
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
387
|
+
end
|
388
|
+
|
389
|
+
# HTTP request headers
|
390
|
+
headers = {}
|
391
|
+
|
392
|
+
# forward client cookies
|
393
|
+
new_cookie = []
|
394
|
+
new_cookie << @cookie unless @cookie.nil?
|
395
|
+
if @forward_cookies
|
396
|
+
req.cookies.each do |c|
|
397
|
+
new_cookie << c.to_s
|
149
398
|
end
|
150
|
-
@ip_encoding = value.to_s
|
151
|
-
when 'rules'
|
152
|
-
@rules = value.to_s.split(/,/)
|
153
|
-
when 'forward_cookies'
|
154
|
-
@forward_cookies = true if value
|
155
|
-
when 'body_to_uri'
|
156
|
-
@body_to_uri = true if value
|
157
|
-
when 'auth_to_uri'
|
158
|
-
@auth_to_uri = true if value
|
159
399
|
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
400
|
+
unless new_cookie.empty?
|
401
|
+
headers['cookie'] = new_cookie.uniq.join('; ').to_s
|
402
|
+
logger.info("Using cookie: #{headers['cookie']}")
|
403
|
+
end
|
404
|
+
send_uri(uri, headers)
|
164
405
|
end
|
165
406
|
|
166
|
-
#
|
167
|
-
|
168
|
-
|
169
|
-
@
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
407
|
+
#
|
408
|
+
# Fetch a URI via SSRF
|
409
|
+
#
|
410
|
+
# @param [String] uri URI to fetch
|
411
|
+
# @param [Hash] HTTP request headers
|
412
|
+
#
|
413
|
+
# @raise [SSRFProxy::HTTP::Error::InvalidClientRequest]
|
414
|
+
# An invalid client HTTP request was supplied.
|
415
|
+
#
|
416
|
+
# @return [Hash] HTTP response hash (version, code, message, headers, body, etc)
|
417
|
+
#
|
418
|
+
def send_uri(uri, headers = {})
|
419
|
+
if uri.nil?
|
420
|
+
raise SSRFProxy::HTTP::Error::InvalidClientRequest,
|
421
|
+
'Request URI is nil'
|
179
422
|
end
|
180
|
-
end
|
181
423
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
opts.each do |option, value|
|
188
|
-
next if value.eql?('')
|
189
|
-
case option
|
190
|
-
when 'match'
|
191
|
-
@match_regex = value.to_s
|
192
|
-
when 'strip'
|
193
|
-
@strip = value.to_s.split(/,/)
|
194
|
-
when 'guess_status'
|
195
|
-
@guess_status = true if value
|
196
|
-
when 'guess_mime'
|
197
|
-
@guess_mime = true if value
|
424
|
+
# encode target host ip
|
425
|
+
if @ip_encoding
|
426
|
+
encoded_uri = encode_ip(uri, @ip_encoding)
|
427
|
+
else
|
428
|
+
encoded_uri = uri
|
198
429
|
end
|
199
|
-
end
|
200
430
|
|
201
|
-
|
431
|
+
# run target url through rules
|
432
|
+
target_uri = run_rules(encoded_uri, @rules)
|
202
433
|
|
203
|
-
|
204
|
-
|
205
|
-
#
|
206
|
-
# @returns - String - SSRF URL
|
207
|
-
#
|
208
|
-
def url
|
209
|
-
@ssrf_url
|
210
|
-
end
|
434
|
+
# replace xxURLxx placeholder in SSRF request URL
|
435
|
+
ssrf_url = "#{@ssrf_url.path}?#{@ssrf_url.query}".gsub(/xxURLxx/, target_uri.to_s)
|
211
436
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
@ssrf_url.host
|
219
|
-
end
|
437
|
+
# replace xxURLxx placeholder in SSRF request body
|
438
|
+
if @post_data.nil?
|
439
|
+
body = ''
|
440
|
+
else
|
441
|
+
body = @post_data.gsub(/xxURLxx/, target_uri.to_s)
|
442
|
+
end
|
220
443
|
|
221
|
-
|
222
|
-
|
223
|
-
#
|
224
|
-
# @returns - String - SSRF port
|
225
|
-
#
|
226
|
-
def port
|
227
|
-
@ssrf_url.port
|
228
|
-
end
|
444
|
+
# set user agent
|
445
|
+
headers['User-Agent'] = @user_agent if headers['User-Agent'].nil?
|
229
446
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
#
|
235
|
-
def cookie
|
236
|
-
@cookie
|
237
|
-
end
|
447
|
+
# set content type
|
448
|
+
if headers['Content-Type'].nil? && @method.eql?('POST')
|
449
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
450
|
+
end
|
238
451
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
452
|
+
# send request
|
453
|
+
start_time = Time.now
|
454
|
+
response = send_http_request(ssrf_url, @method, headers, body)
|
455
|
+
end_time = Time.now
|
456
|
+
duration = ((end_time - start_time) * 1000).round(3)
|
457
|
+
result = {
|
458
|
+
'url' => uri,
|
459
|
+
'duration' => duration,
|
460
|
+
'http_version' => response.http_version,
|
461
|
+
'code' => response.code,
|
462
|
+
'message' => response.message,
|
463
|
+
'headers' => '',
|
464
|
+
'body' => response.body.to_s || '' }
|
465
|
+
logger.info("Received #{result['body'].length} bytes in #{duration} ms")
|
466
|
+
|
467
|
+
# guess HTTP response code and message
|
468
|
+
if @guess_status
|
469
|
+
head = result['body'][0..8192]
|
470
|
+
status = guess_status(head)
|
471
|
+
unless status.empty?
|
472
|
+
result['code'] = status['code']
|
473
|
+
result['message'] = status['message']
|
474
|
+
logger.info("Using HTTP response status: #{result['code']} #{result['message']}")
|
475
|
+
end
|
476
|
+
end
|
477
|
+
result['status_line'] = "HTTP/#{result['http_version']} #{result['code']} #{result['message']}"
|
247
478
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
#
|
257
|
-
def encode_ip(url, mode)
|
258
|
-
return if url.nil?
|
259
|
-
new_host = nil
|
260
|
-
host = URI::parse(url.to_s.split('?').first).host.to_s
|
261
|
-
begin
|
262
|
-
ip = IPAddress.parse(host)
|
263
|
-
rescue
|
264
|
-
logger.warn("Could not parse requested host as IP address: #{host}")
|
265
|
-
return
|
266
|
-
end
|
267
|
-
case mode
|
268
|
-
when 'int'
|
269
|
-
new_host = url.to_s.gsub!(host, "#{ip.to_u32}")
|
270
|
-
when 'ipv6'
|
271
|
-
new_host = url.to_s.gsub!(host, "#{ip.to_ipv6}")
|
272
|
-
when 'oct'
|
273
|
-
new_host = url.to_s.gsub!(host, "0#{ip.to_u32.to_s(8)}")
|
274
|
-
when 'hex'
|
275
|
-
new_host = url.to_s.gsub!(host, "0x#{ip.to_u32.to_s(16)}")
|
276
|
-
else
|
277
|
-
logger.warn("Invalid IP encoding: #{mode}")
|
278
|
-
end
|
279
|
-
new_host
|
280
|
-
end
|
479
|
+
# strip unwanted HTTP response headers
|
480
|
+
response.each_header do |header_name, header_value|
|
481
|
+
if @strip.include?(header_name.downcase)
|
482
|
+
logger.info("Removed response header: #{header_name}")
|
483
|
+
next
|
484
|
+
end
|
485
|
+
result['headers'] << "#{header_name}: #{header_value}\n"
|
486
|
+
end
|
281
487
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
# @returns - String - modified request URL
|
290
|
-
#
|
291
|
-
def run_rules(url, rules)
|
292
|
-
str = url.to_s
|
293
|
-
return str if rules.nil?
|
294
|
-
rules.each do |rule|
|
295
|
-
case rule
|
296
|
-
when 'noproto'
|
297
|
-
str = str.gsub(/^https?:\/\//, '')
|
298
|
-
when 'nossl', 'http'
|
299
|
-
str = str.gsub(/^https:\/\//, 'http://')
|
300
|
-
when 'ssl', 'https'
|
301
|
-
str = str.gsub(/^http:\/\//, 'https://')
|
302
|
-
when 'base64'
|
303
|
-
str = Base64.encode64(str).chomp
|
304
|
-
when 'md4'
|
305
|
-
str = OpenSSL::Digest::MD4.hexdigest(str)
|
306
|
-
when 'md5'
|
307
|
-
md5 = Digest::MD5.new
|
308
|
-
md5.update str
|
309
|
-
str = md5.hexdigest
|
310
|
-
when 'sha1'
|
311
|
-
str = Digest::SHA1.hexdigest(str)
|
312
|
-
when 'reverse'
|
313
|
-
str = str.reverse
|
314
|
-
when 'urlencode'
|
315
|
-
str = CGI.escape(str)
|
316
|
-
when 'urldecode'
|
317
|
-
str = CGI.unescape(str)
|
318
|
-
else
|
319
|
-
logger.warn("Unknown rule: #{rule}")
|
488
|
+
# detect WAF and SSRF protection libraries
|
489
|
+
if @detect_waf
|
490
|
+
head = result['body'][0..8192]
|
491
|
+
# SafeCurl (safe_curl) InvalidURLException
|
492
|
+
if head =~ /fin1te\\SafeCurl\\Exception\\InvalidURLException/
|
493
|
+
logger.info('SafeCurl protection mechanism appears to be in use')
|
494
|
+
end
|
320
495
|
end
|
321
|
-
end
|
322
|
-
str
|
323
|
-
end
|
324
496
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
# @options
|
329
|
-
# - request - String - raw HTTP request
|
330
|
-
#
|
331
|
-
# @returns String - raw HTTP response headers and body
|
332
|
-
#
|
333
|
-
def send_request(request)
|
334
|
-
if request.to_s !~ /\A[A-Z]{1,20} /
|
335
|
-
logger.warn("Received malformed client HTTP request.")
|
336
|
-
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
337
|
-
elsif request.to_s =~ /\ACONNECT ([^\s]+) .*$/
|
338
|
-
logger.warn("CONNECT tunneling is not supported: #{$1}")
|
339
|
-
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
340
|
-
elsif request.to_s =~ /\A(DEBUG|TRACE|TRACK|OPTIONS) /
|
341
|
-
logger.warn("Client request method is not supported: #{$1}")
|
342
|
-
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
343
|
-
end
|
344
|
-
if request.to_s !~ /\A[A-Z]{1,20} https?:\/\//
|
345
|
-
if request.to_s =~ /^Host: ([^\s]+)\r?\n/
|
346
|
-
logger.info("Using host header: #{$1}")
|
497
|
+
# advise client to close HTTP connection
|
498
|
+
if result['headers'] =~ /^connection:.*$/i
|
499
|
+
result['headers'].gsub!(/^connection:.*$/i, 'Connection: close')
|
347
500
|
else
|
348
|
-
|
349
|
-
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
501
|
+
result['headers'] << "Connection: close\n"
|
350
502
|
end
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
503
|
+
|
504
|
+
# guess mime type and add content-type header
|
505
|
+
if @guess_mime
|
506
|
+
content_type = guess_mime(File.extname(uri.to_s.split('?').first))
|
507
|
+
unless content_type.nil?
|
508
|
+
logger.info("Using content-type: #{content_type}")
|
509
|
+
if result['headers'] =~ /^content\-type:.*$/i
|
510
|
+
result['headers'].gsub!(/^content\-type:.*$/i, "Content-Type: #{content_type}")
|
511
|
+
else
|
512
|
+
result['headers'] << "Content-Type: #{content_type}\n"
|
513
|
+
end
|
514
|
+
end
|
364
515
|
end
|
365
|
-
uri = req.request_uri
|
366
|
-
raise SSRFProxy::HTTP::Error::InvalidHttpRequest if uri.nil?
|
367
|
-
rescue => e
|
368
|
-
logger.info("Received malformed client HTTP request.")
|
369
|
-
return "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
370
|
-
end
|
371
516
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
uri = "#{uri}?#{URI.encode_www_form(new_query)}"
|
517
|
+
# match response content
|
518
|
+
unless @match_regex.nil?
|
519
|
+
matches = result['body'].scan(/#{@match_regex}/m)
|
520
|
+
if matches.length > 0
|
521
|
+
result['body'] = matches.flatten.first.to_s
|
522
|
+
logger.info("Response body matches pattern '#{@match_regex}'")
|
379
523
|
else
|
380
|
-
|
381
|
-
|
524
|
+
result['body'] = ''
|
525
|
+
logger.warn("Response body does not match pattern '#{@match_regex}'")
|
382
526
|
end
|
383
|
-
rescue
|
384
|
-
logger.warn "Could not parse request POST data"
|
385
527
|
end
|
386
|
-
end
|
387
528
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
529
|
+
# decode HTML entities
|
530
|
+
if @decode_html
|
531
|
+
result['body'] = HTMLEntities.new.decode(
|
532
|
+
result['body'].encode(
|
533
|
+
'UTF-8',
|
534
|
+
:invalid => :replace,
|
535
|
+
:undef => :replace,
|
536
|
+
:replace => '?'))
|
537
|
+
end
|
538
|
+
|
539
|
+
# prompt for password
|
540
|
+
if @ask_password
|
541
|
+
if result['code'].to_i == 401
|
542
|
+
auth_uri = URI.parse(uri.to_s.split('?').first)
|
543
|
+
realm = "#{auth_uri.host}:#{auth_uri.port}"
|
544
|
+
result['headers'] << "WWW-Authenticate: Basic realm=\"#{realm}\"\n"
|
545
|
+
logger.info("Added WWW-Authenticate header for realm: #{realm}")
|
399
546
|
end
|
400
|
-
break
|
401
547
|
end
|
402
|
-
end
|
403
548
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
549
|
+
# set content length
|
550
|
+
content_length = result['body'].length
|
551
|
+
if result['headers'] =~ /^transfer\-encoding:.*$/i
|
552
|
+
result['headers'].gsub!(/^transfer\-encoding:.*$/i, "Content-Length: #{content_length}")
|
553
|
+
elsif result['headers'] =~ /^content\-length:.*$/i
|
554
|
+
result['headers'].gsub!(/^content\-length:.*$/i, "Content-Length: #{content_length}")
|
555
|
+
else
|
556
|
+
result['headers'] << "Content-Length: #{content_length}\n"
|
410
557
|
end
|
411
|
-
end
|
412
|
-
unless new_cookie.empty?
|
413
|
-
opts['cookie'] = new_cookie.uniq.join('; ').to_s
|
414
|
-
logger.info "Using cookie: #{opts['cookie']}"
|
415
|
-
end
|
416
|
-
send_uri(uri, opts)
|
417
|
-
end
|
418
558
|
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
#
|
426
|
-
# @returns String - raw HTTP response headers and body
|
427
|
-
#
|
428
|
-
def send_uri(uri, opts={})
|
429
|
-
raise SSRFProxy::HTTP::Error::InvalidUriRequest if uri.nil?
|
559
|
+
# set title
|
560
|
+
if result['body'][0..1024] =~ %r{<title>([^<]*)<\/title>}im
|
561
|
+
result['title'] = $1.to_s
|
562
|
+
else
|
563
|
+
result['title'] = ''
|
564
|
+
end
|
430
565
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
uri = encoded_url unless encoded_url.nil?
|
566
|
+
# return HTTP response
|
567
|
+
logger.debug("Response:\n#{result['status_line']}\n#{result['headers']}\n#{result['body']}")
|
568
|
+
result
|
435
569
|
end
|
436
570
|
|
437
|
-
#
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
571
|
+
#
|
572
|
+
# Encode IP address of a given URL
|
573
|
+
#
|
574
|
+
# @param [String] url target URL
|
575
|
+
# @param [String] mode encoding (int, ipv6, oct, hex, dotted_hex)
|
576
|
+
#
|
577
|
+
# @return [String] encoded IP address
|
578
|
+
#
|
579
|
+
def encode_ip(url, mode)
|
580
|
+
return if url.nil?
|
581
|
+
new_host = nil
|
582
|
+
host = URI.parse(url.to_s.split('?').first).host.to_s
|
583
|
+
begin
|
584
|
+
ip = IPAddress::IPv4.new(host)
|
585
|
+
rescue
|
586
|
+
logger.warn("Could not parse requested host as IPv4 address: #{host}")
|
587
|
+
return
|
588
|
+
end
|
589
|
+
case mode
|
590
|
+
when 'int'
|
591
|
+
new_host = url.to_s.gsub(host, ip.to_u32.to_s)
|
592
|
+
when 'ipv6'
|
593
|
+
new_host = url.to_s.gsub(host, "[#{ip.to_ipv6}]")
|
594
|
+
when 'oct'
|
595
|
+
new_host = url.to_s.gsub(host, "0#{ip.to_u32.to_s(8)}")
|
596
|
+
when 'hex'
|
597
|
+
new_host = url.to_s.gsub(host, "0x#{ip.to_u32.to_s(16)}")
|
598
|
+
when 'dotted_hex'
|
599
|
+
res = ip.octets.map { |i| "0x#{i.to_s(16).rjust(2, '0')}" }.join('.')
|
600
|
+
new_host = url.to_s.gsub(host, res.to_s) unless res.nil?
|
601
|
+
else
|
602
|
+
logger.warn("Invalid IP encoding: #{mode}")
|
603
|
+
end
|
604
|
+
new_host
|
454
605
|
end
|
455
606
|
|
456
|
-
#
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
607
|
+
#
|
608
|
+
# Run a specified URL through SSRF rules
|
609
|
+
#
|
610
|
+
# @param [String] url request URL
|
611
|
+
# @param [String] rules comma separated list of rules
|
612
|
+
#
|
613
|
+
# @return [String] modified request URL
|
614
|
+
#
|
615
|
+
def run_rules(url, rules)
|
616
|
+
str = url.to_s
|
617
|
+
return str if rules.nil?
|
618
|
+
rules.each do |rule|
|
619
|
+
case rule
|
620
|
+
when 'noproto'
|
621
|
+
str = str.gsub(%r{^https?://}, '')
|
622
|
+
when 'nossl', 'http'
|
623
|
+
str = str.gsub(%r{^https://}, 'http://')
|
624
|
+
when 'ssl', 'https'
|
625
|
+
str = str.gsub(%r{^http://}, 'https://')
|
626
|
+
when 'base32'
|
627
|
+
str = Base32.encode(str).to_s
|
628
|
+
when 'base64'
|
629
|
+
str = Base64.encode64(str).delete("\n")
|
630
|
+
when 'md4'
|
631
|
+
str = OpenSSL::Digest::MD4.hexdigest(str)
|
632
|
+
when 'md5'
|
633
|
+
md5 = Digest::MD5.new
|
634
|
+
md5.update str
|
635
|
+
str = md5.hexdigest
|
636
|
+
when 'sha1'
|
637
|
+
str = Digest::SHA1.hexdigest(str)
|
638
|
+
when 'reverse'
|
639
|
+
str = str.reverse
|
640
|
+
when 'upcase'
|
641
|
+
str = str.upcase
|
642
|
+
when 'downcase'
|
643
|
+
str = str.downcase
|
644
|
+
when 'rot13'
|
645
|
+
str = str.tr('A-Za-z', 'N-ZA-Mn-za-m')
|
646
|
+
when 'urlencode'
|
647
|
+
str = CGI.escape(str)
|
648
|
+
when 'urldecode'
|
649
|
+
str = CGI.unescape(str)
|
463
650
|
else
|
464
|
-
|
651
|
+
logger.warn("Unknown rule: #{rule}")
|
465
652
|
end
|
466
653
|
end
|
654
|
+
str
|
467
655
|
end
|
468
656
|
|
469
|
-
#
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
657
|
+
#
|
658
|
+
# Send HTTP request to the SSRF server
|
659
|
+
#
|
660
|
+
# @param [String] url URI to fetch
|
661
|
+
# @param [String] method HTTP request method
|
662
|
+
# @param [Hash] headers HTTP request headers
|
663
|
+
# @param [String] body HTTP request body
|
664
|
+
#
|
665
|
+
# @raise [SSRFProxy::HTTP::Error::InvalidSsrfRequestMethod]
|
666
|
+
# Invalid SSRF request method specified.
|
667
|
+
# Method must be GET/HEAD/DELETE/POST/PUT.
|
668
|
+
# @raise [SSRFProxy::HTTP::Error::ConnectionTimeout]
|
669
|
+
# The request to the remote host timed out.
|
670
|
+
# @raise [SSRFProxy::HTTP::Error::InvalidUpstreamProxy]
|
671
|
+
# Invalid upstream proxy specified.
|
672
|
+
#
|
673
|
+
# @return [Hash] Hash of the HTTP response (status, code, headers, body)
|
674
|
+
#
|
675
|
+
def send_http_request(url, method, headers, body)
|
676
|
+
# use upstream proxy
|
677
|
+
if @upstream_proxy.nil?
|
678
|
+
http = Net::HTTP.new(@ssrf_url.host, @ssrf_url.port)
|
679
|
+
elsif @upstream_proxy.scheme =~ /\Ahttps?\z/
|
680
|
+
http = Net::HTTP::Proxy(@upstream_proxy.host, @upstream_proxy.port).new(@ssrf_url.host, @ssrf_url.port)
|
681
|
+
elsif @upstream_proxy.scheme =~ /\Asocks\z/
|
682
|
+
http = Net::HTTP.SOCKSProxy(@upstream_proxy.host, @upstream_proxy.port).new(@ssrf_url.host, @ssrf_url.port)
|
475
683
|
else
|
476
|
-
|
684
|
+
raise SSRFProxy::HTTP::Error::InvalidUpstreamProxy.new,
|
685
|
+
'Unsupported upstream proxy specified. Scheme must be http(s) or socks.'
|
477
686
|
end
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
# replace xxURLxx placeholder in SSRF HTTP GET parameters
|
520
|
-
ssrf_url = "#{@ssrf_url.path}?#{@ssrf_url.query}".gsub(/xxURLxx/, "#{target}")
|
521
|
-
# replace xxURLxx placeholder in SSRF HTTP POST parameters
|
522
|
-
post_data = @post_data.gsub(/xxURLxx/, "#{target}") unless @post_data.nil?
|
523
|
-
if @ssrf_url.scheme == 'https'
|
524
|
-
http.use_ssl = true
|
525
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE #PEER
|
526
|
-
end
|
527
|
-
# set socket options
|
528
|
-
http.open_timeout = @timeout
|
529
|
-
http.read_timeout = @timeout
|
530
|
-
# set request headers
|
531
|
-
headers = {}
|
532
|
-
headers['User-Agent'] = @user_agent unless @user_agent.nil?
|
533
|
-
headers['Cookie'] = opts['cookie'].to_s unless opts['cookie'].nil?
|
534
|
-
headers['Content-Type'] = 'application/x-www-form-urlencoded' if @method == 'POST'
|
535
|
-
response = {}
|
536
|
-
# send http request
|
537
|
-
begin
|
538
|
-
if @method == 'GET'
|
539
|
-
response = http.request Net::HTTP::Get.new(ssrf_url, headers.to_hash)
|
540
|
-
elsif @method == 'HEAD'
|
541
|
-
response = http.request Net::HTTP::Head.new(ssrf_url, headers.to_hash)
|
542
|
-
elsif @method == 'POST'
|
543
|
-
request = Net::HTTP::Post.new(ssrf_url, headers.to_hash)
|
544
|
-
request.body = post_data
|
545
|
-
response = http.request(request)
|
546
|
-
else
|
547
|
-
logger.info("SSRF request method not implemented - METHOD[#{@method}]")
|
548
|
-
response["status"] = 'fail'
|
549
|
-
response["code"] = 501
|
550
|
-
response["message"] = 'Error'
|
551
|
-
response["headers"] = "HTTP\/1.1 501 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
687
|
+
if @ssrf_url.scheme == 'https'
|
688
|
+
http.use_ssl = true
|
689
|
+
if @insecure
|
690
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
691
|
+
else
|
692
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
693
|
+
end
|
694
|
+
end
|
695
|
+
# set socket options
|
696
|
+
http.open_timeout = @timeout
|
697
|
+
http.read_timeout = @timeout
|
698
|
+
# send http request
|
699
|
+
response = {}
|
700
|
+
logger.info("Sending request: #{url}")
|
701
|
+
begin
|
702
|
+
if method == 'GET'
|
703
|
+
response = http.request Net::HTTP::Get.new(url, headers.to_hash)
|
704
|
+
elsif method == 'HEAD'
|
705
|
+
response = http.request Net::HTTP::Head.new(url, headers.to_hash)
|
706
|
+
elsif method == 'DELETE'
|
707
|
+
response = http.request Net::HTTP::Delete.new(url, headers.to_hash)
|
708
|
+
elsif method == 'POST'
|
709
|
+
request = Net::HTTP::Post.new(url, headers.to_hash)
|
710
|
+
request.body = body
|
711
|
+
response = http.request(request)
|
712
|
+
elsif method == 'PUT'
|
713
|
+
request = Net::HTTP::Put.new(url, headers.to_hash)
|
714
|
+
request.body = body
|
715
|
+
response = http.request(request)
|
716
|
+
else
|
717
|
+
logger.info("SSRF request method not implemented -- Method[#{method}]")
|
718
|
+
raise SSRFProxy::HTTP::Error::InvalidSsrfRequestMethod,
|
719
|
+
"Request method not implemented -- Method[#{method}]"
|
720
|
+
end
|
721
|
+
rescue Timeout::Error, Errno::ETIMEDOUT
|
722
|
+
logger.info("Connection timed out [#{@timeout}]")
|
723
|
+
raise SSRFProxy::HTTP::Error::ConnectionTimeout,
|
724
|
+
"Connection timed out [#{@timeout}]"
|
725
|
+
rescue => e
|
726
|
+
logger.error("Unhandled exception: #{e}")
|
727
|
+
raise e
|
552
728
|
end
|
553
|
-
|
554
|
-
logger.warn("Connection timeout - TIMEOUT[#{@timeout}] - URI[#{url}]\n")
|
555
|
-
response["status"] = 'fail'
|
556
|
-
response["code"] = 504
|
557
|
-
response["message"] = 'Timeout'
|
558
|
-
response["headers"] = "HTTP\/1.1 504 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
559
|
-
rescue => e
|
560
|
-
response["status"] = 'fail'
|
561
|
-
response["code"] = 500
|
562
|
-
response["message"] = 'Error'
|
563
|
-
response["headers"] = "HTTP\/1.1 500 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
564
|
-
logger.error("Unhandled exception: #{e.message}: #{e}")
|
729
|
+
response
|
565
730
|
end
|
566
|
-
return response
|
567
|
-
end
|
568
731
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
result["message"] = "#{$2}"
|
621
|
-
end
|
622
|
-
# No route to host
|
623
|
-
elsif head =~ /failed to open stream: No route to host in/
|
624
|
-
result["code"] = 502
|
625
|
-
result["message"] = 'Bad Gateway'
|
626
|
-
# Connection refused
|
627
|
-
elsif head =~ /failed to open stream: Connection refused in/
|
628
|
-
result["code"] = 502
|
629
|
-
result["message"] = 'Bad Gateway'
|
630
|
-
# Connection timed out
|
631
|
-
elsif head =~ /failed to open stream: Connection timed out/
|
632
|
-
result["code"] = 504
|
633
|
-
result["message"] = 'Timeout'
|
634
|
-
end
|
635
|
-
# Java 'java.net.ConnectException' errors
|
636
|
-
elsif head =~ /java\.net\.ConnectException: /
|
637
|
-
# No route to host
|
638
|
-
if head =~ /java\.net\.ConnectException: No route to host/
|
639
|
-
result["code"] = 502
|
640
|
-
result["message"] = 'Bad Gateway'
|
641
|
-
# Connection refused
|
642
|
-
elsif head =~ /java\.net\.ConnectException: Connection refused/
|
643
|
-
result["code"] = 502
|
644
|
-
result["message"] = 'Bad Gateway'
|
645
|
-
# Connection timed out
|
646
|
-
elsif head =~ /java\.net\.ConnectException: Connection timed out/
|
647
|
-
result["code"] = 504
|
648
|
-
result["message"] = 'Timeout'
|
649
|
-
end
|
650
|
-
# Java 'java.net.UnknownHostException' errors
|
651
|
-
elsif head =~ /java\.net\.UnknownHostException: /
|
652
|
-
if head =~ /java\.net\.UnknownHostException: Invalid hostname/
|
653
|
-
result["code"] = 502
|
654
|
-
result["message"] = 'Bad Gateway'
|
655
|
-
end
|
656
|
-
# Python errors
|
657
|
-
elsif head =~ /\[Errno -?[\d]{1,3}\]/
|
658
|
-
if head =~ /\[Errno 113\] No route to host/
|
659
|
-
result["code"] = 502
|
660
|
-
result["message"] = 'Bad Gateway'
|
661
|
-
elsif head =~ /\[Errno -2\] Name or service not known/
|
662
|
-
result["code"] = 502
|
663
|
-
result["message"] = 'Bad Gateway'
|
664
|
-
elsif head =~ /\[Errno 111\] Connection refused/
|
665
|
-
result["code"] = 502
|
666
|
-
result["message"] = 'Bad Gateway'
|
667
|
-
elsif head =~ /\[Errno 110\] Connection timed out/
|
668
|
-
result["code"] = 504
|
669
|
-
result["message"] = 'Timeout'
|
670
|
-
end
|
671
|
-
# Ruby errors
|
672
|
-
elsif head =~ /Errno::[A-Z]+/
|
673
|
-
# Connection refused
|
674
|
-
if head =~ /Errno::ECONNREFUSED/
|
675
|
-
result["code"] = 502
|
676
|
-
result["message"] = 'Bad Gateway'
|
677
|
-
# No route to host
|
678
|
-
elsif head =~ /Errno::EHOSTUNREACH/
|
679
|
-
result["code"] = 502
|
680
|
-
result["message"] = 'Bad Gateway'
|
681
|
-
# Connection timed out
|
682
|
-
elsif head =~ /Errno::ETIMEDOUT/
|
683
|
-
result["code"] = 504
|
684
|
-
result["message"] = 'Timeout'
|
732
|
+
#
|
733
|
+
# Guess HTTP response status code and message based
|
734
|
+
# on common strings in the response body such
|
735
|
+
# as a default title or exception error message
|
736
|
+
#
|
737
|
+
# @param [String] response HTTP response
|
738
|
+
#
|
739
|
+
# @return [Hash] includes HTTP response code and message
|
740
|
+
#
|
741
|
+
def guess_status(response)
|
742
|
+
result = {}
|
743
|
+
# generic page titles containing HTTP status
|
744
|
+
if response =~ />400 Bad Request</
|
745
|
+
result['code'] = 400
|
746
|
+
result['message'] = 'Bad Request'
|
747
|
+
elsif response =~ />401 Unauthorized</
|
748
|
+
result['code'] = 401
|
749
|
+
result['message'] = 'Unauthorized'
|
750
|
+
elsif response =~ />403 Forbidden</
|
751
|
+
result['code'] = 403
|
752
|
+
result['message'] = 'Forbidden'
|
753
|
+
elsif response =~ />404 Not Found</
|
754
|
+
result['code'] = 404
|
755
|
+
result['message'] = 'Not Found'
|
756
|
+
elsif response =~ />500 Internal Server Error</
|
757
|
+
result['code'] = 500
|
758
|
+
result['message'] = 'Internal Server Error'
|
759
|
+
elsif response =~ />503 Service Unavailable</
|
760
|
+
result['code'] = 503
|
761
|
+
result['message'] = 'Service Unavailable'
|
762
|
+
# getaddrinfo() errors
|
763
|
+
elsif response =~ /getaddrinfo: /
|
764
|
+
if response =~ /getaddrinfo: nodename nor servname provided/
|
765
|
+
result['code'] = 502
|
766
|
+
result['message'] = 'Bad Gateway'
|
767
|
+
elsif response =~ /getaddrinfo: Name or service not known/
|
768
|
+
result['code'] = 502
|
769
|
+
result['message'] = 'Bad Gateway'
|
770
|
+
end
|
771
|
+
# getnameinfo() errors
|
772
|
+
elsif response =~ /getnameinfo failed: /
|
773
|
+
result['code'] = 502
|
774
|
+
result['message'] = 'Bad Gateway'
|
775
|
+
# PHP 'failed to open stream' errors
|
776
|
+
elsif response =~ /failed to open stream: /
|
777
|
+
# HTTP request failed! HTTP/[version] [code] [message]
|
778
|
+
if response =~ %r{failed to open stream: HTTP request failed! HTTP\/(0\.9|1\.0|1\.1) ([\d]+) }
|
779
|
+
result['code'] = $2.to_s
|
780
|
+
result['message'] = ''
|
781
|
+
if response =~ %r{failed to open stream: HTTP request failed! HTTP/(0\.9|1\.0|1\.1) [\d]+ ([a-zA-Z ]+)}
|
782
|
+
result['message'] = $2.to_s
|
685
783
|
end
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
784
|
+
# No route to host
|
785
|
+
elsif response =~ /failed to open stream: No route to host in/
|
786
|
+
result['code'] = 502
|
787
|
+
result['message'] = 'Bad Gateway'
|
788
|
+
# Connection refused
|
789
|
+
elsif response =~ /failed to open stream: Connection refused in/
|
790
|
+
result['code'] = 502
|
791
|
+
result['message'] = 'Bad Gateway'
|
792
|
+
# Connection timed out
|
793
|
+
elsif response =~ /failed to open stream: Connection timed out/
|
794
|
+
result['code'] = 504
|
795
|
+
result['message'] = 'Timeout'
|
796
|
+
# Success - This likely indicates an SSL/TLS connection failure
|
797
|
+
elsif response =~ /failed to open stream: Success in/
|
798
|
+
result['code'] = 502
|
799
|
+
result['message'] = 'Bad Gateway'
|
800
|
+
end
|
801
|
+
# Java 'java.net' exceptions
|
802
|
+
elsif response =~ /java\.net\.[^\s]*Exception: /
|
803
|
+
if response =~ /java\.net\.ConnectException: No route to host/
|
804
|
+
result['code'] = 502
|
805
|
+
result['message'] = 'Bad Gateway'
|
806
|
+
elsif response =~ /java\.net\.ConnectException: Connection refused/
|
807
|
+
result['code'] = 502
|
808
|
+
result['message'] = 'Bad Gateway'
|
809
|
+
elsif response =~ /java\.net\.ConnectException: Connection timed out/
|
810
|
+
result['code'] = 504
|
811
|
+
result['message'] = 'Timeout'
|
812
|
+
elsif response =~ /java\.net\.UnknownHostException: Invalid hostname/
|
813
|
+
result['code'] = 502
|
814
|
+
result['message'] = 'Bad Gateway'
|
815
|
+
elsif response =~ /java\.net\.SocketException: Network is unreachable/
|
816
|
+
result['code'] = 502
|
817
|
+
result['message'] = 'Bad Gateway'
|
818
|
+
elsif response =~ /java\.net\.SocketException: Connection reset/
|
819
|
+
result['code'] = 502
|
820
|
+
result['message'] = 'Bad Gateway'
|
821
|
+
elsif response =~ /java\.net\.SocketTimeoutException: Connection timed out/
|
822
|
+
result['code'] = 504
|
823
|
+
result['message'] = 'Timeout'
|
824
|
+
end
|
825
|
+
# C errno
|
826
|
+
elsif response =~ /\[Errno -?[\d]{1,3}\]/
|
827
|
+
if response =~ /\[Errno -2\] Name or service not known/
|
828
|
+
result['code'] = 502
|
829
|
+
result['message'] = 'Bad Gateway'
|
830
|
+
elsif response =~ /\[Errno 101\] Network is unreachable/
|
831
|
+
result['code'] = 502
|
832
|
+
result['message'] = 'Bad Gateway'
|
833
|
+
elsif response =~ /\[Errno 104\] Connection reset by peer/
|
834
|
+
result['code'] = 502
|
835
|
+
result['message'] = 'Bad Gateway'
|
836
|
+
elsif response =~ /\[Errno 110\] Connection timed out/
|
837
|
+
result['code'] = 504
|
838
|
+
result['message'] = 'Timeout'
|
839
|
+
elsif response =~ /\[Errno 111\] Connection refused/
|
840
|
+
result['code'] = 502
|
841
|
+
result['message'] = 'Bad Gateway'
|
842
|
+
elsif response =~ /\[Errno 113\] No route to host/
|
843
|
+
result['code'] = 502
|
844
|
+
result['message'] = 'Bad Gateway'
|
845
|
+
end
|
846
|
+
# Python urllib errors
|
847
|
+
elsif response =~ /HTTPError: HTTP Error \d+/
|
848
|
+
if response =~ /HTTPError: HTTP Error 400: Bad Request/
|
849
|
+
result['code'] = 400
|
850
|
+
result['message'] = 'Bad Request'
|
851
|
+
elsif response =~ /HTTPError: HTTP Error 401: Unauthorized/
|
852
|
+
result['code'] = 401
|
853
|
+
result['message'] = 'Unauthorized'
|
854
|
+
elsif response =~ /HTTPError: HTTP Error 402: Payment Required/
|
855
|
+
result['code'] = 402
|
856
|
+
result['message'] = 'Payment Required'
|
857
|
+
elsif response =~ /HTTPError: HTTP Error 403: Forbidden/
|
858
|
+
result['code'] = 403
|
859
|
+
result['message'] = 'Forbidden'
|
860
|
+
elsif response =~ /HTTPError: HTTP Error 404: Not Found/
|
861
|
+
result['code'] = 404
|
862
|
+
result['message'] = 'Not Found'
|
863
|
+
elsif response =~ /HTTPError: HTTP Error 405: Method Not Allowed/
|
864
|
+
result['code'] = 405
|
865
|
+
result['message'] = 'Method Not Allowed'
|
866
|
+
elsif response =~ /HTTPError: HTTP Error 410: Gone/
|
867
|
+
result['code'] = 410
|
868
|
+
result['message'] = 'Gone'
|
869
|
+
elsif response =~ /HTTPError: HTTP Error 500: Internal Server Error/
|
870
|
+
result['code'] = 500
|
871
|
+
result['message'] = 'Internal Server Error'
|
872
|
+
elsif response =~ /HTTPError: HTTP Error 502: Bad Gateway/
|
873
|
+
result['code'] = 502
|
874
|
+
result['message'] = 'Bad Gateway'
|
875
|
+
elsif response =~ /HTTPError: HTTP Error 503: Service Unavailable/
|
876
|
+
result['code'] = 503
|
877
|
+
result['message'] = 'Service Unavailable'
|
878
|
+
elsif response =~ /HTTPError: HTTP Error 504: Gateway Time-?out/
|
879
|
+
result['code'] = 504
|
880
|
+
result['message'] = 'Timeout'
|
881
|
+
end
|
882
|
+
# Ruby exceptions
|
883
|
+
elsif response =~ /Errno::[A-Z]+/
|
884
|
+
# Connection refused
|
885
|
+
if response =~ /Errno::ECONNREFUSED/
|
886
|
+
result['code'] = 502
|
887
|
+
result['message'] = 'Bad Gateway'
|
888
|
+
# No route to host
|
889
|
+
elsif response =~ /Errno::EHOSTUNREACH/
|
890
|
+
result['code'] = 502
|
891
|
+
result['message'] = 'Bad Gateway'
|
892
|
+
# Connection timed out
|
893
|
+
elsif response =~ /Errno::ETIMEDOUT/
|
894
|
+
result['code'] = 504
|
895
|
+
result['message'] = 'Timeout'
|
896
|
+
end
|
897
|
+
# ASP.NET System.Net.WebClient errors
|
898
|
+
elsif response =~ /System\.Net\.WebClient/
|
899
|
+
# The remote server returned an error: ([code]) [message].
|
900
|
+
if response =~ /WebException: The remote server returned an error: \(([\d+])\) /
|
901
|
+
result['code'] = $1.to_s
|
902
|
+
result['message'] = ''
|
903
|
+
if response =~ /WebException: The remote server returned an error: \(([\d+])\) ([a-zA-Z ]+)\./
|
904
|
+
result['message'] = $2.to_s
|
699
905
|
end
|
906
|
+
# Could not resolve hostname
|
907
|
+
elsif response =~ /WebException: The remote name could not be resolved/
|
908
|
+
result['code'] = 502
|
909
|
+
result['message'] = 'Bad Gateway'
|
910
|
+
# Remote server denied connection (port closed)
|
911
|
+
elsif response =~ /WebException: Unable to connect to the remote server/
|
912
|
+
result['code'] = 502
|
913
|
+
result['message'] = 'Bad Gateway'
|
914
|
+
# This likely indicates a plain-text connection to a HTTPS or non-HTTP service
|
915
|
+
elsif response =~ /WebException: The underlying connection was closed: An unexpected error occurred on a receive/
|
916
|
+
result['code'] = 502
|
917
|
+
result['message'] = 'Bad Gateway'
|
918
|
+
# This likely indicates a HTTPS connection to a plain-text HTTP or non-HTTP service
|
919
|
+
elsif response =~ /WebException: The underlying connection was closed: An unexpected error occurred on a send/
|
920
|
+
result['code'] = 502
|
921
|
+
result['message'] = 'Bad Gateway'
|
922
|
+
# The operation has timed out
|
923
|
+
elsif response =~ /WebException: The operation has timed out/
|
924
|
+
result['code'] = 504
|
925
|
+
result['message'] = 'Timeout'
|
700
926
|
end
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
927
|
+
# Generic error messages
|
928
|
+
elsif response =~ /(Connection refused|No route to host|Connection timed out) - connect\(\d\)/
|
929
|
+
# Connection refused
|
930
|
+
if response =~ /Connection refused - connect\(\d\)/
|
931
|
+
result['code'] = 502
|
932
|
+
result['message'] = 'Bad Gateway'
|
933
|
+
# No route to host
|
934
|
+
elsif response =~ /No route to host - connect\(\d\)/
|
935
|
+
result['code'] = 502
|
936
|
+
result['message'] = 'Bad Gateway'
|
937
|
+
# Connection timed out
|
938
|
+
elsif response =~ /Connection timed out - connect\(\d\)/
|
939
|
+
result['code'] = 504
|
940
|
+
result['message'] = 'Timeout'
|
708
941
|
end
|
709
|
-
result["headers"] << "#{header_name}: #{header_value}\n"
|
710
942
|
end
|
711
|
-
result
|
712
|
-
result["body"] = "#{response.body}" unless response.body.nil?
|
713
|
-
rescue => e
|
714
|
-
logger.info("Malformed HTTP response from server")
|
715
|
-
result["status"] = 'fail'
|
716
|
-
result["code"] = 502
|
717
|
-
result["message"] = 'Error'
|
718
|
-
result["headers"] = "HTTP\/1.1 502 Error\nServer: SSRF Proxy\nContent-Length: 0\n\n"
|
943
|
+
result
|
719
944
|
end
|
720
|
-
return result
|
721
|
-
end
|
722
945
|
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
3gp,video/3gpp,
|
737
|
-
3gp2,video/3gpp2,
|
738
|
-
3gpp,video/3gpp,
|
739
|
-
7z,application/x-7z-compressed,
|
740
|
-
aa,audio/audible,
|
741
|
-
AAC,audio/aac,
|
742
|
-
aaf,application/octet-stream,
|
743
|
-
aax,audio/vnd.audible.aax,
|
744
|
-
ac3,audio/ac3,
|
745
|
-
aca,application/octet-stream,
|
746
|
-
accda,application/msaccess.addin,
|
747
|
-
accdb,application/msaccess,
|
748
|
-
accdc,application/msaccess.cab,
|
749
|
-
accde,application/msaccess,
|
750
|
-
accdr,application/msaccess.runtime,
|
751
|
-
accdt,application/msaccess,
|
752
|
-
accdw,application/msaccess.webapplication,
|
753
|
-
accft,application/msaccess.ftemplate,
|
754
|
-
acx,application/internet-property-stream,
|
755
|
-
AddIn,text/xml,
|
756
|
-
ade,application/msaccess,
|
757
|
-
adobebridge,application/x-bridge-url,
|
758
|
-
adp,application/msaccess,
|
759
|
-
ADT,audio/vnd.dlna.adts,
|
760
|
-
ADTS,audio/aac,
|
761
|
-
afm,application/octet-stream,
|
762
|
-
ai,application/postscript,
|
763
|
-
aif,audio/x-aiff,
|
764
|
-
aifc,audio/aiff,
|
765
|
-
aiff,audio/aiff,
|
766
|
-
air,application/vnd.adobe.air-application-installer-package+zip,
|
767
|
-
amc,application/x-mpeg,
|
768
|
-
application,application/x-ms-application,
|
769
|
-
art,image/x-jg,
|
770
|
-
asa,application/xml,
|
771
|
-
asax,application/xml,
|
772
|
-
ascx,application/xml,
|
773
|
-
asd,application/octet-stream,
|
774
|
-
asf,video/x-ms-asf,
|
775
|
-
ashx,application/xml,
|
776
|
-
asi,application/octet-stream,
|
777
|
-
asm,text/plain,
|
778
|
-
asmx,application/xml,
|
779
|
-
aspx,application/xml,
|
780
|
-
asr,video/x-ms-asf,
|
781
|
-
asx,video/x-ms-asf,
|
782
|
-
atom,application/atom+xml,
|
783
|
-
au,audio/basic,
|
784
|
-
avi,video/x-msvideo,
|
785
|
-
axs,application/olescript,
|
786
|
-
bas,text/plain,
|
787
|
-
bcpio,application/x-bcpio,
|
788
|
-
bin,application/octet-stream,
|
789
|
-
bmp,image/bmp,
|
790
|
-
c,text/plain,
|
791
|
-
cab,application/octet-stream,
|
792
|
-
caf,audio/x-caf,
|
793
|
-
calx,application/vnd.ms-office.calx,
|
794
|
-
cat,application/vnd.ms-pki.seccat,
|
795
|
-
cc,text/plain,
|
796
|
-
cd,text/plain,
|
797
|
-
cdda,audio/aiff,
|
798
|
-
cdf,application/x-cdf,
|
799
|
-
cer,application/x-x509-ca-cert,
|
800
|
-
chm,application/octet-stream,
|
801
|
-
class,application/x-java-applet,
|
802
|
-
clp,application/x-msclip,
|
803
|
-
cmx,image/x-cmx,
|
804
|
-
cnf,text/plain,
|
805
|
-
cod,image/cis-cod,
|
806
|
-
config,application/xml,
|
807
|
-
contact,text/x-ms-contact,
|
808
|
-
coverage,application/xml,
|
809
|
-
cpio,application/x-cpio,
|
810
|
-
cpp,text/plain,
|
811
|
-
crd,application/x-mscardfile,
|
812
|
-
crl,application/pkix-crl,
|
813
|
-
crt,application/x-x509-ca-cert,
|
814
|
-
cs,text/plain,
|
815
|
-
csdproj,text/plain,
|
816
|
-
csh,application/x-csh,
|
817
|
-
csproj,text/plain,
|
818
|
-
css,text/css,
|
819
|
-
csv,text/csv,
|
820
|
-
cur,application/octet-stream,
|
821
|
-
cxx,text/plain,
|
822
|
-
dat,application/octet-stream,
|
823
|
-
datasource,application/xml,
|
824
|
-
dbproj,text/plain,
|
825
|
-
dcr,application/x-director,
|
826
|
-
def,text/plain,
|
827
|
-
deploy,application/octet-stream,
|
828
|
-
der,application/x-x509-ca-cert,
|
829
|
-
dgml,application/xml,
|
830
|
-
dib,image/bmp,
|
831
|
-
dif,video/x-dv,
|
832
|
-
dir,application/x-director,
|
833
|
-
disco,text/xml,
|
834
|
-
dll,application/x-msdownload,
|
835
|
-
dll.config,text/xml,
|
836
|
-
dlm,text/dlm,
|
837
|
-
doc,application/msword,
|
838
|
-
docm,application/vnd.ms-word.document.macroEnabled.12,
|
839
|
-
docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document,
|
840
|
-
dot,application/msword,
|
841
|
-
dotm,application/vnd.ms-word.template.macroEnabled.12,
|
842
|
-
dotx,application/vnd.openxmlformats-officedocument.wordprocessingml.template,
|
843
|
-
dsp,application/octet-stream,
|
844
|
-
dsw,text/plain,
|
845
|
-
dtd,text/xml,
|
846
|
-
dtsConfig,text/xml,
|
847
|
-
dv,video/x-dv,
|
848
|
-
dvi,application/x-dvi,
|
849
|
-
dwf,drawing/x-dwf,
|
850
|
-
dwp,application/octet-stream,
|
851
|
-
dxr,application/x-director,
|
852
|
-
eml,message/rfc822,
|
853
|
-
emz,application/octet-stream,
|
854
|
-
eot,application/octet-stream,
|
855
|
-
eps,application/postscript,
|
856
|
-
etl,application/etl,
|
857
|
-
etx,text/x-setext,
|
858
|
-
evy,application/envoy,
|
859
|
-
exe,application/octet-stream,
|
860
|
-
exe.config,text/xml,
|
861
|
-
fdf,application/vnd.fdf,
|
862
|
-
fif,application/fractals,
|
863
|
-
filters,Application/xml,
|
864
|
-
fla,application/octet-stream,
|
865
|
-
flr,x-world/x-vrml,
|
866
|
-
flv,video/x-flv,
|
867
|
-
fsscript,application/fsharp-script,
|
868
|
-
fsx,application/fsharp-script,
|
869
|
-
generictest,application/xml,
|
870
|
-
gif,image/gif,
|
871
|
-
group,text/x-ms-group,
|
872
|
-
gsm,audio/x-gsm,
|
873
|
-
gtar,application/x-gtar,
|
874
|
-
gz,application/x-gzip,
|
875
|
-
h,text/plain,
|
876
|
-
hdf,application/x-hdf,
|
877
|
-
hdml,text/x-hdml,
|
878
|
-
hhc,application/x-oleobject,
|
879
|
-
hhk,application/octet-stream,
|
880
|
-
hhp,application/octet-stream,
|
881
|
-
hlp,application/winhlp,
|
882
|
-
hpp,text/plain,
|
883
|
-
hqx,application/mac-binhex40,
|
884
|
-
hta,application/hta,
|
885
|
-
htc,text/x-component,
|
886
|
-
htm,text/html,
|
887
|
-
html,text/html,
|
888
|
-
htt,text/webviewhtml,
|
889
|
-
hxa,application/xml,
|
890
|
-
hxc,application/xml,
|
891
|
-
hxd,application/octet-stream,
|
892
|
-
hxe,application/xml,
|
893
|
-
hxf,application/xml,
|
894
|
-
hxh,application/octet-stream,
|
895
|
-
hxi,application/octet-stream,
|
896
|
-
hxk,application/xml,
|
897
|
-
hxq,application/octet-stream,
|
898
|
-
hxr,application/octet-stream,
|
899
|
-
hxs,application/octet-stream,
|
900
|
-
hxt,text/html,
|
901
|
-
hxv,application/xml,
|
902
|
-
hxw,application/octet-stream,
|
903
|
-
hxx,text/plain,
|
904
|
-
i,text/plain,
|
905
|
-
ico,image/x-icon,
|
906
|
-
ics,application/octet-stream,
|
907
|
-
idl,text/plain,
|
908
|
-
ief,image/ief,
|
909
|
-
iii,application/x-iphone,
|
910
|
-
inc,text/plain,
|
911
|
-
inf,application/octet-stream,
|
912
|
-
inl,text/plain,
|
913
|
-
ins,application/x-internet-signup,
|
914
|
-
ipa,application/x-itunes-ipa,
|
915
|
-
ipg,application/x-itunes-ipg,
|
916
|
-
ipproj,text/plain,
|
917
|
-
ipsw,application/x-itunes-ipsw,
|
918
|
-
iqy,text/x-ms-iqy,
|
919
|
-
isp,application/x-internet-signup,
|
920
|
-
ite,application/x-itunes-ite,
|
921
|
-
itlp,application/x-itunes-itlp,
|
922
|
-
itms,application/x-itunes-itms,
|
923
|
-
itpc,application/x-itunes-itpc,
|
924
|
-
IVF,video/x-ivf,
|
925
|
-
jar,application/java-archive,
|
926
|
-
java,application/octet-stream,
|
927
|
-
jck,application/liquidmotion,
|
928
|
-
jcz,application/liquidmotion,
|
929
|
-
jfif,image/pjpeg,
|
930
|
-
jnlp,application/x-java-jnlp-file,
|
931
|
-
jpb,application/octet-stream,
|
932
|
-
jpe,image/jpeg,
|
933
|
-
jpeg,image/jpeg,
|
934
|
-
jpg,image/jpeg,
|
935
|
-
js,application/x-javascript,
|
936
|
-
json,application/json,
|
937
|
-
jsx,text/jscript,
|
938
|
-
jsxbin,text/plain,
|
939
|
-
latex,application/x-latex,
|
940
|
-
library-ms,application/windows-library+xml,
|
941
|
-
lit,application/x-ms-reader,
|
942
|
-
loadtest,application/xml,
|
943
|
-
lpk,application/octet-stream,
|
944
|
-
lsf,video/x-la-asf,
|
945
|
-
lst,text/plain,
|
946
|
-
lsx,video/x-la-asf,
|
947
|
-
lzh,application/octet-stream,
|
948
|
-
m13,application/x-msmediaview,
|
949
|
-
m14,application/x-msmediaview,
|
950
|
-
m1v,video/mpeg,
|
951
|
-
m2t,video/vnd.dlna.mpeg-tts,
|
952
|
-
m2ts,video/vnd.dlna.mpeg-tts,
|
953
|
-
m2v,video/mpeg,
|
954
|
-
m3u,audio/x-mpegurl,
|
955
|
-
m3u8,audio/x-mpegurl,
|
956
|
-
m4a,audio/m4a,
|
957
|
-
m4b,audio/m4b,
|
958
|
-
m4p,audio/m4p,
|
959
|
-
m4r,audio/x-m4r,
|
960
|
-
m4v,video/x-m4v,
|
961
|
-
mac,image/x-macpaint,
|
962
|
-
mak,text/plain,
|
963
|
-
man,application/x-troff-man,
|
964
|
-
manifest,application/x-ms-manifest,
|
965
|
-
map,text/plain,
|
966
|
-
master,application/xml,
|
967
|
-
mda,application/msaccess,
|
968
|
-
mdb,application/x-msaccess,
|
969
|
-
mde,application/msaccess,
|
970
|
-
mdp,application/octet-stream,
|
971
|
-
me,application/x-troff-me,
|
972
|
-
mfp,application/x-shockwave-flash,
|
973
|
-
mht,message/rfc822,
|
974
|
-
mhtml,message/rfc822,
|
975
|
-
mid,audio/mid,
|
976
|
-
midi,audio/mid,
|
977
|
-
mix,application/octet-stream,
|
978
|
-
mk,text/plain,
|
979
|
-
mmf,application/x-smaf,
|
980
|
-
mno,text/xml,
|
981
|
-
mny,application/x-msmoney,
|
982
|
-
mod,video/mpeg,
|
983
|
-
mov,video/quicktime,
|
984
|
-
movie,video/x-sgi-movie,
|
985
|
-
mp2,video/mpeg,
|
986
|
-
mp2v,video/mpeg,
|
987
|
-
mp3,audio/mpeg,
|
988
|
-
mp4,video/mp4,
|
989
|
-
mp4v,video/mp4,
|
990
|
-
mpa,video/mpeg,
|
991
|
-
mpe,video/mpeg,
|
992
|
-
mpeg,video/mpeg,
|
993
|
-
mpf,application/vnd.ms-mediapackage,
|
994
|
-
mpg,video/mpeg,
|
995
|
-
mpp,application/vnd.ms-project,
|
996
|
-
mpv2,video/mpeg,
|
997
|
-
mqv,video/quicktime,
|
998
|
-
ms,application/x-troff-ms,
|
999
|
-
msi,application/octet-stream,
|
1000
|
-
mso,application/octet-stream,
|
1001
|
-
mts,video/vnd.dlna.mpeg-tts,
|
1002
|
-
mtx,application/xml,
|
1003
|
-
mvb,application/x-msmediaview,
|
1004
|
-
mvc,application/x-miva-compiled,
|
1005
|
-
mxp,application/x-mmxp,
|
1006
|
-
nc,application/x-netcdf,
|
1007
|
-
nsc,video/x-ms-asf,
|
1008
|
-
nws,message/rfc822,
|
1009
|
-
ocx,application/octet-stream,
|
1010
|
-
oda,application/oda,
|
1011
|
-
odc,text/x-ms-odc,
|
1012
|
-
odh,text/plain,
|
1013
|
-
odl,text/plain,
|
1014
|
-
odp,application/vnd.oasis.opendocument.presentation,
|
1015
|
-
ods,application/oleobject,
|
1016
|
-
odt,application/vnd.oasis.opendocument.text,
|
1017
|
-
one,application/onenote,
|
1018
|
-
onea,application/onenote,
|
1019
|
-
onepkg,application/onenote,
|
1020
|
-
onetmp,application/onenote,
|
1021
|
-
onetoc,application/onenote,
|
1022
|
-
onetoc2,application/onenote,
|
1023
|
-
orderedtest,application/xml,
|
1024
|
-
osdx,application/opensearchdescription+xml,
|
1025
|
-
p10,application/pkcs10,
|
1026
|
-
p12,application/x-pkcs12,
|
1027
|
-
p7b,application/x-pkcs7-certificates,
|
1028
|
-
p7c,application/pkcs7-mime,
|
1029
|
-
p7m,application/pkcs7-mime,
|
1030
|
-
p7r,application/x-pkcs7-certreqresp,
|
1031
|
-
p7s,application/pkcs7-signature,
|
1032
|
-
pbm,image/x-portable-bitmap,
|
1033
|
-
pcast,application/x-podcast,
|
1034
|
-
pct,image/pict,
|
1035
|
-
pcx,application/octet-stream,
|
1036
|
-
pcz,application/octet-stream,
|
1037
|
-
pdf,application/pdf,
|
1038
|
-
pfb,application/octet-stream,
|
1039
|
-
pfm,application/octet-stream,
|
1040
|
-
pfx,application/x-pkcs12,
|
1041
|
-
pgm,image/x-portable-graymap,
|
1042
|
-
pic,image/pict,
|
1043
|
-
pict,image/pict,
|
1044
|
-
pkgdef,text/plain,
|
1045
|
-
pkgundef,text/plain,
|
1046
|
-
pko,application/vnd.ms-pki.pko,
|
1047
|
-
pls,audio/scpls,
|
1048
|
-
pma,application/x-perfmon,
|
1049
|
-
pmc,application/x-perfmon,
|
1050
|
-
pml,application/x-perfmon,
|
1051
|
-
pmr,application/x-perfmon,
|
1052
|
-
pmw,application/x-perfmon,
|
1053
|
-
png,image/png,
|
1054
|
-
pnm,image/x-portable-anymap,
|
1055
|
-
pnt,image/x-macpaint,
|
1056
|
-
pntg,image/x-macpaint,
|
1057
|
-
pnz,image/png,
|
1058
|
-
pot,application/vnd.ms-powerpoint,
|
1059
|
-
potm,application/vnd.ms-powerpoint.template.macroEnabled.12,
|
1060
|
-
potx,application/vnd.openxmlformats-officedocument.presentationml.template,
|
1061
|
-
ppa,application/vnd.ms-powerpoint,
|
1062
|
-
ppam,application/vnd.ms-powerpoint.addin.macroEnabled.12,
|
1063
|
-
ppm,image/x-portable-pixmap,
|
1064
|
-
pps,application/vnd.ms-powerpoint,
|
1065
|
-
ppsm,application/vnd.ms-powerpoint.slideshow.macroEnabled.12,
|
1066
|
-
ppsx,application/vnd.openxmlformats-officedocument.presentationml.slideshow,
|
1067
|
-
ppt,application/vnd.ms-powerpoint,
|
1068
|
-
pptm,application/vnd.ms-powerpoint.presentation.macroEnabled.12,
|
1069
|
-
pptx,application/vnd.openxmlformats-officedocument.presentationml.presentation,
|
1070
|
-
prf,application/pics-rules,
|
1071
|
-
prm,application/octet-stream,
|
1072
|
-
prx,application/octet-stream,
|
1073
|
-
ps,application/postscript,
|
1074
|
-
psc1,application/PowerShell,
|
1075
|
-
psd,application/octet-stream,
|
1076
|
-
psess,application/xml,
|
1077
|
-
psm,application/octet-stream,
|
1078
|
-
psp,application/octet-stream,
|
1079
|
-
pub,application/x-mspublisher,
|
1080
|
-
pwz,application/vnd.ms-powerpoint,
|
1081
|
-
qht,text/x-html-insertion,
|
1082
|
-
qhtm,text/x-html-insertion,
|
1083
|
-
qt,video/quicktime,
|
1084
|
-
qti,image/x-quicktime,
|
1085
|
-
qtif,image/x-quicktime,
|
1086
|
-
qtl,application/x-quicktimeplayer,
|
1087
|
-
qxd,application/octet-stream,
|
1088
|
-
ra,audio/x-pn-realaudio,
|
1089
|
-
ram,audio/x-pn-realaudio,
|
1090
|
-
rar,application/octet-stream,
|
1091
|
-
ras,image/x-cmu-raster,
|
1092
|
-
rat,application/rat-file,
|
1093
|
-
rc,text/plain,
|
1094
|
-
rc2,text/plain,
|
1095
|
-
rct,text/plain,
|
1096
|
-
rdlc,application/xml,
|
1097
|
-
resx,application/xml,
|
1098
|
-
rf,image/vnd.rn-realflash,
|
1099
|
-
rgb,image/x-rgb,
|
1100
|
-
rgs,text/plain,
|
1101
|
-
rm,application/vnd.rn-realmedia,
|
1102
|
-
rmi,audio/mid,
|
1103
|
-
rmp,application/vnd.rn-rn_music_package,
|
1104
|
-
roff,application/x-troff,
|
1105
|
-
rpm,audio/x-pn-realaudio-plugin,
|
1106
|
-
rqy,text/x-ms-rqy,
|
1107
|
-
rtf,application/rtf,
|
1108
|
-
rtx,text/richtext,
|
1109
|
-
ruleset,application/xml,
|
1110
|
-
s,text/plain,
|
1111
|
-
safariextz,application/x-safari-safariextz,
|
1112
|
-
scd,application/x-msschedule,
|
1113
|
-
sct,text/scriptlet,
|
1114
|
-
sd2,audio/x-sd2,
|
1115
|
-
sdp,application/sdp,
|
1116
|
-
sea,application/octet-stream,
|
1117
|
-
searchConnector-ms,application/windows-search-connector+xml,
|
1118
|
-
setpay,application/set-payment-initiation,
|
1119
|
-
setreg,application/set-registration-initiation,
|
1120
|
-
settings,application/xml,
|
1121
|
-
sgimb,application/x-sgimb,
|
1122
|
-
sgml,text/sgml,
|
1123
|
-
sh,application/x-sh,
|
1124
|
-
shar,application/x-shar,
|
1125
|
-
shtml,text/html,
|
1126
|
-
sit,application/x-stuffit,
|
1127
|
-
sitemap,application/xml,
|
1128
|
-
skin,application/xml,
|
1129
|
-
sldm,application/vnd.ms-powerpoint.slide.macroEnabled.12,
|
1130
|
-
sldx,application/vnd.openxmlformats-officedocument.presentationml.slide,
|
1131
|
-
slk,application/vnd.ms-excel,
|
1132
|
-
sln,text/plain,
|
1133
|
-
slupkg-ms,application/x-ms-license,
|
1134
|
-
smd,audio/x-smd,
|
1135
|
-
smi,application/octet-stream,
|
1136
|
-
smx,audio/x-smd,
|
1137
|
-
smz,audio/x-smd,
|
1138
|
-
snd,audio/basic,
|
1139
|
-
snippet,application/xml,
|
1140
|
-
snp,application/octet-stream,
|
1141
|
-
sol,text/plain,
|
1142
|
-
sor,text/plain,
|
1143
|
-
spc,application/x-pkcs7-certificates,
|
1144
|
-
spl,application/futuresplash,
|
1145
|
-
src,application/x-wais-source,
|
1146
|
-
srf,text/plain,
|
1147
|
-
SSISDeploymentManifest,text/xml,
|
1148
|
-
ssm,application/streamingmedia,
|
1149
|
-
sst,application/vnd.ms-pki.certstore,
|
1150
|
-
stl,application/vnd.ms-pki.stl,
|
1151
|
-
sv4cpio,application/x-sv4cpio,
|
1152
|
-
sv4crc,application/x-sv4crc,
|
1153
|
-
svc,application/xml,
|
1154
|
-
swf,application/x-shockwave-flash,
|
1155
|
-
t,application/x-troff,
|
1156
|
-
tar,application/x-tar,
|
1157
|
-
tcl,application/x-tcl,
|
1158
|
-
testrunconfig,application/xml,
|
1159
|
-
testsettings,application/xml,
|
1160
|
-
tex,application/x-tex,
|
1161
|
-
texi,application/x-texinfo,
|
1162
|
-
texinfo,application/x-texinfo,
|
1163
|
-
tgz,application/x-compressed,
|
1164
|
-
thmx,application/vnd.ms-officetheme,
|
1165
|
-
thn,application/octet-stream,
|
1166
|
-
tif,image/tiff,
|
1167
|
-
tiff,image/tiff,
|
1168
|
-
tlh,text/plain,
|
1169
|
-
tli,text/plain,
|
1170
|
-
toc,application/octet-stream,
|
1171
|
-
tr,application/x-troff,
|
1172
|
-
trm,application/x-msterminal,
|
1173
|
-
trx,application/xml,
|
1174
|
-
ts,video/vnd.dlna.mpeg-tts,
|
1175
|
-
tsv,text/tab-separated-values,
|
1176
|
-
ttf,application/octet-stream,
|
1177
|
-
tts,video/vnd.dlna.mpeg-tts,
|
1178
|
-
txt,text/plain,
|
1179
|
-
u32,application/octet-stream,
|
1180
|
-
uls,text/iuls,
|
1181
|
-
user,text/plain,
|
1182
|
-
ustar,application/x-ustar,
|
1183
|
-
vb,text/plain,
|
1184
|
-
vbdproj,text/plain,
|
1185
|
-
vbk,video/mpeg,
|
1186
|
-
vbproj,text/plain,
|
1187
|
-
vbs,text/vbscript,
|
1188
|
-
vcf,text/x-vcard,
|
1189
|
-
vcproj,Application/xml,
|
1190
|
-
vcs,text/plain,
|
1191
|
-
vcxproj,Application/xml,
|
1192
|
-
vddproj,text/plain,
|
1193
|
-
vdp,text/plain,
|
1194
|
-
vdproj,text/plain,
|
1195
|
-
vdx,application/vnd.ms-visio.viewer,
|
1196
|
-
vml,text/xml,
|
1197
|
-
vscontent,application/xml,
|
1198
|
-
vsct,text/xml,
|
1199
|
-
vsd,application/vnd.visio,
|
1200
|
-
vsi,application/ms-vsi,
|
1201
|
-
vsix,application/vsix,
|
1202
|
-
vsixlangpack,text/xml,
|
1203
|
-
vsixmanifest,text/xml,
|
1204
|
-
vsmdi,application/xml,
|
1205
|
-
vspscc,text/plain,
|
1206
|
-
vss,application/vnd.visio,
|
1207
|
-
vsscc,text/plain,
|
1208
|
-
vssettings,text/xml,
|
1209
|
-
vssscc,text/plain,
|
1210
|
-
vst,application/vnd.visio,
|
1211
|
-
vstemplate,text/xml,
|
1212
|
-
vsto,application/x-ms-vsto,
|
1213
|
-
vsw,application/vnd.visio,
|
1214
|
-
vsx,application/vnd.visio,
|
1215
|
-
vtx,application/vnd.visio,
|
1216
|
-
wav,audio/wav,
|
1217
|
-
wave,audio/wav,
|
1218
|
-
wax,audio/x-ms-wax,
|
1219
|
-
wbk,application/msword,
|
1220
|
-
wbmp,image/vnd.wap.wbmp,
|
1221
|
-
wcm,application/vnd.ms-works,
|
1222
|
-
wdb,application/vnd.ms-works,
|
1223
|
-
wdp,image/vnd.ms-photo,
|
1224
|
-
webarchive,application/x-safari-webarchive,
|
1225
|
-
webtest,application/xml,
|
1226
|
-
wiq,application/xml,
|
1227
|
-
wiz,application/msword,
|
1228
|
-
wks,application/vnd.ms-works,
|
1229
|
-
WLMP,application/wlmoviemaker,
|
1230
|
-
wlpginstall,application/x-wlpg-detect,
|
1231
|
-
wlpginstall3,application/x-wlpg3-detect,
|
1232
|
-
wm,video/x-ms-wm,
|
1233
|
-
wma,audio/x-ms-wma,
|
1234
|
-
wmd,application/x-ms-wmd,
|
1235
|
-
wmf,application/x-msmetafile,
|
1236
|
-
wml,text/vnd.wap.wml,
|
1237
|
-
wmlc,application/vnd.wap.wmlc,
|
1238
|
-
wmls,text/vnd.wap.wmlscript,
|
1239
|
-
wmlsc,application/vnd.wap.wmlscriptc,
|
1240
|
-
wmp,video/x-ms-wmp,
|
1241
|
-
wmv,video/x-ms-wmv,
|
1242
|
-
wmx,video/x-ms-wmx,
|
1243
|
-
wmz,application/x-ms-wmz,
|
1244
|
-
wpl,application/vnd.ms-wpl,
|
1245
|
-
wps,application/vnd.ms-works,
|
1246
|
-
wri,application/x-mswrite,
|
1247
|
-
wrl,x-world/x-vrml,
|
1248
|
-
wrz,x-world/x-vrml,
|
1249
|
-
wsc,text/scriptlet,
|
1250
|
-
wsdl,text/xml,
|
1251
|
-
wvx,video/x-ms-wvx,
|
1252
|
-
x,application/directx,
|
1253
|
-
xaf,x-world/x-vrml,
|
1254
|
-
xaml,application/xaml+xml,
|
1255
|
-
xap,application/x-silverlight-app,
|
1256
|
-
xbap,application/x-ms-xbap,
|
1257
|
-
xbm,image/x-xbitmap,
|
1258
|
-
xdr,text/plain,
|
1259
|
-
xht,application/xhtml+xml,
|
1260
|
-
xhtml,application/xhtml+xml,
|
1261
|
-
xla,application/vnd.ms-excel,
|
1262
|
-
xlam,application/vnd.ms-excel.addin.macroEnabled.12,
|
1263
|
-
xlc,application/vnd.ms-excel,
|
1264
|
-
xld,application/vnd.ms-excel,
|
1265
|
-
xlk,application/vnd.ms-excel,
|
1266
|
-
xll,application/vnd.ms-excel,
|
1267
|
-
xlm,application/vnd.ms-excel,
|
1268
|
-
xls,application/vnd.ms-excel,
|
1269
|
-
xlsb,application/vnd.ms-excel.sheet.binary.macroEnabled.12,
|
1270
|
-
xlsm,application/vnd.ms-excel.sheet.macroEnabled.12,
|
1271
|
-
xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,
|
1272
|
-
xlt,application/vnd.ms-excel,
|
1273
|
-
xltm,application/vnd.ms-excel.template.macroEnabled.12,
|
1274
|
-
xltx,application/vnd.openxmlformats-officedocument.spreadsheetml.template,
|
1275
|
-
xlw,application/vnd.ms-excel,
|
1276
|
-
xml,text/xml,
|
1277
|
-
xmta,application/xml,
|
1278
|
-
xof,x-world/x-vrml,
|
1279
|
-
XOML,text/plain,
|
1280
|
-
xpm,image/x-xpixmap,
|
1281
|
-
xps,application/vnd.ms-xpsdocument,
|
1282
|
-
xrm-ms,text/xml,
|
1283
|
-
xsc,application/xml,
|
1284
|
-
xsd,text/xml,
|
1285
|
-
xsf,text/xml,
|
1286
|
-
xsl,text/xml,
|
1287
|
-
xslt,text/xml,
|
1288
|
-
xsn,application/octet-stream,
|
1289
|
-
xss,application/xml,
|
1290
|
-
xtp,application/octet-stream,
|
1291
|
-
xwd,image/x-xwindowdump,
|
1292
|
-
z,application/x-compress,
|
1293
|
-
zip,application/x-zip-compressed )
|
1294
|
-
content_types.each do |type_info|
|
1295
|
-
if ext == ".#{type_info.split(',').first}"
|
1296
|
-
content_type = type_info.split(',')[1]
|
1297
|
-
return content_type
|
946
|
+
#
|
947
|
+
# Detect WAF and SSRF protection libraries based on common strings in the response body
|
948
|
+
#
|
949
|
+
# @param [String] response HTTP response
|
950
|
+
#
|
951
|
+
# @return [Boolean] true if WAF detected
|
952
|
+
#
|
953
|
+
def detect_waf(response)
|
954
|
+
detected = false
|
955
|
+
# SafeCurl (safe_curl) InvalidURLException
|
956
|
+
if response =~ /fin1te\\SafeCurl\\Exception\\InvalidURLException/
|
957
|
+
logger.info('SafeCurl protection mechanism appears to be in use')
|
958
|
+
detected = true
|
1298
959
|
end
|
960
|
+
detected
|
1299
961
|
end
|
1300
|
-
nil
|
1301
|
-
end
|
1302
962
|
|
1303
|
-
|
963
|
+
#
|
964
|
+
# Guess content type based on file extension
|
965
|
+
#
|
966
|
+
# @param [String] ext File extension [with dots] (Example: '.png')
|
967
|
+
#
|
968
|
+
# @return [String] content-type value
|
969
|
+
#
|
970
|
+
def guess_mime(ext)
|
971
|
+
content_types = WEBrick::HTTPUtils::DefaultMimeTypes
|
972
|
+
common_content_types = {
|
973
|
+
'ico' => 'image/x-icon' }
|
974
|
+
content_types.merge!(common_content_types)
|
975
|
+
content_types.each do |k, v|
|
976
|
+
return v.to_s if ext == ".#{k}"
|
977
|
+
end
|
978
|
+
nil
|
979
|
+
end
|
1304
980
|
|
1305
|
-
|
981
|
+
# private methods
|
982
|
+
private :parse_options,
|
983
|
+
:send_http_request,
|
984
|
+
:run_rules,
|
985
|
+
:encode_ip,
|
986
|
+
:guess_mime,
|
987
|
+
:guess_status,
|
988
|
+
:detect_waf
|
989
|
+
end
|
1306
990
|
end
|