ssrf_proxy 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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'
|