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