ronin-vulns 0.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.github/workflows/ruby.yml +31 -0
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +22 -0
- data/Gemfile +34 -0
- data/README.md +328 -0
- data/Rakefile +34 -0
- data/bin/ronin-vulns +19 -0
- data/data/rfi_test.asp +21 -0
- data/data/rfi_test.aspx +25 -0
- data/data/rfi_test.cfm +27 -0
- data/data/rfi_test.jsp +19 -0
- data/data/rfi_test.php +24 -0
- data/data/rfi_test.pl +25 -0
- data/gemspec.yml +41 -0
- data/lib/ronin/vulns/cli/command.rb +39 -0
- data/lib/ronin/vulns/cli/commands/lfi.rb +145 -0
- data/lib/ronin/vulns/cli/commands/open_redirect.rb +119 -0
- data/lib/ronin/vulns/cli/commands/reflected_xss.rb +99 -0
- data/lib/ronin/vulns/cli/commands/rfi.rb +156 -0
- data/lib/ronin/vulns/cli/commands/scan.rb +316 -0
- data/lib/ronin/vulns/cli/commands/sqli.rb +133 -0
- data/lib/ronin/vulns/cli/commands/ssti.rb +126 -0
- data/lib/ronin/vulns/cli/logging.rb +78 -0
- data/lib/ronin/vulns/cli/web_vuln_command.rb +347 -0
- data/lib/ronin/vulns/cli.rb +45 -0
- data/lib/ronin/vulns/lfi/test_file.rb +91 -0
- data/lib/ronin/vulns/lfi.rb +266 -0
- data/lib/ronin/vulns/open_redirect.rb +118 -0
- data/lib/ronin/vulns/reflected_xss/context.rb +224 -0
- data/lib/ronin/vulns/reflected_xss/test_string.rb +149 -0
- data/lib/ronin/vulns/reflected_xss.rb +184 -0
- data/lib/ronin/vulns/rfi.rb +224 -0
- data/lib/ronin/vulns/root.rb +28 -0
- data/lib/ronin/vulns/sqli/error_pattern.rb +89 -0
- data/lib/ronin/vulns/sqli.rb +397 -0
- data/lib/ronin/vulns/ssti/test_expression.rb +104 -0
- data/lib/ronin/vulns/ssti.rb +203 -0
- data/lib/ronin/vulns/url_scanner.rb +218 -0
- data/lib/ronin/vulns/version.rb +26 -0
- data/lib/ronin/vulns/vuln.rb +49 -0
- data/lib/ronin/vulns/web_vuln/http_request.rb +223 -0
- data/lib/ronin/vulns/web_vuln.rb +774 -0
- data/man/ronin-vulns-lfi.1 +107 -0
- data/man/ronin-vulns-lfi.1.md +80 -0
- data/man/ronin-vulns-open-redirect.1 +98 -0
- data/man/ronin-vulns-open-redirect.1.md +73 -0
- data/man/ronin-vulns-reflected-xss.1 +95 -0
- data/man/ronin-vulns-reflected-xss.1.md +71 -0
- data/man/ronin-vulns-rfi.1 +107 -0
- data/man/ronin-vulns-rfi.1.md +80 -0
- data/man/ronin-vulns-scan.1 +138 -0
- data/man/ronin-vulns-scan.1.md +103 -0
- data/man/ronin-vulns-sqli.1 +107 -0
- data/man/ronin-vulns-sqli.1.md +80 -0
- data/man/ronin-vulns-ssti.1 +99 -0
- data/man/ronin-vulns-ssti.1.md +74 -0
- data/ronin-vulns.gemspec +60 -0
- metadata +161 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# ronin-vulns - A Ruby library for blind vulnerability testing.
|
5
|
+
#
|
6
|
+
# Copyright (c) 2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
7
|
+
#
|
8
|
+
# ronin-vulns is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Lesser General Public License as published
|
10
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# ronin-vulns is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Lesser General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Lesser General Public License
|
19
|
+
# along with ronin-vulns. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
#
|
21
|
+
|
22
|
+
require 'ronin/vulns/web_vuln'
|
23
|
+
|
24
|
+
module Ronin
|
25
|
+
module Vulns
|
26
|
+
class ReflectedXSS < WebVuln
|
27
|
+
#
|
28
|
+
# A test string of characters to determine which special characters are
|
29
|
+
# escaped/filtered and which are passed through.
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
#
|
33
|
+
class TestString
|
34
|
+
|
35
|
+
# The test string.
|
36
|
+
#
|
37
|
+
# @return [String]
|
38
|
+
attr_reader :string
|
39
|
+
|
40
|
+
# The test regexp to determine which special characters were
|
41
|
+
# escaped/filtered and which were passed through unescaped.
|
42
|
+
#
|
43
|
+
# @return [Regexp]
|
44
|
+
attr_reader :regexp
|
45
|
+
|
46
|
+
#
|
47
|
+
# Initializes the test string.
|
48
|
+
#
|
49
|
+
# @param [String] string
|
50
|
+
# The test string.
|
51
|
+
#
|
52
|
+
# @param [Regexp] regexp
|
53
|
+
# The test regexp to determine which special characters were
|
54
|
+
# escaped/filtered and which were passed through unescaped.
|
55
|
+
#
|
56
|
+
def initialize(string,regexp)
|
57
|
+
@string = string
|
58
|
+
@regexp = regexp
|
59
|
+
end
|
60
|
+
|
61
|
+
# Special characters and their common escaped equivalents.
|
62
|
+
ESCAPED_CHARS = {
|
63
|
+
"'" => ['%27', ''', "\\'"],
|
64
|
+
'"' => ['%22', '"', "\\\""],
|
65
|
+
' ' => ['+', '%20', ' '],
|
66
|
+
'=' => ['%3D'],
|
67
|
+
'/' => ['%2F'],
|
68
|
+
'<' => ['%3C', '<'],
|
69
|
+
'>' => ['%3E', '>'],
|
70
|
+
'&' => ['%26', '&'],
|
71
|
+
}
|
72
|
+
|
73
|
+
#
|
74
|
+
# Builds a test string from a mapping of characters and their HTML
|
75
|
+
# escaped equivalents.
|
76
|
+
#
|
77
|
+
# @param [String] chars
|
78
|
+
# The characters for the test string.
|
79
|
+
#
|
80
|
+
# @return [TestString]
|
81
|
+
# The built test string.
|
82
|
+
#
|
83
|
+
def self.build(chars)
|
84
|
+
string = String.new
|
85
|
+
regexp = String.new
|
86
|
+
|
87
|
+
chars.each_char do |char|
|
88
|
+
string << char
|
89
|
+
|
90
|
+
regexp << "(?:(#{Regexp.escape(char)})"
|
91
|
+
|
92
|
+
if (escaped_chars = ESCAPED_CHARS[char])
|
93
|
+
escaped_chars.each do |string|
|
94
|
+
regexp << "|#{Regexp.escape(string)}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
regexp << ')?'
|
99
|
+
end
|
100
|
+
|
101
|
+
return new(string,Regexp.new(regexp))
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Wraps the test string with a prefix and suffix.
|
106
|
+
#
|
107
|
+
# @param [String] prefix
|
108
|
+
# The prefix string to prepend to the test string.
|
109
|
+
#
|
110
|
+
# @param [String] suffix
|
111
|
+
# The suffix string to append to the test string.
|
112
|
+
#
|
113
|
+
# @return [TestString]
|
114
|
+
# The new test string with the prefix and suffix.
|
115
|
+
#
|
116
|
+
def wrap(prefix,suffix)
|
117
|
+
self.class.new(
|
118
|
+
"#{prefix}#{@string}#{suffix}",
|
119
|
+
/#{Regexp.escape(prefix)}#{@regexp}#{Regexp.escape(suffix)}/
|
120
|
+
)
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Matches the response body against {#regexp}.
|
125
|
+
#
|
126
|
+
# @param [String] body
|
127
|
+
# The response body to try matching.
|
128
|
+
#
|
129
|
+
# @return [MatchData, nil]
|
130
|
+
# The match data or `nil` if the body did not match {#regexp}.
|
131
|
+
#
|
132
|
+
def match(body)
|
133
|
+
body.match(@regexp)
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# Converts the test string to a String.
|
138
|
+
#
|
139
|
+
# @return [String]
|
140
|
+
# The {#string}.
|
141
|
+
#
|
142
|
+
def to_s
|
143
|
+
@string
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# ronin-vulns - A Ruby library for blind vulnerability testing.
|
5
|
+
#
|
6
|
+
# Copyright (c) 2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
7
|
+
#
|
8
|
+
# ronin-vulns is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Lesser General Public License as published
|
10
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# ronin-vulns is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Lesser General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Lesser General Public License
|
19
|
+
# along with ronin-vulns. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
#
|
21
|
+
|
22
|
+
require 'ronin/vulns/web_vuln'
|
23
|
+
require 'ronin/vulns/reflected_xss/test_string'
|
24
|
+
require 'ronin/vulns/reflected_xss/context'
|
25
|
+
|
26
|
+
require 'set'
|
27
|
+
|
28
|
+
module Ronin
|
29
|
+
module Vulns
|
30
|
+
#
|
31
|
+
# Represents a (Reflected) Cross Site Scripting (XSS) vulnerability.
|
32
|
+
#
|
33
|
+
# ## Features
|
34
|
+
#
|
35
|
+
# * Tests a URL with just one HTTP request (per param).
|
36
|
+
# * Tests which HTML special characters are allowed.
|
37
|
+
# * Identifies the context, tag name, and/or attribute name of the XSS.
|
38
|
+
# * Determines viability of XSS based on the context.
|
39
|
+
# * Includes random data in the test values.
|
40
|
+
#
|
41
|
+
class ReflectedXSS < WebVuln
|
42
|
+
|
43
|
+
# The characters that are allowed and will not be escaped or filtered.
|
44
|
+
#
|
45
|
+
# @return [Set<String>, nil]
|
46
|
+
attr_reader :allowed_chars
|
47
|
+
|
48
|
+
# The context the XSS occurred in.
|
49
|
+
#
|
50
|
+
# @return [Context, nil]
|
51
|
+
attr_reader :context
|
52
|
+
|
53
|
+
#
|
54
|
+
# Tests the test string by sending an HTTP request with the test string
|
55
|
+
# embedded.
|
56
|
+
#
|
57
|
+
# @param [TestString] test_string
|
58
|
+
#
|
59
|
+
# @yield [body, match]
|
60
|
+
# If the response was `text/html` and the test string appears (at least
|
61
|
+
# partially) in the response body, the response body and match data will
|
62
|
+
# be yielded.
|
63
|
+
#
|
64
|
+
# @yieldparam [String] body
|
65
|
+
# The response body.
|
66
|
+
#
|
67
|
+
# @yieldparam [MatchData] match
|
68
|
+
# The matched data for the test string.
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
#
|
72
|
+
def test_string(test_string)
|
73
|
+
test_string = test_string.wrap(random_value,random_value)
|
74
|
+
|
75
|
+
response = exploit("#{original_value}#{test_string}")
|
76
|
+
content_type = response.content_type
|
77
|
+
body = response.body
|
78
|
+
|
79
|
+
if content_type && content_type.include?('text/html')
|
80
|
+
if (match = test_string.match(body))
|
81
|
+
yield body, match
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Tests whether characters in the test string will be escaped/filtered or
|
88
|
+
# passed through and updates {#allowed_chars}.
|
89
|
+
#
|
90
|
+
# @param [TestString] test_string
|
91
|
+
# The test string to send.
|
92
|
+
#
|
93
|
+
# @yield [body, match]
|
94
|
+
# If a block is given, it will be passed the response body and the
|
95
|
+
# regular expression match data, if the response contains the test
|
96
|
+
# string.
|
97
|
+
#
|
98
|
+
# @yieldparam [String] body
|
99
|
+
# The response body.
|
100
|
+
#
|
101
|
+
# @yieldparam [MatchData] match
|
102
|
+
# The matched data for the test string.
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
#
|
106
|
+
def test_chars(test_string)
|
107
|
+
test_string(test_string) do |body,match|
|
108
|
+
@allowed_chars ||= Set.new
|
109
|
+
@allowed_chars.merge(match.captures.compact)
|
110
|
+
|
111
|
+
yield body, match if block_given?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# HTML special characters to test.
|
116
|
+
HTML_TEST_STRING = TestString.build("'\"= /><")
|
117
|
+
|
118
|
+
#
|
119
|
+
# Tests which HTML characters are accepted or escaped/filtered.
|
120
|
+
#
|
121
|
+
# @yield [body, match]
|
122
|
+
# If a block is given, it will be passed the response body and the
|
123
|
+
# regular expression match data, if the response contains the test
|
124
|
+
# string.
|
125
|
+
#
|
126
|
+
# @yieldparam [String] body
|
127
|
+
# The response body.
|
128
|
+
#
|
129
|
+
# @yieldparam [MatchData] match
|
130
|
+
# The matched data for the test string.
|
131
|
+
#
|
132
|
+
# @api private
|
133
|
+
#
|
134
|
+
def test_html_chars(&block)
|
135
|
+
test_chars(HTML_TEST_STRING,&block)
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Tests whether the URL is vulnerable to (Reflected) Cross Site Scripting
|
140
|
+
# (XSS).
|
141
|
+
#
|
142
|
+
# @return [Boolean]
|
143
|
+
# Indicates whether the URL is vulnerable to (Reflected) Cross Site
|
144
|
+
# Scripting (XSS).
|
145
|
+
#
|
146
|
+
# @note
|
147
|
+
# If the URL is vulnerable, {#allowed_chars} and {#context} will be set.
|
148
|
+
#
|
149
|
+
def vulnerable?
|
150
|
+
# test HTML special characters
|
151
|
+
test_html_chars do |body,match|
|
152
|
+
xss_index = match.begin(0)
|
153
|
+
|
154
|
+
# determine the contents which the XSS occurs
|
155
|
+
if (@context = Context.identify(body,xss_index))
|
156
|
+
# determine whether enough special HTML characters are allowed to
|
157
|
+
# escape the context which the XSS occurs.
|
158
|
+
return @context.viable?(@allowed_chars)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
return false
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# Returns the type or kind of vulnerability.
|
167
|
+
#
|
168
|
+
# @return [Symbol]
|
169
|
+
#
|
170
|
+
# @note
|
171
|
+
# This is used internally to map an vulnerability class to a printable
|
172
|
+
# type.
|
173
|
+
#
|
174
|
+
# @api private
|
175
|
+
#
|
176
|
+
# @abstract
|
177
|
+
#
|
178
|
+
def self.vuln_type
|
179
|
+
:reflected_xss
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-vulns - A Ruby library for blind vulnerability testing.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-vulns is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-vulns is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-vulns. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/vulns/web_vuln'
|
22
|
+
require 'ronin/vulns/version'
|
23
|
+
|
24
|
+
require 'ronin/support/network/http'
|
25
|
+
require 'uri/query_params'
|
26
|
+
|
27
|
+
module Ronin
|
28
|
+
module Vulns
|
29
|
+
#
|
30
|
+
# Represents a Remote File Inclusion (RFI) vulnerability.
|
31
|
+
#
|
32
|
+
class RFI < WebVuln
|
33
|
+
|
34
|
+
# The script extensions and their languages
|
35
|
+
URL_EXTS = {
|
36
|
+
'.asp' => :asp,
|
37
|
+
'.aspx' => :asp_net,
|
38
|
+
'.cfm' => :cold_fusion,
|
39
|
+
'.cfml' => :cold_fusion,
|
40
|
+
'.jsp' => :jsp,
|
41
|
+
'.php' => :php,
|
42
|
+
'.pl' => :perl
|
43
|
+
}
|
44
|
+
|
45
|
+
# The github.com base URL for all RFI test scripts.
|
46
|
+
GITHUB_BASE_URL = "https://raw.githubusercontent.com/ronin-rb/ronin-vulns/#{VERSION}/data"
|
47
|
+
|
48
|
+
# Mapping of scripting languages to RFI test scripts.
|
49
|
+
TEST_SCRIPT_URLS = {
|
50
|
+
asp: "#{GITHUB_BASE_URL}/rfi_test.asp",
|
51
|
+
asp_net: "#{GITHUB_BASE_URL}/rfi_test.aspx",
|
52
|
+
cold_fusion: "#{GITHUB_BASE_URL}/rfi_test.cfm",
|
53
|
+
jsp: "#{GITHUB_BASE_URL}/rfi_test.jsp",
|
54
|
+
php: "#{GITHUB_BASE_URL}/rfi_test.php",
|
55
|
+
perl: "#{GITHUB_BASE_URL}/rfi_test.pl"
|
56
|
+
}
|
57
|
+
|
58
|
+
# The string that will be returned if the Remote File Inclusion (RFI)
|
59
|
+
# script is executed.
|
60
|
+
VULN_RESPONSE_STRING = "Security Alert: Remote File Inclusion Detected!"
|
61
|
+
|
62
|
+
# The filter bypass technique to use.
|
63
|
+
#
|
64
|
+
# @return [nil, :double_encode, :suffix_escape, :null_byte]
|
65
|
+
attr_reader :filter_bypass
|
66
|
+
|
67
|
+
# URL of the Remote File Inclusion (RFI) Test script
|
68
|
+
#
|
69
|
+
# @return [URI::HTTP, String]
|
70
|
+
attr_reader :test_script_url
|
71
|
+
|
72
|
+
#
|
73
|
+
# Creates a new Remote File Inclusion (RFI) object.
|
74
|
+
#
|
75
|
+
# @param [String, URI::HTTP] url
|
76
|
+
# The URL to attempt to exploit.
|
77
|
+
#
|
78
|
+
# @param [:null_byte, :double_encode, nil] filter_bypass
|
79
|
+
# Specifies which filter bypass technique to use.
|
80
|
+
# * `:double_encode` - will cause the inclusion URL to be URI escaped
|
81
|
+
# twice.
|
82
|
+
# * `:suffix_escape` - escape any appended suffix (ex: `param + ".php"`)
|
83
|
+
# by adding a URI fragment character (`#`) to the end of the RFI
|
84
|
+
# script URL. The fragment component of the URI is not sent to the
|
85
|
+
# web server.
|
86
|
+
# * `:null_byte` - will cause the inclusion URL to be appended with a
|
87
|
+
# `%00` character. **Note:* this technique only works on PHP < 5.3.
|
88
|
+
#
|
89
|
+
# @param [:asp, :asp_net, :cold_fusion, :jsp, :php, :perl, nil] script_lang
|
90
|
+
# Explicitly specifies the scripting language that the URL uses.
|
91
|
+
#
|
92
|
+
# @param [String, URI::HTTP, nil] test_script_url
|
93
|
+
# The URL of the RFI test script. If not specified, it will default to
|
94
|
+
# {test_script_for}.
|
95
|
+
#
|
96
|
+
def initialize(url, script_lang: nil, test_script_url: nil, filter_bypass: nil, **kwargs)
|
97
|
+
super(url,**kwargs)
|
98
|
+
|
99
|
+
@test_script_url = if test_script_url
|
100
|
+
test_script_url
|
101
|
+
elsif script_lang
|
102
|
+
self.class.test_script_url_for(script_lang)
|
103
|
+
else
|
104
|
+
self.class.test_script_for(@url)
|
105
|
+
end
|
106
|
+
|
107
|
+
@filter_bypass = filter_bypass
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Returns the test script URL for the given scripting language.
|
112
|
+
#
|
113
|
+
# @param [:asp, :asp_net, :cold_fusion, :jsp, :php, :perl] script_lang
|
114
|
+
# The scripting language.
|
115
|
+
#
|
116
|
+
# @return [String]
|
117
|
+
# The test script URL for the given scripting language.
|
118
|
+
#
|
119
|
+
# @raise [ArgumentError]
|
120
|
+
# An unknown scripting language value was given.
|
121
|
+
#
|
122
|
+
def self.test_script_url_for(script_lang)
|
123
|
+
TEST_SCRIPT_URLS.fetch(script_lang) do
|
124
|
+
raise(ArgumentError,"unknown scripting language: #{script_lang.inspect}")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Attempts to infer the programming language used for the web page at the
|
130
|
+
# given URL.
|
131
|
+
#
|
132
|
+
# @param [String, URI::HTTP] url
|
133
|
+
# The URL to infer from.
|
134
|
+
#
|
135
|
+
# @return [:asp, :cold_fusion, :jsp, :php, :perl, nil]
|
136
|
+
# The programming language inferred from the URL.
|
137
|
+
#
|
138
|
+
def self.infer_script_lang(url)
|
139
|
+
url = URI(url)
|
140
|
+
|
141
|
+
return URL_EXTS[File.extname(url.path)]
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# Selects the RFI test script for the scripting language used by the given
|
146
|
+
# URL.
|
147
|
+
#
|
148
|
+
# @param [String, URI::HTTP] url
|
149
|
+
# The URL to test.
|
150
|
+
#
|
151
|
+
# @return [String, nil]
|
152
|
+
# The RFI test script URL or `nil` if the scripting language could not
|
153
|
+
# be inferred from the URL.
|
154
|
+
#
|
155
|
+
def self.test_script_for(url)
|
156
|
+
if (lang = infer_script_lang(url))
|
157
|
+
TEST_SCRIPT_URLS.fetch(lang)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Optionally applies a filter bypass technique to the RFI URL.
|
163
|
+
#
|
164
|
+
# @param [URI::HTTP, String] url
|
165
|
+
# The RFI URL to optionall encode before it will be injected into a
|
166
|
+
# HTTP request.
|
167
|
+
#
|
168
|
+
# @return [String]
|
169
|
+
# The optionally encoded RFI URL.
|
170
|
+
#
|
171
|
+
def encode_payload(url)
|
172
|
+
url = url.to_s
|
173
|
+
|
174
|
+
case @filter_bypass
|
175
|
+
when :double_encode
|
176
|
+
# Optionally double URI encodes the script URL
|
177
|
+
url = URI::QueryParams.escape(url)
|
178
|
+
when :suffix_escape
|
179
|
+
# Optionally append a '#' character to escape any appended suffixes
|
180
|
+
# (ex: `param + ".php"`).
|
181
|
+
url = "#{url}#"
|
182
|
+
when :null_byte
|
183
|
+
# Optionally append a null-byte
|
184
|
+
# NOTE: uri-query_params will automatically URI encode the null byte
|
185
|
+
url = "#{url}\0"
|
186
|
+
end
|
187
|
+
|
188
|
+
return url
|
189
|
+
end
|
190
|
+
|
191
|
+
#
|
192
|
+
# Tests whether the URL and query parameter are vulnerable to Remote File
|
193
|
+
# Inclusion (RFI).
|
194
|
+
#
|
195
|
+
# @return [Boolean]
|
196
|
+
# Specifies whether the URL and query parameter are vulnerable to RFI.
|
197
|
+
#
|
198
|
+
def vulnerable?
|
199
|
+
response = exploit(@test_script_url)
|
200
|
+
body = response.body
|
201
|
+
|
202
|
+
return body.include?(VULN_RESPONSE_STRING)
|
203
|
+
end
|
204
|
+
|
205
|
+
#
|
206
|
+
# Returns the type or kind of vulnerability.
|
207
|
+
#
|
208
|
+
# @return [Symbol]
|
209
|
+
#
|
210
|
+
# @note
|
211
|
+
# This is used internally to map an vulnerability class to a printable
|
212
|
+
# type.
|
213
|
+
#
|
214
|
+
# @api private
|
215
|
+
#
|
216
|
+
# @abstract
|
217
|
+
#
|
218
|
+
def self.vuln_type
|
219
|
+
:rfi
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-vulns - A Ruby library for blind vulnerability testing.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-vulns is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-vulns is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-vulns. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
module Ronin
|
22
|
+
module Vulns
|
23
|
+
# Path to `ronin-vulns` root directory.
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
ROOT = File.expand_path(File.join(__dir__,'..','..','..'))
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-vulns - A Ruby library for blind vulnerability testing.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
|
+
#
|
7
|
+
# ronin-vulns is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# ronin-vulns is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with ronin-vulns. If not, see <https://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'ronin/vulns/web_vuln'
|
22
|
+
|
23
|
+
module Ronin
|
24
|
+
module Vulns
|
25
|
+
class SQLI < WebVuln
|
26
|
+
#
|
27
|
+
# Represents a collection of patterns for SQL error messages for a
|
28
|
+
# particular database.
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
class ErrorPattern
|
33
|
+
|
34
|
+
# The combined error message regexp.
|
35
|
+
#
|
36
|
+
# @return [Regexp]
|
37
|
+
attr_reader :regexp
|
38
|
+
|
39
|
+
#
|
40
|
+
# Initializes the error pattern.
|
41
|
+
#
|
42
|
+
# @param [Regexp] regexp
|
43
|
+
# The combined of regular expression.
|
44
|
+
#
|
45
|
+
def initialize(regexp)
|
46
|
+
@regexp = regexp
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Creates an error pattern from multiple different regexps.
|
51
|
+
#
|
52
|
+
# @param [Array<Regexp>] regexps
|
53
|
+
# The collection of regular expressions.
|
54
|
+
#
|
55
|
+
def self.[](*regexps)
|
56
|
+
new(Regexp.union(regexps))
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Tests whether the respones body contains a SQL error.
|
61
|
+
#
|
62
|
+
# @param [String] response_body
|
63
|
+
# The HTTP response body.
|
64
|
+
#
|
65
|
+
# @return [MatchData, nil]
|
66
|
+
# The match data if the {#regexp} is found within the response body.
|
67
|
+
#
|
68
|
+
def match(response_body)
|
69
|
+
@regexp.match(response_body)
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Tests whether the file was successfully included into the response
|
74
|
+
# body.
|
75
|
+
#
|
76
|
+
# @param [String] response_body
|
77
|
+
# The HTTP response body.
|
78
|
+
#
|
79
|
+
# @return [Integer, nil]
|
80
|
+
# Indicates whether the {#regexp} was found in the response body.
|
81
|
+
#
|
82
|
+
def =~(response_body)
|
83
|
+
response_body =~ @regexp
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|