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/server.rb
CHANGED
@@ -1,147 +1,327 @@
|
|
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
|
-
|
9
|
+
#
|
10
|
+
# SSRFProxy::Server takes a SSRFProxy::HTTP object, interface
|
11
|
+
# and port, and starts a HTTP proxy server on the specified
|
12
|
+
# interface and port. All client HTTP requests are sent via
|
13
|
+
# the specified SSRFProxy::HTTP object.
|
14
|
+
#
|
15
|
+
class Server
|
16
|
+
include Celluloid::IO
|
17
|
+
finalizer :shutdown
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
#
|
20
|
+
# SSRFProxy::Server errors
|
21
|
+
#
|
22
|
+
module Error
|
23
|
+
# SSRFProxy::Server custom errors
|
24
|
+
class Error < StandardError; end
|
25
|
+
exceptions = %w(
|
26
|
+
InvalidSsrf
|
27
|
+
ProxyRecursion
|
28
|
+
AddressInUse
|
29
|
+
RemoteProxyUnresponsive
|
30
|
+
RemoteHostUnresponsive )
|
31
|
+
exceptions.each { |e| const_set(e, Class.new(Error)) }
|
32
|
+
end
|
24
33
|
|
25
|
-
|
26
|
-
|
27
|
-
|
34
|
+
#
|
35
|
+
# Start the local server and listen for connections
|
36
|
+
#
|
37
|
+
# @param [SSRFProxy::HTTP] ssrf A configured SSRFProxy::HTTP object
|
38
|
+
# @param [String] interface Listen interface (Default: 127.0.0.1)
|
39
|
+
# @param [Integer] port Listen port (Default: 8081)
|
40
|
+
#
|
41
|
+
# @raise [SSRFProxy::Server::Error::InvalidSsrf]
|
42
|
+
# Invalid SSRFProxy::SSRF object provided.
|
43
|
+
# @raise [SSRFProxy::Server::Error::ProxyRecursion]
|
44
|
+
# Proxy recursion error. SSRF Proxy cannot use itself as an
|
45
|
+
# upstream proxy.
|
46
|
+
# @raise [SSRFProxy::Server::Error::RemoteProxyUnresponsive]
|
47
|
+
# Could not connect to remote proxy.
|
48
|
+
# @raise [SSRFProxy::Server::Error::AddressInUse]
|
49
|
+
# Could not bind to the port on the specified interface as
|
50
|
+
# address already in use.
|
51
|
+
#
|
52
|
+
# @example Start SSRF Proxy server with the default options
|
53
|
+
# ssrf_proxy = SSRFProxy::Server.new(
|
54
|
+
# SSRFProxy::HTTP.new('http://example.local/index.php?url=xxURLxx'),
|
55
|
+
# '127.0.0.1',
|
56
|
+
# 8081)
|
57
|
+
# ssrf_proxy.serve
|
58
|
+
#
|
59
|
+
def initialize(ssrf, interface = '127.0.0.1', port = 8081)
|
60
|
+
@banner = 'SSRF Proxy'
|
61
|
+
@server = nil
|
62
|
+
@max_request_len = 8192
|
63
|
+
@logger = ::Logger.new(STDOUT).tap do |log|
|
64
|
+
log.progname = 'ssrf-proxy-server'
|
65
|
+
log.level = ::Logger::WARN
|
66
|
+
log.datetime_format = '%Y-%m-%d %H:%M:%S '
|
67
|
+
end
|
68
|
+
# set ssrf
|
69
|
+
unless ssrf.class == SSRFProxy::HTTP
|
70
|
+
raise SSRFProxy::Server::Error::InvalidSsrf.new,
|
71
|
+
'Invalid SSRF provided'
|
72
|
+
end
|
73
|
+
@ssrf = ssrf
|
28
74
|
|
29
|
-
|
30
|
-
|
75
|
+
# check if the remote proxy server is responsive
|
76
|
+
unless @ssrf.proxy.nil?
|
77
|
+
if @ssrf.proxy.host == interface && @ssrf.proxy.port == port
|
78
|
+
raise SSRFProxy::Server::Error::ProxyRecursion.new,
|
79
|
+
"Proxy recursion error: #{@ssrf.proxy}"
|
80
|
+
end
|
81
|
+
if port_open?(@ssrf.proxy.host, @ssrf.proxy.port)
|
82
|
+
print_good("Connected to remote proxy #{@ssrf.proxy.host}:#{@ssrf.proxy.port} successfully")
|
83
|
+
else
|
84
|
+
raise SSRFProxy::Server::Error::RemoteProxyUnresponsive.new,
|
85
|
+
"Could not connect to remote proxy #{@ssrf.proxy.host}:#{@ssrf.proxy.port}"
|
86
|
+
end
|
87
|
+
end
|
31
88
|
|
32
|
-
|
89
|
+
# if no upstream proxy is set, check if the remote server is responsive
|
90
|
+
if @ssrf.proxy.nil?
|
91
|
+
if port_open?(@ssrf.host, @ssrf.port)
|
92
|
+
print_good("Connected to remote host #{@ssrf.host}:#{@ssrf.port} successfully")
|
93
|
+
else
|
94
|
+
raise SSRFProxy::Server::Error::RemoteHostUnresponsive.new,
|
95
|
+
"Could not connect to remote host #{@ssrf.host}:#{@ssrf.port}"
|
96
|
+
end
|
97
|
+
end
|
33
98
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
99
|
+
# start server
|
100
|
+
logger.info "Starting HTTP proxy on #{interface}:#{port}"
|
101
|
+
begin
|
102
|
+
print_status "Listening on #{interface}:#{port}"
|
103
|
+
@server = TCPServer.new(interface, port.to_i)
|
104
|
+
rescue Errno::EADDRINUSE
|
105
|
+
raise SSRFProxy::Server::Error::AddressInUse.new,
|
106
|
+
"Could not bind to #{interface}:#{port} - address already in use"
|
107
|
+
end
|
40
108
|
end
|
41
|
-
end
|
42
109
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
110
|
+
#
|
111
|
+
# Checks if a port is open or not on a remote host
|
112
|
+
# From: https://gist.github.com/ashrithr/5305786
|
113
|
+
#
|
114
|
+
# @param [String] ip connect to IP
|
115
|
+
# @param [Integer] port connect to port
|
116
|
+
# @param [Integer] seconds connection timeout
|
117
|
+
#
|
118
|
+
def port_open?(ip, port, seconds = 10)
|
119
|
+
Timeout.timeout(seconds) do
|
120
|
+
begin
|
121
|
+
TCPSocket.new(ip, port).close
|
122
|
+
true
|
123
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
rescue Timeout::Error
|
128
|
+
false
|
129
|
+
end
|
52
130
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
def initialize(interface='127.0.0.1', port=8081, ssrf)
|
62
|
-
@logger = ::Logger.new(STDOUT).tap do |log|
|
63
|
-
log.progname = 'ssrf-proxy-server'
|
64
|
-
log.level = ::Logger::WARN
|
65
|
-
log.datetime_format = '%Y-%m-%d %H:%M:%S '
|
131
|
+
#
|
132
|
+
# Print status message
|
133
|
+
#
|
134
|
+
# @param [String] msg message to print
|
135
|
+
#
|
136
|
+
def print_status(msg = '')
|
137
|
+
puts '[*] '.blue + msg
|
66
138
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
139
|
+
|
140
|
+
#
|
141
|
+
# Print progress messages
|
142
|
+
#
|
143
|
+
# @param [String] msg message to print
|
144
|
+
#
|
145
|
+
def print_good(msg = '')
|
146
|
+
puts '[+] '.green + msg
|
71
147
|
end
|
72
|
-
@ssrf = ssrf
|
73
|
-
# start server
|
74
|
-
logger.info "Starting HTTP proxy on #{interface}:#{port}"
|
75
|
-
print_status "Listening on #{interface}:#{port}"
|
76
|
-
@server = TCPServer.new(interface, port.to_i)
|
77
|
-
end
|
78
148
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
149
|
+
#
|
150
|
+
# Print error message
|
151
|
+
#
|
152
|
+
# @param [String] msg message to print
|
153
|
+
#
|
154
|
+
def print_error(msg = '')
|
155
|
+
puts '[-] '.red + msg
|
156
|
+
end
|
85
157
|
|
86
|
-
|
158
|
+
#
|
159
|
+
# Logger accessor
|
160
|
+
#
|
161
|
+
# @return [Logger] class logger object
|
162
|
+
#
|
163
|
+
def logger
|
164
|
+
@logger
|
165
|
+
end
|
87
166
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
logger.debug 'Shutdown complete'
|
95
|
-
end
|
167
|
+
#
|
168
|
+
# Run proxy server asynchronously
|
169
|
+
#
|
170
|
+
def serve
|
171
|
+
loop { async.handle_connection(@server.accept) }
|
172
|
+
end
|
96
173
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
_, port, host = socket.peeraddr
|
105
|
-
max_len = 4096
|
106
|
-
logger.debug "Client #{host}:#{port} connected"
|
107
|
-
request = socket.readpartial(max_len)
|
108
|
-
logger.debug("Received client request (#{request.length} bytes):\n#{request}")
|
109
|
-
if request.length >= max_len
|
110
|
-
logger.warn("Client request too long (truncated at #{request.length} bytes)")
|
174
|
+
#
|
175
|
+
# Handle shutdown of client socket
|
176
|
+
#
|
177
|
+
def shutdown
|
178
|
+
logger.info 'Shutting down'
|
179
|
+
@server.close if @server
|
180
|
+
logger.debug 'Shutdown complete'
|
111
181
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
182
|
+
|
183
|
+
#
|
184
|
+
# Handle client socket connection
|
185
|
+
#
|
186
|
+
# @param [Celluloid::IO::TCPSocket] socket client socket
|
187
|
+
#
|
188
|
+
def handle_connection(socket)
|
189
|
+
start_time = Time.now
|
190
|
+
_, port, host = socket.peeraddr
|
191
|
+
logger.debug("Client #{host}:#{port} connected")
|
192
|
+
request = socket.readpartial(@max_request_len)
|
193
|
+
logger.debug("Received client request (#{request.length} bytes):\n#{request}")
|
194
|
+
|
195
|
+
response = nil
|
196
|
+
if request.to_s =~ /\ACONNECT ([_a-zA-Z0-9\.\-]+:[\d]+) .*$/
|
197
|
+
host = $1.to_s
|
198
|
+
logger.info("Negotiating connection to #{host}")
|
199
|
+
response = send_request("GET http://#{host}/ HTTP/1.0\n\n")
|
200
|
+
|
201
|
+
if response['code'].to_i == 502 || response['code'].to_i == 504
|
202
|
+
logger.info("Connection to #{host} failed")
|
203
|
+
socket.write("#{response['status_line']}\n#{response['headers']}\n#{response['body']}")
|
204
|
+
raise Errno::ECONNRESET
|
205
|
+
end
|
206
|
+
|
123
207
|
logger.info("Connected to #{host} successfully")
|
124
208
|
socket.write("HTTP/1.0 200 Connection established\r\n\r\n")
|
125
|
-
request = socket.readpartial(
|
209
|
+
request = socket.readpartial(@max_request_len)
|
126
210
|
logger.debug("Received client request (#{request.length} bytes):\n#{request}")
|
127
|
-
if request.length >= max_len
|
128
|
-
logger.warn("Client request too long (truncated at #{request.length} bytes)")
|
129
|
-
end
|
130
|
-
response = @ssrf.send_request(request)
|
131
211
|
end
|
132
|
-
|
133
|
-
response =
|
212
|
+
|
213
|
+
response = send_request(request.to_s)
|
214
|
+
socket.write("#{response['status_line']}\n#{response['headers']}\n#{response['body']}")
|
215
|
+
raise Errno::ECONNRESET
|
216
|
+
rescue EOFError, Errno::ECONNRESET
|
217
|
+
socket.close
|
218
|
+
logger.debug("Client #{host}:#{port} disconnected")
|
219
|
+
end_time = Time.now
|
220
|
+
duration = end_time - start_time
|
221
|
+
logger.info("Served #{response['body'].length} bytes in #{(duration * 1000).round(3)} ms")
|
134
222
|
end
|
135
|
-
socket.write(response)
|
136
|
-
socket.close
|
137
|
-
rescue EOFError, Errno::ECONNRESET
|
138
|
-
logger.debug "Client #{host}:#{port} disconnected"
|
139
|
-
socket.close
|
140
|
-
end
|
141
223
|
|
142
|
-
|
224
|
+
#
|
225
|
+
# Send client HTTP request
|
226
|
+
#
|
227
|
+
# @param [String] client HTTP request
|
228
|
+
#
|
229
|
+
# @return [Hash] HTTP response
|
230
|
+
#
|
231
|
+
def send_request(request)
|
232
|
+
response_error = {
|
233
|
+
'uri' => '',
|
234
|
+
'duration' => '0',
|
235
|
+
'http_version' => '1.0',
|
236
|
+
'headers' => "Server: #{@banner}\n",
|
237
|
+
'body' => '' }
|
143
238
|
|
144
|
-
|
239
|
+
# parse client request
|
240
|
+
begin
|
241
|
+
if request.to_s !~ %r{\A(CONNECT|GET|HEAD|DELETE|POST|PUT) https?://}
|
242
|
+
if request.to_s !~ /^Host: ([^\s]+)\r?\n/
|
243
|
+
logger.warn('No host specified')
|
244
|
+
raise SSRFProxy::HTTP::Error::InvalidClientRequest,
|
245
|
+
'No host specified'
|
246
|
+
end
|
247
|
+
end
|
248
|
+
req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
|
249
|
+
req.parse(StringIO.new(request))
|
250
|
+
rescue => e
|
251
|
+
logger.info('Received malformed client HTTP request.')
|
252
|
+
error_msg = "Error -- Invalid request: Received malformed client HTTP request: #{e.message}"
|
253
|
+
print_error(error_msg)
|
254
|
+
response_error['code'] = '502'
|
255
|
+
response_error['message'] = 'Bad Gateway'
|
256
|
+
response_error['status_line'] = "HTTP/#{response_error['http_version']}"
|
257
|
+
response_error['status_line'] << " #{response_error['code']}"
|
258
|
+
response_error['status_line'] << " #{response_error['message']}"
|
259
|
+
return response_error
|
260
|
+
end
|
261
|
+
uri = req.request_uri
|
145
262
|
|
146
|
-
|
263
|
+
# send request
|
264
|
+
response = nil
|
265
|
+
logger.info("Sending request: #{uri}")
|
266
|
+
status_msg = "Request -> #{req.request_method}"
|
267
|
+
status_msg << " -> PROXY[#{@ssrf.proxy.host}:#{@ssrf.proxy.port}]" unless @ssrf.proxy.nil?
|
268
|
+
status_msg << " -> SSRF[#{@ssrf.host}:#{@ssrf.port}] -> URI[#{uri}]"
|
269
|
+
print_status(status_msg)
|
270
|
+
|
271
|
+
begin
|
272
|
+
response = @ssrf.send_request(request.to_s)
|
273
|
+
rescue SSRFProxy::HTTP::Error::InvalidClientRequest => e
|
274
|
+
logger.info(e.message)
|
275
|
+
error_msg = "Error -- Invalid request: #{e.message}"
|
276
|
+
print_error(error_msg)
|
277
|
+
response_error['code'] = '502'
|
278
|
+
response_error['message'] = 'Bad Gateway'
|
279
|
+
response_error['status_line'] = "HTTP/#{response_error['http_version']}"
|
280
|
+
response_error['status_line'] << " #{response_error['code']}"
|
281
|
+
response_error['status_line'] << " #{response_error['message']}"
|
282
|
+
return response_error
|
283
|
+
rescue SSRFProxy::HTTP::Error::ConnectionTimeout => e
|
284
|
+
logger.info(e.message)
|
285
|
+
error_msg = 'Response <- 504'
|
286
|
+
error_msg << " <- PROXY[#{@ssrf.proxy.host}:#{@ssrf.proxy.port}]" unless @ssrf.proxy.nil?
|
287
|
+
error_msg << " <- SSRF[#{@ssrf.host}:#{@ssrf.port}] <- URI[#{uri}]"
|
288
|
+
error_msg << " -- Error: #{e.message}"
|
289
|
+
print_error(error_msg)
|
290
|
+
response_error['code'] = '504'
|
291
|
+
response_error['message'] = 'Timeout'
|
292
|
+
response_error['status_line'] = "HTTP/#{response_error['http_version']}"
|
293
|
+
response_error['status_line'] << " #{response_error['code']}"
|
294
|
+
response_error['status_line'] << " #{response_error['message']}"
|
295
|
+
return response_error
|
296
|
+
rescue => e
|
297
|
+
logger.warn(e.message)
|
298
|
+
error_msg = "Error -- Unexpected error: #{e.message}"
|
299
|
+
print_error(error_msg)
|
300
|
+
response_error['code'] = '502'
|
301
|
+
response_error['message'] = 'Bad Gateway'
|
302
|
+
response_error['status_line'] = "HTTP/#{response_error['http_version']}"
|
303
|
+
response_error['status_line'] << " #{response_error['code']} "
|
304
|
+
response_error['status_line'] << " #{response_error['message']}"
|
305
|
+
return response_error
|
306
|
+
end
|
147
307
|
|
308
|
+
# return response
|
309
|
+
status_msg = "Response <- #{response['code']}"
|
310
|
+
status_msg << " <- PROXY[#{@ssrf.proxy.host}:#{@ssrf.proxy.port}]" unless @ssrf.proxy.nil?
|
311
|
+
status_msg << " <- SSRF[#{@ssrf.host}:#{@ssrf.port}] <- URI[#{uri}]"
|
312
|
+
status_msg << " -- Title[#{response['title']}]" unless response['title'].eql?('')
|
313
|
+
status_msg << " -- [#{response['body'].size} bytes]"
|
314
|
+
print_good(status_msg)
|
315
|
+
response
|
316
|
+
end
|
317
|
+
|
318
|
+
# private methods
|
319
|
+
private :print_status,
|
320
|
+
:print_good,
|
321
|
+
:print_error,
|
322
|
+
:shutdown,
|
323
|
+
:handle_connection,
|
324
|
+
:send_request,
|
325
|
+
:port_open?
|
326
|
+
end
|
327
|
+
end
|
data/lib/ssrf_proxy/version.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
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
8
|
module SSRFProxy
|
9
|
-
|
9
|
+
# Gem version
|
10
|
+
VERSION = '0.0.3'.freeze
|
11
|
+
# Elite font ASCII art from: http://patorjk.com/software/taag/
|
12
|
+
BANNER = " \n" \
|
13
|
+
" .▄▄ · .▄▄ · ▄▄▄ ·▄▄▄ ▄▄▄·▄▄▄ ▐▄• ▄ ▄· ▄▌ \n" \
|
14
|
+
" ▐█ ▀. ▐█ ▀. ▀▄ █·▐▄▄· ▐█ ▄█▀▄ █·▪ █▌█▌▪▐█▪██▌ \n" \
|
15
|
+
" ▄▀▀▀█▄▄▀▀▀█▄▐▀▀▄ ██▪ ██▀·▐▀▀▄ ▄█▀▄ ·██· ▐█▌▐█▪ \n" \
|
16
|
+
" ▐█▄▪▐█▐█▄▪▐█▐█•█▌██▌. ▐█▪·•▐█•█▌▐█▌.▐▌▪▐█·█▌ ▐█▀·. \n" \
|
17
|
+
" ▀▀▀▀ ▀▀▀▀ .▀ ▀▀▀▀ .▀ .▀ ▀ ▀█▄▀▪•▀▀ ▀▀ ▀ • \n".freeze
|
10
18
|
end
|
data/lib/ssrf_proxy.rb
CHANGED
@@ -1,18 +1,45 @@
|
|
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
|
-
|
9
|
-
require
|
10
|
-
require
|
8
|
+
# ouput
|
9
|
+
require 'logger'
|
10
|
+
require 'colorize'
|
11
|
+
String.disable_colorization = false
|
11
12
|
|
12
|
-
|
13
|
+
# proxy server
|
14
|
+
require 'socket'
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
+
# threading
|
17
|
+
require 'celluloid/current'
|
18
|
+
require 'celluloid/io'
|
16
19
|
|
17
|
-
|
20
|
+
# command line option parsing
|
21
|
+
require 'getoptlong'
|
18
22
|
|
23
|
+
# http requests
|
24
|
+
require 'net/http'
|
25
|
+
require 'socksify/http'
|
26
|
+
|
27
|
+
# http parsing
|
28
|
+
require 'uri'
|
29
|
+
require 'cgi'
|
30
|
+
require 'webrick'
|
31
|
+
require 'stringio'
|
32
|
+
require 'base64'
|
33
|
+
require 'htmlentities'
|
34
|
+
|
35
|
+
# client request url rules
|
36
|
+
require 'digest'
|
37
|
+
require 'base32'
|
38
|
+
|
39
|
+
# ip encoding
|
40
|
+
require 'ipaddress'
|
41
|
+
|
42
|
+
# SSRF Proxy gem libs
|
43
|
+
require 'ssrf_proxy/version'
|
44
|
+
require 'ssrf_proxy/http'
|
45
|
+
require 'ssrf_proxy/server'
|