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.
data/bin/ssrf-scan DELETED
@@ -1,452 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # Copyright (c) 2015 Brendan Coles <bcoles@gmail.com>
4
- # SSRF Proxy - https://github.com/bcoles/ssrf_proxy
5
- # See the file 'LICENSE' for copying permission
6
- #
7
-
8
- require 'getoptlong'
9
- require 'ssrf_proxy'
10
-
11
- def banner
12
- puts "
13
- _______________________________________________
14
- ___
15
- ___ ___ ___| _| ___ ___ ___ _ _ _ _
16
- |_ -|_ -| _| _| | . | _| . |_'_| | |
17
- |___|___|_| |_| | _|_| |___|_,_|_ |
18
- |_| |___| ".blue
19
- puts "
20
- SSRF Proxy v0.0.2
21
- https://github.com/bcoles/ssrf_proxy"
22
- puts "
23
- _______________________________________________
24
- ".blue
25
- end
26
-
27
- def usage
28
-
29
- puts "Usage: ssrf-scan [options] -u <SSRF URL> --host <HOST>"
30
- puts "Example: ssrf-scan -u http://target/?url=xxURLxx"
31
- puts "Options:"
32
- puts "
33
- -h, --help Help
34
- -v, --verbose Verbose output
35
- -d, --debug Debugging output
36
-
37
- Scan options:
38
- --host=HOST IP address or hostname to scan
39
- (Default: 127.0.0.1)
40
- --port=PORT Port to scan (Default: 80)
41
-
42
- Connection options:
43
- --proxy=PROXY Upstream HTTP proxy
44
-
45
- SSRF options:
46
- -u, --url=URL SSRF URL with 'xxURLxx' placeholder
47
- --method=METHOD HTTP method (GET/POST/HEAD) (Default: GET)
48
- --post-data=DATA HTTP post data
49
- --cookie=COOKIE HTTP cookies (separated by ';')
50
- --user-agent=AGENT HTTP user-agent (Default: Mozilla/5.0)
51
- --timeout=SECONDS Connection timeout in seconds (Default: 10)
52
- --ip-encoding=MODE Encode IP address for blacklist evasion.
53
- (Modes: int, ipv6, oct, hex) (Default: none)
54
- --rules=RULES Rules for parsing client request for xxURLxx
55
- (separated by ',') (Default: none)
56
-
57
- Response modification:
58
- --match=REGEX Regex to match response body content.
59
- (Default: \\A(.+)\\z)
60
- --strip=HEADERS Headers to remove from the response.
61
- (separated by ',') (Default: none)
62
- --guess-status Replaces response status code and message
63
- headers (determined by common strings in the
64
- response body, such as 404 Not Found.)
65
- --guess-mime Replaces response content-type header with the
66
- appropriate mime type (determined by the file
67
- extension of the requested resource.)
68
-
69
- "
70
- exit 1
71
- end
72
-
73
- #
74
- # @note parse options and start scan
75
- #
76
- def start_scan
77
-
78
- # get args
79
- opts = GetoptLong.new(
80
- [ '-h', '--help', GetoptLong::NO_ARGUMENT ],
81
- [ '-v', '--verbose', GetoptLong::NO_ARGUMENT ],
82
- [ '-d', '--debug', GetoptLong::NO_ARGUMENT ],
83
-
84
- [ '--host', GetoptLong::REQUIRED_ARGUMENT ],
85
- [ '--port', GetoptLong::REQUIRED_ARGUMENT ],
86
-
87
- [ '--proxy', GetoptLong::REQUIRED_ARGUMENT ],
88
-
89
- [ '-u', '--url', GetoptLong::REQUIRED_ARGUMENT ],
90
- [ '--method', GetoptLong::REQUIRED_ARGUMENT ],
91
- [ '--post-data', GetoptLong::REQUIRED_ARGUMENT ],
92
- [ '--cookie', GetoptLong::REQUIRED_ARGUMENT ],
93
- [ '--user-agent', GetoptLong::REQUIRED_ARGUMENT ],
94
- [ '--timeout', GetoptLong::REQUIRED_ARGUMENT ],
95
-
96
- [ '--ip-encoding', GetoptLong::REQUIRED_ARGUMENT ],
97
- [ '--rules', GetoptLong::REQUIRED_ARGUMENT ],
98
- [ '--forward-cookies', GetoptLong::NO_ARGUMENT ],
99
- [ '--body-to-uri', GetoptLong::NO_ARGUMENT ],
100
- [ '--auth-to-uri', GetoptLong::NO_ARGUMENT ],
101
-
102
- [ '--match', GetoptLong::REQUIRED_ARGUMENT ],
103
- [ '--strip', GetoptLong::REQUIRED_ARGUMENT ],
104
- [ '--guess-status', GetoptLong::NO_ARGUMENT ],
105
- [ '--guess-mime', GetoptLong::NO_ARGUMENT ]
106
- )
107
-
108
- # scan option
109
- host = '127.0.0.1'
110
- port = 80
111
-
112
- # ssrf details
113
- url = nil
114
- rules = ''
115
- ip_encoding = ''
116
- method = 'GET'
117
- post_data = ''
118
- match = "\\A(.+)\\z"
119
- strip = ''
120
- guess_mime = false
121
- guess_status = false
122
- forward_cookies = false
123
- body_to_uri = false
124
- auth_to_uri = false
125
-
126
- # http connection defaults
127
- cookie = ''
128
- timeout = 10
129
- upstream_proxy = nil
130
- user_agent = 'Mozilla/5.0'
131
-
132
- # logging
133
- log_level = ::Logger::WARN
134
-
135
- # handle args
136
- opts.each do |opt, arg|
137
- case opt
138
- when '-u','--url'
139
- url = arg
140
- when '--host'
141
- host = arg
142
- when '--port'
143
- port = arg
144
- when '--rules'
145
- rules=arg
146
- when '--proxy'
147
- upstream_proxy = URI::parse(arg)
148
- when '--cookie'
149
- cookie=arg
150
- when '--timeout'
151
- timeout=arg
152
- when '--user-agent'
153
- user_agent=arg
154
- when '--method'
155
- method=arg
156
- when '--post-data'
157
- post_data=arg
158
- when '--match'
159
- match=arg
160
- when '--strip'
161
- strip=arg
162
- when '--guess-status'
163
- guess_status = true
164
- when '--guess-mime'
165
- guess_mime = true
166
- when '--forward-cookies'
167
- forward_cookies = true
168
- when '--body-to-uri'
169
- body_to_uri = true
170
- when '--auth-to-uri'
171
- auth_to_uri = true
172
- when '-h','--help'
173
- usage
174
- when '-v','--verbose'
175
- log_level = ::Logger::INFO unless log_level == ::Logger::DEBUG
176
- when '-d','--debug'
177
- log_level = ::Logger::DEBUG
178
- end
179
- end
180
-
181
- opts = {
182
- 'proxy' => "#{upstream_proxy}",
183
- 'method' => "#{method}",
184
- 'post_data' => "#{post_data}",
185
- 'rules' => "#{rules}",
186
- 'match' => "#{match}",
187
- 'strip' => "#{strip}",
188
- 'guess_status' => "#{guess_status}",
189
- 'guess_mime' => "#{guess_mime}",
190
- 'forward_cookies'=> "#{forward_cookies}",
191
- 'body_to_uri' => "#{body_to_uri}",
192
- 'auth_to_uri' => "#{auth_to_uri}",
193
- 'cookie' => "#{cookie}",
194
- 'timeout' => "#{timeout}",
195
- 'user_agent' => "#{user_agent}"
196
- }
197
-
198
- # setup ssrf
199
- ssrf = SSRFProxy::HTTP.new(url, opts)
200
- ssrf.logger.level = log_level
201
-
202
- # start scanner
203
- scan = SSRFProxyScanner.new(ssrf, opts, host)
204
- scan.logger.level = log_level
205
-
206
- end
207
-
208
- #
209
- # @note SSRFProxyScanner
210
- # - version: 0.0.2
211
- #
212
- class SSRFProxyScanner
213
-
214
- # @note output status messages
215
- def print_status(msg='')
216
- puts '[*] '.blue + msg
217
- end
218
- # @note output progress messages
219
- def print_good(msg='')
220
- puts '[+] '.green + msg
221
- end
222
-
223
- attr_accessor :logger
224
-
225
- # @note logging
226
- def logger
227
- @logger || ::Logger.new(STDOUT).tap do |log|
228
- log.progname = 'ssrf-proxy-scanner'
229
- log.level = ::Logger::WARN
230
- log.datetime_format = '%Y-%m-%d %H:%M:%S '
231
- end
232
- end
233
-
234
- # @note SSRFProxyScanner errors
235
- module Error
236
- # custom errors
237
- class Error < StandardError; end
238
- exceptions = %w( InvalidSsrf )
239
- exceptions.each { |e| const_set(e, Class.new(Error)) }
240
- end
241
-
242
- #
243
- # @note Start the scanner
244
- #
245
- # @options
246
- # - ssrf - SSRFProxy::HTTP - SSRF
247
- # - opts - Hash - SSRF and HTTP connection options
248
- # - host - String - target host to scan
249
- # - port - Integer - target port to scan
250
- #
251
- def initialize(ssrf, opts = {}, host='127.0.0.1', port=80)
252
- return unless ssrf.class == SSRFProxy::HTTP
253
-
254
- @report = []
255
- @ssrf = ssrf
256
-
257
- scan_host host, port
258
-
259
- # output report
260
- puts "#{'-'*60}\n"
261
- @report.sort.each do |r|
262
- print_good "#{r.join(' :: ')}\n"
263
- end
264
-
265
- end
266
-
267
- #
268
- # @note Report accessor
269
- #
270
- # @returns Array - findings
271
- #
272
- def report
273
- @report
274
- end
275
-
276
- private
277
-
278
- # @note request the specified url via SSRF
279
- def request url
280
- begin
281
- response = @ssrf.send_uri(url)
282
- rescue => e
283
- logger.error "Error: #{e.message}"
284
- end
285
- return response.to_s
286
- end
287
-
288
- # @note port scan common ports for the specified host
289
- def scan_common_ports host
290
- [21,22,23,80,81,443,631,3128,3306,3389,4444,5432,6666,6789,8080,8081,8082,8443,10000,10040,10443,11211].each do |port|
291
- url = "http://#{host}:#{port}/#{rand(10)}"
292
- res = request "#{url}".unpack("C*").pack("U*")
293
- #next if res =~ /failed to open stream: Connection refused/ # php: port is closed
294
- @report << [url, 'SSH'] if port == 22 && res =~ /SSH/
295
- @report << [url, 'CUPS'] if port == 631 && res =~ /CUPS/
296
- @report << [url, 'MySQL'] if port == 3306 && res =~ /Got packets out of order/
297
- @report << [url, 'Groovy Shell'] if port == 6789 && res =~ /Groovy Shell/
298
- @report << [url, 'Apache Felix Shell'] if port == 6666 && res =~ /Felix/i
299
- @report << [url, 'memcached'] if port == 11211 && res =~ /memcached/
300
- end
301
- end
302
-
303
- # @note check the first ip in all /24s
304
- def ping_sweep_network(network)
305
- print_status "Scanning #{network}.x.x.1"
306
- 255.times do |i|
307
- 255.times do |j|
308
- request "http://#{network}.#{i}.#{j}.1/"
309
- end
310
- end
311
- end
312
-
313
- # @note scan for vendor docs directory
314
- def scan_doc host, port=80
315
- url = "http://#{host}/doc/"
316
- res = request url
317
- @report << [url, 'Documentation'] if res =~ /Parent Directory/
318
- end
319
-
320
- # @note scan for vendor icons directory
321
- def scan_icons host, port=80
322
- url = "http://#{host}/icons/"
323
- res = request url
324
- @report << [url, 'Icons'] if res =~ /Parent Directory/
325
- end
326
-
327
- # @note scan for CUPS
328
- def scan_cups host, port=631
329
- url = "http://#{host}:#{port}/"
330
- res = request url
331
- @report << [url, 'CUPS'] if res =~ /CUPS/
332
- end
333
-
334
- # @note scan for mod_status (/server-status)
335
- def scan_mod_status host, port=80
336
- url = "http://#{host}:#{port}/server-status"
337
- res = request url
338
- @report << [url, 'mod_status'] if res =~ /Server Status/
339
- end
340
-
341
- # @note scan for Tomcat management interface
342
- def scan_tomcat host, port=8080
343
- url = "http://#{host}:#{port}/manager/html"
344
- res = request url
345
- @report << [url, 'Tomcat Manager'] if res =~ /Tomcat Manager/
346
- end
347
-
348
- # @note scan for Jenkins script console
349
- def scan_jenkins_script host, port=8080
350
- url = "http://#{host}:#{port}/jenkins/script"
351
- res = request url
352
- @report << [url, 'Jenkins Script Console'] if res =~ /Script Console/
353
- end
354
-
355
- # @note scan for WordPress
356
- def scan_wordpress host, port=80
357
- url = "http://#{host}:#{port}/wp-admin/"
358
- res = request url
359
- @report << [url, 'WordPress'] if res =~ /WordPress/i
360
- url = "http://#{host}:#{port}/wordpress/"
361
- res = request url
362
- @report << [url, 'WordPress'] if res =~ /wp-content/i
363
- end
364
-
365
- # @note scan for phpMyAdmin
366
- def scan_phpmyadmin host, port=80
367
- url = "http://#{host}:#{port}/phpmyadmin/"
368
- res = request url
369
- @report << [url, 'phpMyAdmin'] if res =~ /phpMyAdmin/
370
- end
371
-
372
- # @note scan for Groovy Shell
373
- def scan_groovy_shell host, port=6789
374
- url = "http://#{host}:#{port}/"
375
- res = request url
376
- @report << [url, 'Groovy Shell'] if res =~ /Groovy Shell/
377
- end
378
-
379
- # @note scan for redis
380
- def scan_redis host, port=6379
381
- url = "http://#{host}:#{port}/1"
382
- res = request url
383
- @report << [url, 'Redis'] if res =~ /redis/i
384
- end
385
-
386
- # @note scan for couchdb
387
- def scan_couchdb host, port=5984
388
- url = "http://#{host}:#{port}/_users/_all_docs"
389
- res = request url
390
- @report << [url, 'CouchDB'] if res =~ /"total_rows"/
391
- end
392
-
393
- # @note scan for /Trace.axd
394
- def trace_axd
395
- url = "http://#{host}:#{port}/trace.axd"
396
- res = request url
397
- @report << [url, 'Trace.axd'] if res =~ /Trace/
398
- end
399
-
400
- # @note scan for Ruby Gem Server
401
- def scan_gems host, port=8808
402
- url = "http://#{host}:#{port}/"
403
- res = request url
404
- @report << [url, 'Ruby Gems Server'] if res =~ /RubyGems Documentation Index/
405
- end
406
-
407
- # @note scan XAMPP
408
- def scan_xampp host, port=80
409
- url = "http://#{host}:#{port}/xampp/"
410
- res = request url
411
- @report << [url, 'XAMPP'] if res =~ /XAMPP/
412
- url = "http://#{host}:#{port}/xampp/phpinfo.php"
413
- res = request url
414
- @report << [url, 'XAMPP'] if res =~ /XAMPP/
415
- end
416
-
417
- # @note scan a host
418
- def scan_host host, port
419
- #print_status "Beginning port scan (#{host})"
420
- #scan_common_ports host
421
-
422
- print_status "Beginning app enumeration (#{host}:#{port})"
423
- scan_port host, port
424
-
425
- print_status "Beginning service enumeration (#{host})"
426
- scan_cups host
427
- scan_groovy_shell host
428
- scan_couchdb host
429
- scan_jenkins_script host
430
- scan_redis host
431
- scan_gems host
432
-
433
- print_good "Scan complete (#{host})"
434
- end
435
-
436
- # @note scan a port for web apps
437
- def scan_port host, port=80
438
- scan_mod_status host, port
439
- scan_doc host, port
440
- scan_icons host, port
441
- scan_xampp host, port
442
- scan_wordpress host, port
443
- scan_tomcat host, port
444
- scan_phpmyadmin host, port
445
- end
446
-
447
- end
448
-
449
- banner
450
- usage if ARGV.length == 0
451
- start_scan
452
-