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/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
|
-
|