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/bin/ssrf-proxy
CHANGED
@@ -1,40 +1,30 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
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 'getoptlong'
|
9
8
|
require 'ssrf_proxy'
|
10
9
|
|
11
10
|
#
|
12
11
|
# @note output banner
|
13
12
|
#
|
14
13
|
def banner
|
15
|
-
puts "
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|___|___|_| |_| | _|_| |___|_,_|_ |
|
21
|
-
|_| |___| ".blue
|
22
|
-
puts "
|
23
|
-
SSRF Proxy v0.0.2
|
24
|
-
https://github.com/bcoles/ssrf_proxy"
|
25
|
-
puts "
|
26
|
-
_______________________________________________
|
27
|
-
".blue
|
14
|
+
puts "\n_________________________________________________________".blue
|
15
|
+
puts SSRFProxy::BANNER.blue
|
16
|
+
puts "\n SSRF Proxy v#{SSRFProxy::VERSION}"
|
17
|
+
puts " https://github.com/bcoles/ssrf_proxy\n"
|
18
|
+
puts "\n_________________________________________________________\n".blue
|
28
19
|
end
|
29
20
|
|
30
21
|
#
|
31
22
|
# @note output usage
|
32
23
|
#
|
33
24
|
def usage
|
34
|
-
|
35
|
-
puts
|
36
|
-
puts
|
37
|
-
puts "Options:"
|
25
|
+
puts 'Usage: ssrf-proxy [options] -u <SSRF URL>'
|
26
|
+
puts 'Example: ssrf-proxy -u http://target/?url=xxURLxx'
|
27
|
+
puts 'Options:'
|
38
28
|
puts "
|
39
29
|
-h, --help Help
|
40
30
|
-v, --verbose Verbose output
|
@@ -43,38 +33,48 @@ def usage
|
|
43
33
|
Server options:
|
44
34
|
-p, --port=PORT Listen port (Default: 8081)
|
45
35
|
--interface=IP Listen interface (Default: 127.0.0.1)
|
46
|
-
--proxy=PROXY Upstream HTTP proxy
|
47
36
|
|
48
37
|
SSRF request options:
|
49
38
|
-u, --url=URL SSRF URL with 'xxURLxx' placeholder
|
50
|
-
--method=METHOD HTTP method (GET/POST/
|
39
|
+
--method=METHOD HTTP method (GET/HEAD/DELETE/POST/PUT)
|
40
|
+
(Default: GET)
|
51
41
|
--post-data=DATA HTTP post data
|
52
42
|
--cookie=COOKIE HTTP cookies (separated by ';')
|
53
43
|
--user-agent=AGENT HTTP user-agent (Default: Mozilla/5.0)
|
54
|
-
--timeout=SECONDS Connection timeout in seconds (Default: 10)
|
55
|
-
--ip-encoding=MODE Encode IP address for blacklist evasion.
|
56
|
-
(Modes: int, ipv6, oct, hex) (Default: none)
|
57
44
|
--rules=RULES Rules for parsing client request for xxURLxx
|
58
45
|
(separated by ',') (Default: none)
|
59
46
|
|
47
|
+
SSRF connection options:
|
48
|
+
--proxy=PROXY Use a proxy to connect to the server.
|
49
|
+
(Supported proxies: http, https, socks)
|
50
|
+
--insecure Skip server SSL certificate validation.
|
51
|
+
--timeout=SECONDS Connection timeout in seconds (Default: 10)
|
52
|
+
|
60
53
|
HTTP response modification:
|
61
54
|
--match=REGEX Regex to match response body content.
|
62
|
-
(Default: \\A(
|
55
|
+
(Default: \\A(.*)\\z)
|
63
56
|
--strip=HEADERS Headers to remove from the response.
|
64
57
|
(separated by ',') (Default: none)
|
58
|
+
--decode-html Decode HTML entities in response body.
|
65
59
|
--guess-status Replaces response status code and message
|
66
60
|
headers (determined by common strings in the
|
67
61
|
response body, such as 404 Not Found.)
|
68
62
|
--guess-mime Replaces response content-type header with the
|
69
63
|
appropriate mime type (determined by the file
|
70
64
|
extension of the requested resource.)
|
65
|
+
--ask-password Prompt for password on authentication failure.
|
66
|
+
Adds a 'WWW-Authenticate' HTTP header to the
|
67
|
+
response if the response code is 401.
|
71
68
|
|
72
69
|
Client request modification:
|
73
70
|
--forward-cookies Forward client HTTP cookies through proxy to
|
74
71
|
SSRF server.
|
75
|
-
--
|
76
|
-
--
|
77
|
-
|
72
|
+
--cookies-to-uri Add client request cookies to URI query.
|
73
|
+
--body-to-uri Add client request body to URI query.
|
74
|
+
--auth-to-uri Use client request basic authentication
|
75
|
+
credentials in request URI.
|
76
|
+
--ip-encoding=MODE Encode client request host IP address.
|
77
|
+
(Modes: int, ipv6, oct, hex, dotted_hex)
|
78
78
|
|
79
79
|
"
|
80
80
|
exit 1
|
@@ -84,144 +84,161 @@ end
|
|
84
84
|
# @note parse options and start server
|
85
85
|
#
|
86
86
|
def start_server
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
#
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
87
|
+
# get args
|
88
|
+
opts = GetoptLong.new(
|
89
|
+
['-h', '--help', GetoptLong::NO_ARGUMENT],
|
90
|
+
['-v', '--verbose', GetoptLong::NO_ARGUMENT],
|
91
|
+
['-d', '--debug', GetoptLong::NO_ARGUMENT],
|
92
|
+
|
93
|
+
['-p', '--port', GetoptLong::REQUIRED_ARGUMENT],
|
94
|
+
['--interface', GetoptLong::REQUIRED_ARGUMENT],
|
95
|
+
['--proxy', GetoptLong::REQUIRED_ARGUMENT],
|
96
|
+
|
97
|
+
['-u', '--url', GetoptLong::REQUIRED_ARGUMENT],
|
98
|
+
['--method', GetoptLong::REQUIRED_ARGUMENT],
|
99
|
+
['--post-data', GetoptLong::REQUIRED_ARGUMENT],
|
100
|
+
['--cookie', GetoptLong::REQUIRED_ARGUMENT],
|
101
|
+
['--user-agent', GetoptLong::REQUIRED_ARGUMENT],
|
102
|
+
['--insecure', GetoptLong::NO_ARGUMENT],
|
103
|
+
['--timeout', GetoptLong::REQUIRED_ARGUMENT],
|
104
|
+
|
105
|
+
['--ip-encoding', GetoptLong::REQUIRED_ARGUMENT],
|
106
|
+
['--rules', GetoptLong::REQUIRED_ARGUMENT],
|
107
|
+
['--forward-cookies', GetoptLong::NO_ARGUMENT],
|
108
|
+
['--body-to-uri', GetoptLong::NO_ARGUMENT],
|
109
|
+
['--auth-to-uri', GetoptLong::NO_ARGUMENT],
|
110
|
+
['--cookies-to-uri', GetoptLong::NO_ARGUMENT],
|
111
|
+
|
112
|
+
['--match', GetoptLong::REQUIRED_ARGUMENT],
|
113
|
+
['--strip', GetoptLong::REQUIRED_ARGUMENT],
|
114
|
+
['--decode-html', GetoptLong::NO_ARGUMENT],
|
115
|
+
['--guess-status', GetoptLong::NO_ARGUMENT],
|
116
|
+
['--guess-mime', GetoptLong::NO_ARGUMENT],
|
117
|
+
['--ask-password', GetoptLong::NO_ARGUMENT]
|
118
|
+
)
|
119
|
+
|
120
|
+
# local proxy server defaults
|
121
|
+
interface = '127.0.0.1'
|
122
|
+
port = 8081
|
123
|
+
|
124
|
+
# ssrf defaults
|
125
|
+
url = nil
|
126
|
+
rules = ''
|
127
|
+
ip_encoding = ''
|
128
|
+
method = 'GET'
|
129
|
+
post_data = ''
|
130
|
+
match = '\\A(.*)\\z'
|
131
|
+
strip = ''
|
132
|
+
decode_html = false
|
133
|
+
guess_status = false
|
134
|
+
guess_mime = false
|
135
|
+
ask_password = false
|
136
|
+
forward_cookies = false
|
137
|
+
body_to_uri = false
|
138
|
+
auth_to_uri = false
|
139
|
+
cookies_to_uri = false
|
140
|
+
|
141
|
+
# http connection defaults
|
142
|
+
cookie = ''
|
143
|
+
timeout = 10
|
144
|
+
upstream_proxy = nil
|
145
|
+
user_agent = 'Mozilla/5.0'
|
146
|
+
insecure = false
|
147
|
+
|
148
|
+
# logging
|
149
|
+
log_level = ::Logger::WARN
|
150
|
+
|
151
|
+
# handle args
|
152
|
+
opts.each do |opt, arg|
|
153
|
+
case opt
|
154
|
+
when '-p', '--port'
|
155
|
+
port = arg
|
156
|
+
when '--interface'
|
157
|
+
interface = arg
|
158
|
+
when '-u', '--url'
|
159
|
+
url = arg
|
160
|
+
when '--ip-encoding'
|
161
|
+
ip_encoding = arg
|
162
|
+
when '--rules'
|
163
|
+
rules = arg
|
164
|
+
when '--proxy'
|
165
|
+
upstream_proxy = URI.parse(arg)
|
166
|
+
when '--cookie'
|
167
|
+
cookie = arg
|
168
|
+
when '--timeout'
|
169
|
+
timeout = arg.to_i
|
170
|
+
when '--user-agent'
|
171
|
+
user_agent = arg
|
172
|
+
when '--insecure'
|
173
|
+
insecure = true
|
174
|
+
when '--method'
|
175
|
+
method = arg
|
176
|
+
when '--post-data'
|
177
|
+
post_data = arg
|
178
|
+
when '--match'
|
179
|
+
match = arg
|
180
|
+
when '--strip'
|
181
|
+
strip = arg
|
182
|
+
when '--decode-html'
|
183
|
+
decode_html = true
|
184
|
+
when '--guess-status'
|
185
|
+
guess_status = true
|
186
|
+
when '--guess-mime'
|
187
|
+
guess_mime = true
|
188
|
+
when '--ask-password'
|
189
|
+
ask_password = true
|
190
|
+
when '--forward-cookies'
|
191
|
+
forward_cookies = true
|
192
|
+
when '--body-to-uri'
|
193
|
+
body_to_uri = true
|
194
|
+
when '--auth-to-uri'
|
195
|
+
auth_to_uri = true
|
196
|
+
when '--cookies-to-uri'
|
197
|
+
cookies_to_uri = true
|
198
|
+
when '-h', '--help'
|
199
|
+
usage
|
200
|
+
when '-v', '--verbose'
|
201
|
+
log_level = ::Logger::INFO unless log_level == ::Logger::DEBUG
|
202
|
+
when '-d', '--debug'
|
203
|
+
log_level = ::Logger::DEBUG
|
204
|
+
end
|
189
205
|
end
|
190
|
-
end
|
191
206
|
|
192
|
-
opts = {
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
207
|
+
opts = {
|
208
|
+
'proxy' => upstream_proxy.to_s,
|
209
|
+
'method' => method.to_s,
|
210
|
+
'post_data' => post_data.to_s,
|
211
|
+
'rules' => rules.to_s,
|
212
|
+
'ip_encoding' => ip_encoding.to_s,
|
213
|
+
'match' => match.to_s,
|
214
|
+
'strip' => strip.to_s,
|
215
|
+
'decode_html' => decode_html,
|
216
|
+
'guess_mime' => guess_mime,
|
217
|
+
'guess_status' => guess_status,
|
218
|
+
'ask_password' => ask_password,
|
219
|
+
'forward_cookies' => forward_cookies,
|
220
|
+
'body_to_uri' => body_to_uri,
|
221
|
+
'auth_to_uri' => auth_to_uri,
|
222
|
+
'cookies_to_uri' => cookies_to_uri,
|
223
|
+
'cookie' => cookie.to_s,
|
224
|
+
'timeout' => timeout.to_s,
|
225
|
+
'user_agent' => user_agent.to_s,
|
226
|
+
'insecure' => insecure.to_s
|
227
|
+
}
|
209
228
|
|
210
229
|
# setup ssrf
|
211
230
|
ssrf = SSRFProxy::HTTP.new(url, opts)
|
212
231
|
ssrf.logger.level = log_level
|
213
232
|
# start server
|
214
233
|
begin
|
215
|
-
ssrf_proxy = SSRFProxy::Server.new(interface, port
|
234
|
+
ssrf_proxy = SSRFProxy::Server.new(ssrf, interface, port)
|
216
235
|
ssrf_proxy.logger.level = log_level
|
217
236
|
ssrf_proxy.serve
|
218
237
|
rescue => e
|
219
238
|
puts "Error: #{e.message}"
|
220
239
|
end
|
221
|
-
|
222
240
|
end
|
223
241
|
|
224
242
|
banner
|
225
|
-
usage if ARGV.
|
243
|
+
usage if ARGV.empty?
|
226
244
|
start_server
|
227
|
-
|