ronin-vulns 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b883fd15ba88606984046997827e571053dc77cc7309cb228746435706789fe
4
- data.tar.gz: 1090624e7a3dbb9d1b212052a1f300f454620cdb5508f4605e3124b8432698b1
3
+ metadata.gz: b260c485d9272af5724bed43fc2959995463f8b78e276bf669d1bb98740e917c
4
+ data.tar.gz: de99077b9d9fe73bd167309af9c7c6d7688bd03a02d2af9bd2fc8eae133aa43a
5
5
  SHA512:
6
- metadata.gz: ba3fecaec4b2dcba4043c8cd95d67b0443011c04f0f83296d86f2cbef4b364d30359093ebee289076a1cbf8e55b77e3a03ca9bcd8b6aea6a972390571ff14910
7
- data.tar.gz: ffb84a729dbab47c6df0bde5f4a7df8f9c0e345de58bb9ddc36d6c6420c87b77f615b0b1d1d7caa5f8edfc456bcca909b3dbf28d32dab2ddde83bd3ec066adec
6
+ metadata.gz: 1f4ce00d82035a11ade026eb6deee9be1244b499d692e47d4961be3cfc2a2dd9d8c609b6c11c5fab72e454f5ccc60adf9dd6ab7e4ceced5b0df9ea27fc18ec27
7
+ data.tar.gz: fb41c677b9eaf56c3cc3fabc028db365061ed98e745dfc3bf942238ebd6c2b642716a2e5dc7f010bb9fa2249112bf4ab66db715d35221783104a3702c694aeea
data/.document CHANGED
@@ -2,4 +2,3 @@ lib/**/*.rb
2
2
  -
3
3
  ChangeLog.md
4
4
  COPYING.txt
5
- man/*.md
@@ -30,3 +30,17 @@ jobs:
30
30
  run: bundle install --jobs 4 --retry 3
31
31
  - name: Run tests
32
32
  run: bundle exec rake test
33
+
34
+ # rubocop linting
35
+ rubocop:
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v2
39
+ - name: Set up Ruby
40
+ uses: ruby/setup-ruby@v1
41
+ with:
42
+ ruby-version: 3.0
43
+ - name: Install dependencies
44
+ run: bundle install --jobs 4 --retry 3
45
+ - name: Run rubocop
46
+ run: bundle exec rubocop --parallel
data/.rubocop.yml ADDED
@@ -0,0 +1,28 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ SuggestExtensions: false
4
+ TargetRubyVersion: 3.1
5
+
6
+ inherit_gem:
7
+ rubocop-ronin: rubocop.yml
8
+
9
+ # we need to make the branching logic extremely explicit here
10
+ Lint/DuplicateBranch:
11
+ Exclude:
12
+ - 'lib/ronin/vulns/lfi.rb'
13
+
14
+ # we need to use eval() in the specs to test the SSTI test expression
15
+ Security/Eval:
16
+ Exclude:
17
+ - 'spec/ssti_spec.rb'
18
+
19
+ # we need to call URLScanner.scan with a block
20
+ Lint/EmptyBlock:
21
+ Exclude:
22
+ - 'spec/reflected_xss_spec.rb'
23
+ - 'spec/url_scanner_spec.rb'
24
+
25
+ # Ronin::Vulns::Vuln does not define an #initialize method
26
+ Lint/MissingSuper:
27
+ Exclude:
28
+ - 'lib/ronin/vulns/web_vuln.rb'
data/ChangeLog.md CHANGED
@@ -1,3 +1,24 @@
1
+ ### 0.1.3 / 2023-07-07
2
+
3
+ * Fixed a bug in {Ronin::Vulns::SSTI.scan} where when called without `escape:`
4
+ it would not return all found vulnerabilities.
5
+ * Fixed a bug in {Ronin::Vulns::SQLI.scan} where repeat requests would be sent
6
+ even if `escape_quote:`, `escape_parens:`, or `terminate:` keyword arguments
7
+ are given.
8
+ * Improved {Ronin::Vulns::ReflectedXSS::Context} to detect when the XSS occurs
9
+ after or *inside of* an HTML comment.
10
+
11
+ ### 0.1.2 / 2023-03-01
12
+
13
+ * Require `ronin-support` ~> 1.0, >= 1.0.1
14
+
15
+ #### CLI
16
+
17
+ * Validate that given URLs start with either `http://` or `https://`, and print
18
+ an error message otherwise.
19
+ * Print a `No vulnerabilities found` message when no vulnerabilities were
20
+ discovered.
21
+
1
22
  ### 0.1.1 / 2023-02-02
2
23
 
3
24
  * Fixed typo in {Ronin::Vulns::CLI::WebVulnCommand#process_url} which effected
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
@@ -8,9 +10,9 @@ gem 'jruby-openssl', '~> 0.7', platforms: :jruby
8
10
  # branch: '0.4.0'
9
11
 
10
12
  # Ronin dependencies
11
- # gem 'ronin-support', '~> 1.0', github: "ronin-rb/ronin-support",
13
+ # gem 'ronin-support', '~> 1.0', github: 'ronin-rb/ronin-support',
12
14
  # branch: 'main'
13
- # gem 'ronin-core', '~> 0.1', github: "ronin-rb/ronin-core",
15
+ # gem 'ronin-core', '~> 0.1', github: 'ronin-rb/ronin-core',
14
16
  # branch: 'main'
15
17
 
16
18
  group :development do
@@ -31,4 +33,6 @@ group :development do
31
33
  gem 'dead_end', require: false
32
34
  gem 'sord', require: false, platform: :mri
33
35
  gem 'stackprof', require: false, platform: :mri
36
+ gem 'rubocop', require: false, platform: :mri
37
+ gem 'rubocop-ronin', require: false, platform: :mri
34
38
  end
data/Rakefile CHANGED
@@ -1,11 +1,11 @@
1
- require 'rubygems'
1
+ # frozen_string_literal: true
2
2
 
3
3
  begin
4
4
  require 'bundler'
5
5
  rescue LoadError => e
6
6
  warn e.message
7
7
  warn "Run `gem install bundler` to install Bundler"
8
- exit -1
8
+ exit(-1)
9
9
  end
10
10
 
11
11
  begin
data/bin/ronin-vulns CHANGED
@@ -1,17 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'rubygems'
4
5
 
5
6
  root = File.expand_path(File.join(File.dirname(__FILE__),'..'))
6
7
  if File.file?(File.join(root,'Gemfile.lock'))
7
8
  Dir.chdir(root) do
8
- begin
9
- require 'bundler/setup'
10
- rescue LoadError => e
11
- warn e.message
12
- warn "Run `gem install bundler` to install Bundler"
13
- exit -1
14
- end
9
+ require 'bundler/setup'
10
+ rescue LoadError => e
11
+ warn e.message
12
+ warn "Run `gem install bundler` to install Bundler"
13
+ exit(-1)
15
14
  end
16
15
  end
17
16
 
data/gemspec.yml CHANGED
@@ -34,7 +34,7 @@ generated_files:
34
34
  - man/ronin-vulns-scan.1
35
35
 
36
36
  dependencies:
37
- ronin-support: ~> 1.0
37
+ ronin-support: ~> 1.0, >= 1.0.1
38
38
  ronin-core: ~> 0.1
39
39
 
40
40
  development_dependencies:
@@ -78,6 +78,7 @@ module Ronin
78
78
  #
79
79
  def scan_kwargs
80
80
  kwargs = super()
81
+
81
82
  kwargs[:test_url] = options[:test_url] if options[:test_url]
82
83
  return kwargs
83
84
  end
@@ -69,7 +69,7 @@ module Ronin
69
69
  'double-encode' => :double_encode,
70
70
  'suffix-escape' => :suffix_escape,
71
71
  'null-byte' => :null_byte
72
- },
72
+ }
73
73
  },
74
74
  desc: 'Optional filter-bypass strategy to use'
75
75
 
@@ -98,7 +98,7 @@ module Ronin
98
98
  'double-encode' => :double_encode,
99
99
  'suffix-escape' => :suffix_escape,
100
100
  'null-byte' => :null_byte
101
- },
101
+ }
102
102
  },
103
103
  desc: 'Optional filter-bypass strategy to use'
104
104
 
@@ -127,7 +127,7 @@ module Ronin
127
127
  option :sqli_terminate, desc: 'Terminates the SQL expression with a --'
128
128
 
129
129
  option :ssti_test_expr, value: {
130
- type: /\A\d+\s*[\*\/\+\-]\s*\d+\z/,
130
+ type: %r{\A\d+\s*[\*/\+\-]\s*\d+\z},
131
131
  usage: '{X*Y | X/Z | X+Y | X-Y}'
132
132
  },
133
133
  desc: 'Optional numeric test to use' do |expr|
@@ -260,10 +260,10 @@ module Ronin
260
260
  def scan_kwargs
261
261
  kwargs = super()
262
262
 
263
- kwargs[:lfi] = lfi_kwargs
264
- kwargs[:rfi] = rfi_kwargs
265
- kwargs[:sqli] = sqli_kwargs
266
- kwargs[:ssti] = ssti_kwargs
263
+ kwargs[:lfi] = lfi_kwargs
264
+ kwargs[:rfi] = rfi_kwargs
265
+ kwargs[:sqli] = sqli_kwargs
266
+ kwargs[:ssti] = ssti_kwargs
267
267
  kwargs[:open_redirect] = open_redirect_kwargs
268
268
  kwargs[:reflected_xss] = reflected_xss_kwargs
269
269
 
@@ -63,7 +63,7 @@ module Ronin
63
63
 
64
64
  option :test_expr, short: '-T',
65
65
  value: {
66
- type: /\A\d+\s*[\*\/\+\-]\s*\d+\z/,
66
+ type: %r{\A\d+\s*[\*/\+\-]\s*\d+\z},
67
67
  usage: '{X*Y | X/Z | X+Y | X-Y}'
68
68
  },
69
69
  desc: 'Optional numeric test to use' do |expr|
@@ -86,6 +86,7 @@ module Ronin
86
86
  #
87
87
  def scan_kwargs
88
88
  kwargs = super()
89
+
89
90
  kwargs[:test_expr] = @test_expr if @test_expr
90
91
  return kwargs
91
92
  end
@@ -23,6 +23,9 @@ require 'ronin/core/cli/logging'
23
23
  module Ronin
24
24
  module Vulns
25
25
  class CLI
26
+ #
27
+ # Mixin that adds methods for logging discovered web vulnerabilities.
28
+ #
26
29
  module Logging
27
30
  include Core::CLI::Logging
28
31
 
@@ -53,7 +53,9 @@ module Ronin
53
53
  desc: 'Sets an additional header' do |header|
54
54
  name, value = header.split(/:\s*/,2)
55
55
 
56
+ # lazy initialize the headers
56
57
  @headers ||= {}
58
+
57
59
  @headers[name] = value
58
60
  end
59
61
 
@@ -74,14 +76,16 @@ module Ronin
74
76
  desc: 'Sets an additional cookie param' do |param|
75
77
  name, value = param.split('=',2)
76
78
 
79
+ # lazy initialize the cookie
77
80
  @cookie ||= Support::Network::HTTP::Cookie.new
81
+
78
82
  @cookie[name] = value
79
83
  end
80
84
 
81
85
  option :referer, short: '-R',
82
86
  value: {
83
87
  type: String,
84
- usage: 'URL',
88
+ usage: 'URL'
85
89
  },
86
90
  desc: 'Sets the Referer header' do |referer|
87
91
  @referer = referer
@@ -95,7 +99,9 @@ module Ronin
95
99
  desc: 'Sets an additional form param' do |param|
96
100
  name, value = param.split('=',2)
97
101
 
102
+ # lazy initialize the form data
98
103
  @form_data ||= {}
104
+
99
105
  @form_data[name] = value
100
106
  end
101
107
 
@@ -104,7 +110,9 @@ module Ronin
104
110
  usage: 'NAME'
105
111
  },
106
112
  desc: 'Tests the URL query param name' do |name|
113
+ # lazy initialize the test query params
107
114
  @test_query_params ||= Set.new
115
+
108
116
  @test_query_params << name
109
117
  end
110
118
 
@@ -117,7 +125,9 @@ module Ronin
117
125
  usage: 'NAME'
118
126
  },
119
127
  desc: 'Tests the HTTP Header name' do |name|
128
+ # lazy initialize the test heade rnames
120
129
  @test_header_names ||= Set.new
130
+
121
131
  @test_header_names << name
122
132
  end
123
133
 
@@ -126,7 +136,9 @@ module Ronin
126
136
  usage: 'NAME'
127
137
  },
128
138
  desc: 'Tests the HTTP Cookie name' do |name|
139
+ # lazy initialize the test cookie params
129
140
  @test_cookie_params ||= Set.new
141
+
130
142
  @test_cookie_params << name
131
143
  end
132
144
 
@@ -135,11 +147,13 @@ module Ronin
135
147
  end
136
148
 
137
149
  option :test_form_param, value: {
138
- type: String,
150
+ type: String,
139
151
  usage: 'NAME'
140
- },
152
+ },
141
153
  desc: 'Tests the form param name' do |name|
154
+ # lazy initialize the test form params
142
155
  @test_form_params ||= Set.new
156
+
143
157
  @test_form_params << name
144
158
  end
145
159
 
@@ -235,19 +249,27 @@ module Ronin
235
249
  # The URL(s) to scan.
236
250
  #
237
251
  def run(*urls)
252
+ unless (options[:input] || !urls.empty?)
253
+ print_error "must specify URL(s) or --input"
254
+ exit(-1)
255
+ end
256
+
257
+ vulns_discovered = false
258
+
238
259
  if options[:input]
239
260
  File.open(options[:input]) do |file|
240
261
  file.each_line(chomp: true) do |url|
241
- process_url(url)
262
+ vulns_discovered ||= process_url(url)
242
263
  end
243
264
  end
244
265
  elsif !urls.empty?
245
266
  urls.each do |url|
246
- process_url(url)
267
+ vulns_discovered ||= process_url(url)
247
268
  end
248
- else
249
- print_error "must specify URL(s) or --input"
250
- exit(-1)
269
+ end
270
+
271
+ unless vulns_discovered
272
+ puts colors.green("No vulnerabilities found")
251
273
  end
252
274
  end
253
275
 
@@ -257,16 +279,32 @@ module Ronin
257
279
  # @param [String] url
258
280
  # A URL to scan.
259
281
  #
282
+ # @return [Boolean]
283
+ # Indicates whether a vulnerability was discovered in the URL.
284
+ #
260
285
  def process_url(url)
286
+ unless url.start_with?('http://') || url.start_with?('https://')
287
+ print_error("URL must start with http:// or https://: #{url.inspect}")
288
+ exit(-1)
289
+ end
290
+
291
+ vuln_discovered = false
292
+
261
293
  if @scan_mode == :first
262
294
  if (first_vuln = test_url(url))
263
295
  log_vuln(first_vuln)
296
+
297
+ vuln_discovered = true
264
298
  end
265
299
  else
266
300
  scan_url(url) do |vuln|
267
301
  log_vuln(vuln)
302
+
303
+ vuln_discovered = true
268
304
  end
269
305
  end
306
+
307
+ return vuln_discovered
270
308
  end
271
309
 
272
310
  #
@@ -295,7 +333,7 @@ module Ronin
295
333
  kwargs[:query_params] = true
296
334
  end
297
335
 
298
- kwargs[:header_names] = @test_header_names if @test_header_names
336
+ kwargs[:header_names] = @test_header_names if @test_header_names
299
337
 
300
338
  if @test_cookie_params
301
339
  kwargs[:cookie_params] = @test_cookie_params
@@ -303,7 +341,7 @@ module Ronin
303
341
  kwargs[:cookie_params] = true
304
342
  end
305
343
 
306
- kwargs[:form_params] = @test_form_params if @test_form_params
344
+ kwargs[:form_params] = @test_form_params if @test_form_params
307
345
 
308
346
  return kwargs
309
347
  end
@@ -57,7 +57,7 @@ module Ronin
57
57
  attr_reader :os
58
58
 
59
59
  # Optional filter bypass technique to use.
60
- #
60
+ #
61
61
  # @return [:null_byte, :base64, :rot13, :zlib, nil]
62
62
  attr_reader :filter_bypass
63
63
 
@@ -176,7 +176,7 @@ module Ronin
176
176
  "#{@escape_path}#{path[3..]}"
177
177
  elsif @os == :windows && path =~ /\A[A-Z]:/
178
178
  # pass through absolute Windows paths to other drives
179
- path
179
+ path
180
180
  elsif path.start_with?(@separator)
181
181
  # escape absolute paths
182
182
  "#{@escape_path}#{path[1..]}"
@@ -234,10 +234,9 @@ module Ronin
234
234
  Crypto.rot(body,-13) =~ @test_file
235
235
  when :zlib
236
236
  body.scan(Text::Patterns::BASE64).any? do |string|
237
- begin
238
- Compression.zlib_inflate(Base64.decode64(string)) =~ @test_file
239
- rescue Zlib::DataError
240
- end
237
+ Compression.zlib_inflate(Base64.decode64(string)) =~ @test_file
238
+ rescue Zlib::DataError
239
+ # not zlib compressed Base64, ignore
241
240
  end
242
241
  else
243
242
  body =~ @test_file
@@ -80,7 +80,7 @@ module Ronin
80
80
  when '301', '302', '303', '307', '308'
81
81
  if (locations = response.get_fields('Location'))
82
82
  escaped_test_url = Regexp.escape(@test_url)
83
- regexp = %r{\A#{escaped_test_url}(?:[\?&].+)?\z}
83
+ regexp = /\A#{escaped_test_url}(?:[\?&].+)?\z/
84
84
 
85
85
  locations.last =~ regexp
86
86
  end
@@ -89,7 +89,18 @@ module Ronin
89
89
 
90
90
  if content_type && content_type.include?('text/html')
91
91
  escaped_test_url = Regexp.escape(CGI.escapeHTML(@test_url))
92
- regexp = %r{<meta\s+http-equiv\s*=\s*(?:"refresh"|'refresh'|refresh)\s+content\s*=\s*(?:"\s*\d+\s*;\s*url\s*=\s*'\s*#{escaped_test_url}\s*'\s*"|'\s*\d+\s*;\s*url\s*=\s*"\s*#{escaped_test_url}\s*"\s*'|\s*\d+;url=(?:"#{escaped_test_url}"|'#{escaped_test_url}'))\s*(?:/\s*)?>}i
92
+
93
+ regexp = %r{
94
+ <meta\s+
95
+ http-equiv\s*=\s*(?: "refresh" | 'refresh' | refresh )\s+
96
+ content\s*=\s*
97
+ (?:
98
+ "\s*\d+\s*;\s*url\s*=\s*'\s*#{escaped_test_url}\s*'\s*"|
99
+ '\s*\d+\s*;\s*url\s*=\s*"\s*#{escaped_test_url}\s*"\s*'|
100
+ \s*\d+;url=(?: "#{escaped_test_url}" | '#{escaped_test_url}' )
101
+ )\s*
102
+ (?:/\s*)?>
103
+ }xi
93
104
 
94
105
  response.body =~ regexp
95
106
  end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # frozen_string_literal: true
3
2
  #
4
3
  # ronin-vulns - A Ruby library for blind vulnerability testing.
5
4
  #
@@ -31,7 +30,7 @@ module Ronin
31
30
 
32
31
  # Where in the HTML the XSS occurs.
33
32
  #
34
- # @return [:double_quoted_attr_value, :single_quoted_attr_value, :unquoted_attr_value, :attr_name, :attr_list, :tag_name, :tag_body]
33
+ # @return [:double_quoted_attr_value, :single_quoted_attr_value, :unquoted_attr_value, :attr_name, :attr_list, :tag_name, :tag_body, :comment]
35
34
  # The context which the XSS occurs in.
36
35
  # * `:tag_body` occurred within a tag's body (ex: `<tag>XSS...</tag>`)
37
36
  # * `:double_quoted_attr_value` - occurred in a double quoted
@@ -45,13 +44,14 @@ module Ronin
45
44
  # * `:attr_list` - occurred in the attribute list
46
45
  # (ex: `<tag XSS>...</tag>`)
47
46
  # * `:tag_name` - occurred in the tag name (ex: `<tagXSS>...</tag>`)
47
+ # * `:comment` - occurred in a comment (ex: `<!-- XSS -->`)
48
48
  #
49
49
  # @api public
50
50
  attr_reader :location
51
51
 
52
52
  # The name of the parent tag which the XSS occurs in.
53
53
  #
54
- # @return [String]
54
+ # @return [String, nil]
55
55
  #
56
56
  # @api public
57
57
  attr_reader :tag
@@ -66,9 +66,9 @@ module Ronin
66
66
  #
67
67
  # Initializes the context.
68
68
  #
69
- # @param [:double_quoted_attr_value, :single_quoted_attr_value, :unquoted_attr_value, :attr_name, :attr_list, :tag_name, :tag_body] location
69
+ # @param [:double_quoted_attr_value, :single_quoted_attr_value, :unquoted_attr_value, :attr_name, :attr_list, :tag_name, :tag_body, :comment] location
70
70
  #
71
- # @param [String] tag
71
+ # @param [String, nil] tag
72
72
  #
73
73
  # @param [String, nil] attr
74
74
  #
@@ -101,6 +101,11 @@ module Ronin
101
101
  # @api private
102
102
  ATTR_LIST = /(?:\s+#{ATTR})*/
103
103
 
104
+ # HTML comment regexp.
105
+ #
106
+ # @api private
107
+ COMMENT = /<![^>]*>/
108
+
104
109
  # HTML tag name regexp.
105
110
  #
106
111
  # @api private
@@ -109,39 +114,44 @@ module Ronin
109
114
  # Regexp matching when an XSS occurs within a tag's inner HTML.
110
115
  #
111
116
  # @api private
112
- IN_TAG_BODY = %r{<(#{TAG_NAME})#{ATTR_LIST}\s*(?:>|/>)[^<>]*\z}
117
+ IN_TAG_BODY = %r{<(#{TAG_NAME})#{ATTR_LIST}\s*(?:>|/>)([^<>]|#{COMMENT})*\z}
113
118
 
114
119
  # Regexp matching when an XSS occurs within a double-quoted attribute
115
120
  # value.
116
121
  #
117
122
  # @api private
118
- IN_DOUBLE_QUOTED_ATTR_VALUE = %r{<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\s*=\s*"[^"]+\z}
123
+ IN_DOUBLE_QUOTED_ATTR_VALUE = /<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\s*=\s*"[^"]+\z/
119
124
 
120
125
  # Regexp matching when an XSS occurs within a single-quoted attribute
121
126
  # value.
122
127
  #
123
128
  # @api private
124
- IN_SINGLE_QUOTED_ATTR_VALUE = %r{<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\s*=\s*'[^']+\z}
129
+ IN_SINGLE_QUOTED_ATTR_VALUE = /<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\s*=\s*'[^']+\z/
125
130
 
126
131
  # Regexp matching when an XSS occurs within an unquoted attribute value.
127
132
  #
128
133
  # @api private
129
- IN_UNQUOTED_ATTR_VALUE = %r{<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})=[^"'\s]+\z}
134
+ IN_UNQUOTED_ATTR_VALUE = /<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})=[^"'\s]+\z/
130
135
 
131
136
  # Regexp matching when an XSS occurs within an attribute's name.
132
137
  #
133
138
  # @api private
134
- IN_ATTR_NAME = %r{<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\z}
139
+ IN_ATTR_NAME = /<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\z/
135
140
 
136
141
  # Regexp matching when an XSS occurs within a tag's attribute list.
137
142
  #
138
143
  # @api private
139
- IN_ATTR_LIST = %r{<(#{TAG_NAME})#{ATTR_LIST}\s+\z}
144
+ IN_ATTR_LIST = /<(#{TAG_NAME})#{ATTR_LIST}\s+\z/
140
145
 
141
146
  # Regexp matching when an XSS occurs within a tag's name.
142
147
  #
143
148
  # @api private
144
- IN_TAG_NAME = %r{<(#{TAG_NAME})\z}
149
+ IN_TAG_NAME = /<(#{TAG_NAME})\z/
150
+
151
+ # Regexp matching when an XSS occurs within a comment.
152
+ #
153
+ # @api private
154
+ IN_COMMENT = /<![^>]*\z/
145
155
 
146
156
  #
147
157
  # Determine the context of the XSS by checking the characters that come
@@ -161,7 +171,7 @@ module Ronin
161
171
  def self.identify(body,index)
162
172
  prefix = body[0,index]
163
173
 
164
- if (match = prefix.match(IN_TAG_BODY))
174
+ if (match = prefix.match(IN_TAG_BODY))
165
175
  new(:tag_body, tag: match[1])
166
176
  elsif (match = prefix.match(IN_DOUBLE_QUOTED_ATTR_VALUE))
167
177
  new(:double_quoted_attr_value, tag: match[1], attr: match[2])
@@ -175,6 +185,8 @@ module Ronin
175
185
  new(:attr_list, tag: match[1])
176
186
  elsif (match = prefix.match(IN_TAG_NAME))
177
187
  new(:tag_name, tag: match[1])
188
+ elsif prefix.match?(IN_COMMENT)
189
+ new(:comment)
178
190
  end
179
191
  end
180
192
 
@@ -194,7 +206,8 @@ module Ronin
194
206
  attr_name: MINIMAL_REQUIRED_CHARS,
195
207
  attr_list: MINIMAL_REQUIRED_CHARS,
196
208
  tag_name: MINIMAL_REQUIRED_CHARS,
197
- tag_body: MINIMAL_REQUIRED_CHARS
209
+ tag_body: MINIMAL_REQUIRED_CHARS,
210
+ comment: MINIMAL_REQUIRED_CHARS
198
211
  }
199
212
 
200
213
  #
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # frozen_string_literal: true
3
2
  #
4
3
  # ronin-vulns - A Ruby library for blind vulnerability testing.
5
4
  #
@@ -67,7 +66,7 @@ module Ronin
67
66
  '/' => ['%2F'],
68
67
  '<' => ['%3C', '&lt;'],
69
68
  '>' => ['%3E', '&gt;'],
70
- '&' => ['%26', '&amp;'],
69
+ '&' => ['%26', '&amp;']
71
70
  }
72
71
 
73
72
  #
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # frozen_string_literal: true
3
2
  #
4
3
  # ronin-vulns - A Ruby library for blind vulnerability testing.
5
4
  #
@@ -91,7 +90,7 @@ module Ronin
91
90
  # The test string to send.
92
91
  #
93
92
  # @yield [body, match]
94
- # If a block is given, it will be passed the response body and the
93
+ # If a block is given, it will be passed the response body and the
95
94
  # regular expression match data, if the response contains the test
96
95
  # string.
97
96
  #
@@ -119,7 +118,7 @@ module Ronin
119
118
  # Tests which HTML characters are accepted or escaped/filtered.
120
119
  #
121
120
  # @yield [body, match]
122
- # If a block is given, it will be passed the response body and the
121
+ # If a block is given, it will be passed the response body and the
123
122
  # regular expression match data, if the response contains the test
124
123
  # string.
125
124
  #
@@ -65,7 +65,7 @@ module Ronin
65
65
  attr_reader :filter_bypass
66
66
 
67
67
  # URL of the Remote File Inclusion (RFI) Test script
68
- #
68
+ #
69
69
  # @return [URI::HTTP, String]
70
70
  attr_reader :test_script_url
71
71
 
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # frozen_string_literal: true
3
2
  #
4
3
  # ronin-vulns - A Ruby library for blind vulnerability testing.
5
4
  #
@@ -42,7 +41,7 @@ module Ronin
42
41
  attr_reader :escape_quote
43
42
 
44
43
  # Specifies whether to escape parenthesis.
45
- #
44
+ #
46
45
  # @return [Boolean]
47
46
  attr_reader :escape_parens
48
47
 
@@ -106,6 +105,18 @@ module Ronin
106
105
  # @param [URI::HTTP, String] url
107
106
  # The URL to test or exploit.
108
107
  #
108
+ # @param [Array<Boolean>, Boolean] escape_quote
109
+ # Controls whether to escape a quoted string value. If not specified,
110
+ # with and without quoted string escaping will be tested.
111
+ #
112
+ # @param [Array<Boolean>, Boolean] escape_parens
113
+ # Controls whether to escape parenthesis. If not specified, with and
114
+ # without parenthesis escaping will be tested.
115
+ #
116
+ # @param [Array<Boolean>, Boolean] terminate
117
+ # Controls whether to terminate the SQL statement with `--`.
118
+ # If not specified, with and without `--` terminate will be tested.
119
+ #
109
120
  # @param [Ronin::Support::Network::HTTP, nil] http
110
121
  # An HTTP session to use for testing the URL.
111
122
  #
@@ -116,28 +127,28 @@ module Ronin
116
127
  # If a block is given it will be yielded each discovered SQL injection
117
128
  # vulnerability.
118
129
  #
119
- # @yieldparam [SQLi] sqli
130
+ # @yieldparam [SQLI] sqli
120
131
  # A discovered SQL injection vulnerability in the URL.
121
132
  #
122
- # @return [Array<SQLi>]
133
+ # @return [Array<SQLI>]
123
134
  # All discovered SQL injection vulnerabilities.
124
135
  #
125
- def self.scan(url, http: nil, **kwargs, &block)
136
+ def self.scan(url, escape_quote: [false, true],
137
+ escape_parens: [false, true],
138
+ terminate: [false, true],
139
+ # WebVuln.scan keyword arguments
140
+ http: nil, **kwargs, &block)
126
141
  url = URI(url)
127
142
  http ||= Support::Network::HTTP.connect_uri(url)
128
143
 
129
- escape_quotes = [false, true]
130
- escape_parens = [false, true]
131
- terminations = [false, true]
132
-
133
144
  vulns = []
134
145
 
135
- escape_quotes.each do |escape_quote|
136
- escape_parens.each do |escape_paren|
137
- terminations.each do |terminate|
138
- vulns.concat(super(url, escape_quote: escape_quote,
139
- escape_parens: escape_paren,
140
- terminate: terminate,
146
+ Array(escape_quote).each do |escape_quote_value|
147
+ Array(escape_parens).each do |escape_parens_value|
148
+ Array(terminate).each do |terminate_value|
149
+ vulns.concat(super(url, escape_quote: escape_quote_value,
150
+ escape_parens: escape_parens_value,
151
+ terminate: terminate_value,
141
152
  http: http,
142
153
  **kwargs,
143
154
  &block))
@@ -205,7 +216,7 @@ module Ronin
205
216
  /ERROR: parser: parse error at or near/,
206
217
  /PostgreSQL query failed/,
207
218
  /org\.postgresql\.jdbc/,
208
- /Pdo[.\/_\\]Pgsql/,
219
+ %r{Pdo[\./_\\]Pgsql},
209
220
  /PSQLException/
210
221
  ],
211
222
 
@@ -219,12 +230,12 @@ module Ronin
219
230
  /MySqlClient\./,
220
231
  /com\.mysql\.jdbc/,
221
232
  /Zend_Db_(?:Adapter|Statement)_Mysqli_Exception/,
222
- /Pdo[.\/_\\]Mysql/,
233
+ %r{Pdo[\./_\\]Mysql},
223
234
  /MySqlException/
224
235
  ],
225
236
 
226
237
  sqlite: ErrorPattern[
227
- /SQLite\/JDBCDriver/,
238
+ %r{SQLite/JDBCDriver},
228
239
  /SQLite\.Exception/,
229
240
  /(Microsoft|System)\.Data\.SQLite\.SQLiteException/,
230
241
  /Warning.*\W(?:sqlite_|SQLite3::)/,
@@ -233,7 +244,7 @@ module Ronin
233
244
  /sqlite3\.OperationalError:/,
234
245
  /SQLite3::SQLException/,
235
246
  /org\.sqlite\.JDBC/,
236
- /Pdo[.\/_\\]Sqlite/,
247
+ %r{Pdo[\./_\\]Sqlite},
237
248
  /SQLiteException/
238
249
  ],
239
250
 
@@ -254,7 +265,7 @@ module Ronin
254
265
  /macromedia\.jdbc\.sqlserver/,
255
266
  /Zend_Db_(?:Adapter|Statement)_Sqlsrv_Exception/,
256
267
  /com\.microsoft\.sqlserver\.jdbc/,
257
- /Pdo[.\/_\\](?:Mssql|SqlSrv)/,
268
+ %r{Pdo[\./_\\](?:Mssql|SqlSrv)},
258
269
  /SQL(?:Srv|Server)Exception/
259
270
  ],
260
271
 
@@ -268,7 +279,7 @@ module Ronin
268
279
  /macromedia\.jdbc\.oracle/,
269
280
  /oracle\.jdbc/,
270
281
  /Zend_Db_(?:Adapter|Statement)_Oracle_Exception/,
271
- /Pdo[.\/_\\](?:Oracle|OCI)/,
282
+ %r{Pdo[\./_\\](?:Oracle|OCI)},
272
283
  /OracleException/
273
284
  ]
274
285
  }
@@ -304,7 +315,7 @@ module Ronin
304
315
  # @api private
305
316
  #
306
317
  def random_id
307
- rand(8_999) + 1_000
318
+ rand(8_999..9999)
308
319
  end
309
320
 
310
321
  #
@@ -317,6 +328,7 @@ module Ronin
317
328
  #
318
329
  def test_or_true_and_false
319
330
  id = random_id
331
+
320
332
  response1 = exploit("OR #{id}=#{id}")
321
333
  response2 = exploit("AND #{random_id}=#{random_id}")
322
334
 
@@ -68,7 +68,7 @@ module Ronin
68
68
  # Could not parse the test expression.
69
69
  #
70
70
  def self.parse(string)
71
- unless (match = string.match(/\A(\d+)\s*([\*\/\+\-])\s*(\d+)\z/))
71
+ unless (match = string.match(%r{\A(\d+)\s*([\*/\+\-])\s*(\d+)\z}))
72
72
  raise(ArgumentError,"could not parse the expression: #{string.inspect}")
73
73
  end
74
74
 
@@ -87,8 +87,8 @@ module Ronin
87
87
  # A random test expression.
88
88
  #
89
89
  def self.random_test
90
- int1 = rand(999) + 1_000
91
- int2 = rand(999) + 1_000
90
+ int1 = rand(1_000..1_999)
91
+ int2 = rand(1_000..1_999)
92
92
 
93
93
  string = "#{int1}*#{int2}"
94
94
  result = (int1 * int2).to_s
@@ -102,13 +102,13 @@ module Ronin
102
102
  # @param [URI::HTTP, String] url
103
103
  # The URL to scan.
104
104
  #
105
+ # @param [Array<Proc>, Proc, nil] escape
106
+ # The escape method to use. If `escape:` is not given, then all escapes
107
+ # in {ESCAPES} will be tested..
108
+ #
105
109
  # @param [Hash{Symbol => Object}] kwargs
106
110
  # Additional keyword arguments for {#initialize}.
107
111
  #
108
- # @option kwargs [Proc, nil] :escape
109
- # The escape method to use. If `escape:` is not given, then all escapes
110
- # in {ESCAPES} will be tested..
111
- #
112
112
  # @option kwargs [Array<Symbol, String>, Symbol, String, true, nil] :query_params
113
113
  # The query param name(s) to test.
114
114
  #
@@ -145,14 +145,14 @@ module Ronin
145
145
  # @return [Array<SSTI>]
146
146
  # All discovered SSTI vulnerabilities.
147
147
  #
148
- def self.scan(url, **kwargs,&block)
149
- if kwargs.has_key?(:escape)
150
- super(url, **kwargs, &block)
151
- else
152
- ESCAPES.each do |escape|
153
- super(url, escape: escape, **kwargs, &block)
154
- end
148
+ def self.scan(url, escape: ESCAPES, **kwargs,&block)
149
+ vulns = []
150
+
151
+ Array(escape).each do |escape_char|
152
+ vulns.concat(super(url, escape: escape_char, **kwargs, &block))
155
153
  end
154
+
155
+ return vulns
156
156
  end
157
157
 
158
158
  #
@@ -27,6 +27,9 @@ require 'ronin/vulns/open_redirect'
27
27
 
28
28
  module Ronin
29
29
  module Vulns
30
+ #
31
+ # Top-level module which scans a URL for all web vulnerabilities.
32
+ #
30
33
  module URLScanner
31
34
  #
32
35
  # Scans a URL for web vulnerabilities.
@@ -21,6 +21,6 @@
21
21
  module Ronin
22
22
  module Vulns
23
23
  # The ronin-vulns version
24
- VERSION = '0.1.1'
24
+ VERSION = '0.1.3'
25
25
  end
26
26
  end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # frozen_string_literal: true
3
2
  #
4
3
  # ronin-vulns - A Ruby library for blind vulnerability testing.
5
4
  #
@@ -119,9 +118,10 @@ module Ronin
119
118
  cookie: nil,
120
119
  form_data: nil)
121
120
  @url = url
122
-
121
+
123
122
  if query_params && !query_params.empty?
124
123
  @url = url.dup
124
+
125
125
  @url.query_params = query_params
126
126
  end
127
127
 
@@ -292,7 +292,7 @@ module Ronin
292
292
  url = URI(url)
293
293
  http ||= Support::Network::HTTP.connect_uri(url)
294
294
 
295
- unless cookie_params
295
+ unless cookie_params
296
296
  cookie_params = Set.new
297
297
 
298
298
  http.get_cookies(url.request_uri).each do |set_cookie|
data/ronin-vulns.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  Gem::Specification.new do |gem|
@@ -20,7 +22,7 @@ Gem::Specification.new do |gem|
20
22
  gem.homepage = gemspec['homepage']
21
23
  gem.metadata = gemspec['metadata'] if gemspec['metadata']
22
24
 
23
- glob = lambda { |patterns| gem.files & Dir[*patterns] }
25
+ glob = ->(patterns) { gem.files & Dir[*patterns] }
24
26
 
25
27
  gem.files = `git ls-files`.split($/)
26
28
  gem.files = glob[gemspec['files']] if gemspec['files']
@@ -44,7 +46,7 @@ Gem::Specification.new do |gem|
44
46
  gem.required_rubygems_version = gemspec['required_rubygems_version']
45
47
  gem.post_install_message = gemspec['post_install_message']
46
48
 
47
- split = lambda { |string| string.split(/,\s*/) }
49
+ split = ->(string) { string.split(/,\s*/) }
48
50
 
49
51
  if gemspec['dependencies']
50
52
  gemspec['dependencies'].each do |name,versions|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ronin-vulns
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-02 00:00:00.000000000 Z
11
+ date: 2023-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ronin-support
@@ -17,6 +17,9 @@ dependencies:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.1
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -24,6 +27,9 @@ dependencies:
24
27
  - - "~>"
25
28
  - !ruby/object:Gem::Version
26
29
  version: '1.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.1
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: ronin-core
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -70,6 +76,7 @@ files:
70
76
  - ".github/workflows/ruby.yml"
71
77
  - ".gitignore"
72
78
  - ".rspec"
79
+ - ".rubocop.yml"
73
80
  - ".ruby-version"
74
81
  - ".yardopts"
75
82
  - COPYING.txt