ronin-vulns 0.1.5 → 0.2.0.rc1
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 +4 -4
- data/.gitignore +1 -0
- data/ChangeLog.md +43 -0
- data/Gemfile +14 -4
- data/README.md +7 -3
- data/Rakefile +9 -0
- data/data/completions/ronin-vulns +139 -0
- data/gemspec.yml +7 -1
- data/lib/ronin/vulns/cli/command.rb +1 -1
- data/lib/ronin/vulns/cli/commands/command_injection.rb +163 -0
- data/lib/ronin/vulns/cli/commands/completion.rb +63 -0
- data/lib/ronin/vulns/cli/commands/irb.rb +59 -0
- data/lib/ronin/vulns/cli/commands/lfi.rb +21 -9
- data/lib/ronin/vulns/cli/commands/open_redirect.rb +13 -1
- data/lib/ronin/vulns/cli/commands/reflected_xss.rb +13 -1
- data/lib/ronin/vulns/cli/commands/rfi.rb +13 -1
- data/lib/ronin/vulns/cli/commands/scan.rb +21 -9
- data/lib/ronin/vulns/cli/commands/sqli.rb +13 -1
- data/lib/ronin/vulns/cli/commands/ssti.rb +13 -1
- data/lib/ronin/vulns/cli/importable.rb +76 -0
- data/lib/ronin/vulns/cli/printing.rb +184 -0
- data/lib/ronin/vulns/cli/ruby_shell.rb +53 -0
- data/lib/ronin/vulns/cli/web_vuln_command.rb +216 -20
- data/lib/ronin/vulns/cli.rb +3 -2
- data/lib/ronin/vulns/command_injection.rb +267 -0
- data/lib/ronin/vulns/importer.rb +116 -0
- data/lib/ronin/vulns/lfi/test_file.rb +1 -1
- data/lib/ronin/vulns/lfi.rb +1 -1
- data/lib/ronin/vulns/open_redirect.rb +1 -1
- data/lib/ronin/vulns/reflected_xss/context.rb +1 -1
- data/lib/ronin/vulns/reflected_xss/test_string.rb +1 -1
- data/lib/ronin/vulns/reflected_xss.rb +1 -1
- data/lib/ronin/vulns/rfi.rb +64 -9
- data/lib/ronin/vulns/root.rb +1 -1
- data/lib/ronin/vulns/sqli/error_pattern.rb +1 -1
- data/lib/ronin/vulns/sqli.rb +36 -28
- data/lib/ronin/vulns/ssti/test_expression.rb +1 -1
- data/lib/ronin/vulns/ssti.rb +69 -53
- data/lib/ronin/vulns/url_scanner.rb +10 -1
- data/lib/ronin/vulns/version.rb +2 -2
- data/lib/ronin/vulns/vuln.rb +1 -1
- data/lib/ronin/vulns/web_vuln/http_request.rb +40 -1
- data/lib/ronin/vulns/web_vuln.rb +86 -16
- data/man/ronin-vulns-command-injection.1 +109 -0
- data/man/ronin-vulns-command-injection.1.md +112 -0
- data/man/ronin-vulns-completion.1 +76 -0
- data/man/ronin-vulns-completion.1.md +78 -0
- data/man/ronin-vulns-irb.1 +27 -0
- data/man/ronin-vulns-irb.1.md +26 -0
- data/man/ronin-vulns-lfi.1 +54 -51
- data/man/ronin-vulns-lfi.1.md +52 -20
- data/man/ronin-vulns-open-redirect.1 +51 -47
- data/man/ronin-vulns-open-redirect.1.md +50 -18
- data/man/ronin-vulns-reflected-xss.1 +50 -45
- data/man/ronin-vulns-reflected-xss.1.md +49 -17
- data/man/ronin-vulns-rfi.1 +54 -52
- data/man/ronin-vulns-rfi.1.md +52 -20
- data/man/ronin-vulns-scan.1 +68 -69
- data/man/ronin-vulns-scan.1.md +61 -29
- data/man/ronin-vulns-sqli.1 +54 -52
- data/man/ronin-vulns-sqli.1.md +52 -20
- data/man/ronin-vulns-ssti.1 +52 -48
- data/man/ronin-vulns-ssti.1.md +50 -18
- data/man/ronin-vulns.1 +73 -0
- data/man/ronin-vulns.1.md +69 -0
- data/scripts/setup +58 -0
- metadata +36 -5
- data/lib/ronin/vulns/cli/logging.rb +0 -81
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# ronin-vulns - A Ruby library for blind vulnerability testing.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2022-
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
6
|
#
|
7
7
|
# ronin-vulns is free software: you can redistribute it and/or modify
|
8
8
|
# it under the terms of the GNU Lesser General Public License as published
|
@@ -19,9 +19,11 @@
|
|
19
19
|
#
|
20
20
|
|
21
21
|
require 'ronin/vulns/cli/command'
|
22
|
-
require 'ronin/vulns/cli/
|
22
|
+
require 'ronin/vulns/cli/importable'
|
23
|
+
require 'ronin/vulns/cli/printing'
|
23
24
|
|
24
25
|
require 'ronin/support/network/http/cookie'
|
26
|
+
require 'ronin/support/network/http/user_agents'
|
25
27
|
|
26
28
|
require 'set'
|
27
29
|
|
@@ -33,8 +35,10 @@ module Ronin
|
|
33
35
|
#
|
34
36
|
class WebVulnCommand < Command
|
35
37
|
|
36
|
-
include
|
38
|
+
include Printing
|
39
|
+
include Importable
|
37
40
|
|
41
|
+
option :import, desc: 'Imports discovered vulnerabilities into the database'
|
38
42
|
option :first, short: '-F',
|
39
43
|
desc: 'Only find the first vulnerability for each URL' do
|
40
44
|
@scan_mode = :first
|
@@ -45,6 +49,34 @@ module Ronin
|
|
45
49
|
@scan_mode = :all
|
46
50
|
end
|
47
51
|
|
52
|
+
option :print_curl, desc: 'Also prints an example curl command for each vulnerability'
|
53
|
+
|
54
|
+
option :print_http, desc: 'Also prints an example HTTP request for each vulnerability'
|
55
|
+
|
56
|
+
option :request_method, short: '-M',
|
57
|
+
value: {
|
58
|
+
type: {
|
59
|
+
'COPY' => :copy,
|
60
|
+
'DELETE' => :delete,
|
61
|
+
'GET' => :get,
|
62
|
+
'HEAD' => :head,
|
63
|
+
'LOCK' => :lock,
|
64
|
+
'MKCOL' => :mkcol,
|
65
|
+
'MOVE' => :move,
|
66
|
+
'OPTIONS' => :options,
|
67
|
+
'PATCH' => :patch,
|
68
|
+
'POST' => :post,
|
69
|
+
'PROPFIND' => :propfind,
|
70
|
+
'PROPPATCH' => :proppatch,
|
71
|
+
'PUT' => :put,
|
72
|
+
'TRACE' => :trace,
|
73
|
+
'UNLOCK' => :unlock
|
74
|
+
}
|
75
|
+
},
|
76
|
+
desc: 'The HTTP request method to use' do |verb|
|
77
|
+
self.request_method = verb
|
78
|
+
end
|
79
|
+
|
48
80
|
option :header, short: '-H',
|
49
81
|
value: {
|
50
82
|
type: /[A-Za-z0-9-]+:\s*\w+/,
|
@@ -56,6 +88,25 @@ module Ronin
|
|
56
88
|
self.headers[name] = value
|
57
89
|
end
|
58
90
|
|
91
|
+
option :user_agent_string, short: '-U',
|
92
|
+
value: {
|
93
|
+
type: String,
|
94
|
+
usage: 'STRING'
|
95
|
+
},
|
96
|
+
desc: 'Sets the User-Agent header' do |ua|
|
97
|
+
self.user_agent = ua
|
98
|
+
end
|
99
|
+
|
100
|
+
option :user_agent, short: '-u',
|
101
|
+
value: {
|
102
|
+
type: Support::Network::HTTP::UserAgents::ALIASES.transform_keys { |key|
|
103
|
+
key.to_s.tr('_','-')
|
104
|
+
}
|
105
|
+
},
|
106
|
+
desc: 'Sets the User-Agent to use' do |name|
|
107
|
+
self.user_agent = name
|
108
|
+
end
|
109
|
+
|
59
110
|
option :cookie, short: '-C',
|
60
111
|
value: {
|
61
112
|
type: String,
|
@@ -148,6 +199,10 @@ module Ronin
|
|
148
199
|
self.test_form_params << name
|
149
200
|
end
|
150
201
|
|
202
|
+
option :test_all_form_params, desc: 'Tests all form param names' do
|
203
|
+
self.test_form_params = true
|
204
|
+
end
|
205
|
+
|
151
206
|
option :input, short: '-i',
|
152
207
|
value: {
|
153
208
|
type: String,
|
@@ -197,23 +252,68 @@ module Ronin
|
|
197
252
|
exit(-1)
|
198
253
|
end
|
199
254
|
|
200
|
-
|
255
|
+
db_connect if options[:import]
|
256
|
+
|
257
|
+
vulns = []
|
201
258
|
|
202
259
|
if options[:input]
|
203
260
|
File.open(options[:input]) do |file|
|
204
261
|
file.each_line(chomp: true) do |url|
|
205
|
-
|
262
|
+
process_url(url) do |vuln|
|
263
|
+
vulns << vuln
|
264
|
+
end
|
206
265
|
end
|
207
266
|
end
|
208
267
|
elsif !urls.empty?
|
209
268
|
urls.each do |url|
|
210
|
-
|
269
|
+
process_url(url) do |vuln|
|
270
|
+
vulns << vuln
|
271
|
+
end
|
211
272
|
end
|
212
273
|
end
|
213
274
|
|
214
|
-
unless
|
215
|
-
|
216
|
-
|
275
|
+
puts unless vulns.empty?
|
276
|
+
print_vulns(vulns)
|
277
|
+
end
|
278
|
+
|
279
|
+
#
|
280
|
+
# Print a summary of all web vulnerabilities found.
|
281
|
+
#
|
282
|
+
# @param [Array<WebVuln>] vulns
|
283
|
+
# The discovered web vulnerabilities.
|
284
|
+
#
|
285
|
+
# @param [Boolean] print_curl
|
286
|
+
# Prints an example `curl` command to trigger the web vulnerability.
|
287
|
+
#
|
288
|
+
# @param [Boolean] print_http
|
289
|
+
# Prints an example HTTP request to trigger the web vulnerability.
|
290
|
+
#
|
291
|
+
# @since 0.2.0
|
292
|
+
#
|
293
|
+
def print_vulns(vulns, print_curl: options[:print_curl],
|
294
|
+
print_http: options[:print_http])
|
295
|
+
super(vulns, print_curl: print_curl,
|
296
|
+
print_http: print_http)
|
297
|
+
end
|
298
|
+
|
299
|
+
#
|
300
|
+
# Prints detailed information about a discovered web vulnerability.
|
301
|
+
#
|
302
|
+
# @param [WebVuln] vuln
|
303
|
+
# The web vulnerability to log.
|
304
|
+
#
|
305
|
+
# @param [Boolean] print_curl
|
306
|
+
# Prints an example `curl` command to trigger the web vulnerability.
|
307
|
+
#
|
308
|
+
# @param [Boolean] print_http
|
309
|
+
# Prints an example HTTP request to trigger the web vulnerability.
|
310
|
+
#
|
311
|
+
# @since 0.2.0
|
312
|
+
#
|
313
|
+
def print_vuln(vuln, print_curl: options[:print_curl],
|
314
|
+
print_http: options[:print_http])
|
315
|
+
super(vuln, print_curl: print_curl,
|
316
|
+
print_http: print_http)
|
217
317
|
end
|
218
318
|
|
219
319
|
#
|
@@ -222,8 +322,12 @@ module Ronin
|
|
222
322
|
# @param [String] url
|
223
323
|
# A URL to scan.
|
224
324
|
#
|
225
|
-
# @
|
226
|
-
#
|
325
|
+
# @yield [vuln]
|
326
|
+
# The given block will be passed each newly discovered web
|
327
|
+
# vulnerability.
|
328
|
+
#
|
329
|
+
# @yieldparam [WebVuln] vuln
|
330
|
+
# A newly discovered web vulnerability.
|
227
331
|
#
|
228
332
|
def process_url(url)
|
229
333
|
unless url.start_with?('http://') || url.start_with?('https://')
|
@@ -231,23 +335,60 @@ module Ronin
|
|
231
335
|
exit(-1)
|
232
336
|
end
|
233
337
|
|
234
|
-
vuln_discovered = false
|
235
|
-
|
236
338
|
if @scan_mode == :first
|
237
339
|
if (first_vuln = test_url(url))
|
238
|
-
|
239
|
-
|
240
|
-
vuln_discovered = true
|
340
|
+
process_vuln(first_vuln)
|
341
|
+
yield first_vuln
|
241
342
|
end
|
242
343
|
else
|
243
344
|
scan_url(url) do |vuln|
|
244
|
-
|
245
|
-
|
246
|
-
vuln_discovered = true
|
345
|
+
process_vuln(vuln)
|
346
|
+
yield vuln
|
247
347
|
end
|
248
348
|
end
|
349
|
+
end
|
249
350
|
|
250
|
-
|
351
|
+
#
|
352
|
+
# Logs and optioanlly imports a new discovered web vulnerability.
|
353
|
+
#
|
354
|
+
# @param [WebVuln] vuln
|
355
|
+
# The discovered web vulnerability.
|
356
|
+
#
|
357
|
+
# @since 0.2.0
|
358
|
+
#
|
359
|
+
def process_vuln(vuln)
|
360
|
+
log_vuln(vuln)
|
361
|
+
import_vuln(vuln) if options[:import]
|
362
|
+
end
|
363
|
+
|
364
|
+
#
|
365
|
+
# The HTTP request method to use.
|
366
|
+
#
|
367
|
+
# @return [:copy, :delete, :get, :head, :lock, :mkcol, :move,
|
368
|
+
# :options, :patch, :post, :propfind, :proppatch, :put,
|
369
|
+
# :trace, :unlock]
|
370
|
+
#
|
371
|
+
# @since 0.2.0
|
372
|
+
#
|
373
|
+
def request_method
|
374
|
+
@scan_kwargs[:request_method]
|
375
|
+
end
|
376
|
+
|
377
|
+
#
|
378
|
+
# Sets the HTTP request method to use.
|
379
|
+
#
|
380
|
+
# @param [:copy, :delete, :get, :head, :lock, :mkcol, :move,
|
381
|
+
# :options, :patch, :post, :propfind, :proppatch, :put,
|
382
|
+
# :trace, :unlock] new_request_method
|
383
|
+
#
|
384
|
+
# @return [:copy, :delete, :get, :head, :lock, :mkcol, :move,
|
385
|
+
# :options, :patch, :post, :propfind, :proppatch, :put,
|
386
|
+
# :trace, :unlock]
|
387
|
+
#
|
388
|
+
# @since 0.2.0
|
389
|
+
#
|
390
|
+
def request_method=(new_request_method)
|
391
|
+
@scan_kwargs[:request_method] = new_request_method
|
251
392
|
end
|
252
393
|
|
253
394
|
#
|
@@ -259,6 +400,49 @@ module Ronin
|
|
259
400
|
@scan_kwargs[:headers] ||= {}
|
260
401
|
end
|
261
402
|
|
403
|
+
#
|
404
|
+
# The optional HTTP `User-Agent` header to send.
|
405
|
+
#
|
406
|
+
# @return [String, :random, :chrome, :chrome_linux, :chrome_macos,
|
407
|
+
# :chrome_windows, :chrome_iphone, :chrome_ipad,
|
408
|
+
# :chrome_android, :firefox, :firefox_linux, :firefox_macos,
|
409
|
+
# :firefox_windows, :firefox_iphone, :firefox_ipad,
|
410
|
+
# :firefox_android, :safari, :safari_macos, :safari_iphone,
|
411
|
+
# :safari_ipad, :edge, :linux, :macos, :windows, :iphone,
|
412
|
+
# :ipad, :android, nil]
|
413
|
+
#
|
414
|
+
# @since 0.2.0
|
415
|
+
#
|
416
|
+
def user_agent
|
417
|
+
@scan_kwargs[:user_agent]
|
418
|
+
end
|
419
|
+
|
420
|
+
#
|
421
|
+
# Sets the HTTP `User-Agent` header.
|
422
|
+
#
|
423
|
+
# @param [String, :random, :chrome, :chrome_linux, :chrome_macos,
|
424
|
+
# :chrome_windows, :chrome_iphone, :chrome_ipad,
|
425
|
+
# :chrome_android, :firefox, :firefox_linux, :firefox_macos,
|
426
|
+
# :firefox_windows, :firefox_iphone, :firefox_ipad,
|
427
|
+
# :firefox_android, :safari, :safari_macos, :safari_iphone,
|
428
|
+
# :safari_ipad, :edge, :linux, :macos, :windows, :iphone,
|
429
|
+
# :ipad, :android] new_user_agent
|
430
|
+
# The new `User-Agent` value to send.
|
431
|
+
#
|
432
|
+
# @return [String, :random, :chrome, :chrome_linux, :chrome_macos,
|
433
|
+
# :chrome_windows, :chrome_iphone, :chrome_ipad,
|
434
|
+
# :chrome_android, :firefox, :firefox_linux, :firefox_macos,
|
435
|
+
# :firefox_windows, :firefox_iphone, :firefox_ipad,
|
436
|
+
# :firefox_android, :safari, :safari_macos, :safari_iphone,
|
437
|
+
# :safari_ipad, :edge, :linux, :macos, :windows, :iphone,
|
438
|
+
# :ipad, :android]
|
439
|
+
#
|
440
|
+
# @since 0.2.0
|
441
|
+
#
|
442
|
+
def user_agent=(new_user_agent)
|
443
|
+
@scan_kwargs[:user_agent] = new_user_agent
|
444
|
+
end
|
445
|
+
|
262
446
|
#
|
263
447
|
# The optional `Cookie` header to send.
|
264
448
|
#
|
@@ -358,6 +542,18 @@ module Ronin
|
|
358
542
|
@scan_kwargs[:form_params] ||= Set.new
|
359
543
|
end
|
360
544
|
|
545
|
+
#
|
546
|
+
# Sets the form params to test.
|
547
|
+
#
|
548
|
+
# @param [Set<String>, true] new_form_params
|
549
|
+
# The new form param names to test.
|
550
|
+
#
|
551
|
+
# @return [Set<String>, true]
|
552
|
+
#
|
553
|
+
def test_form_params=(new_form_params)
|
554
|
+
@scan_kwargs[:form_params] = new_form_params
|
555
|
+
end
|
556
|
+
|
361
557
|
#
|
362
558
|
# Scans a URL for web vulnerabilities.
|
363
559
|
#
|
data/lib/ronin/vulns/cli.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# ronin-vulns - A Ruby library for blind vulnerability testing.
|
4
4
|
#
|
5
|
-
# Copyright (c) 2022-
|
5
|
+
# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3 at gmail.com)
|
6
6
|
#
|
7
7
|
# ronin-vulns is free software: you can redistribute it and/or modify
|
8
8
|
# it under the terms of the GNU Lesser General Public License as published
|
@@ -43,7 +43,8 @@ module Ronin
|
|
43
43
|
command_name 'ronin-vulns'
|
44
44
|
version Ronin::Vulns::VERSION
|
45
45
|
|
46
|
-
command_aliases['xss']
|
46
|
+
command_aliases['xss'] = 'reflected-xss'
|
47
|
+
command_aliases['cmdi'] = 'command-injection'
|
47
48
|
|
48
49
|
end
|
49
50
|
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# ronin-vulns - A Ruby library for blind vulnerability testing.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2022-2024 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
|
+
require 'time'
|
24
|
+
|
25
|
+
module Ronin
|
26
|
+
module Vulns
|
27
|
+
#
|
28
|
+
# Represents a Command Injection vulnerability.
|
29
|
+
#
|
30
|
+
# ## Features
|
31
|
+
#
|
32
|
+
# * Supports using `;`, `|`, `&`, and `\n` escape characters.
|
33
|
+
# * Supports escaping single and double-quoted strings.
|
34
|
+
# * Supports using `;`, `#`, and `\n` terminator characters.
|
35
|
+
#
|
36
|
+
# @since 0.2.0
|
37
|
+
#
|
38
|
+
class CommandInjection < WebVuln
|
39
|
+
|
40
|
+
# The character to use to escape a quoted string.
|
41
|
+
#
|
42
|
+
# @return [String, nil]
|
43
|
+
attr_reader :escape_quote
|
44
|
+
|
45
|
+
# The escape character or string to use to escape the command and execute
|
46
|
+
# another.
|
47
|
+
#
|
48
|
+
# @return [String]
|
49
|
+
attr_reader :escape_operator
|
50
|
+
|
51
|
+
# The terminator charactor to terminate the injected command with.
|
52
|
+
#
|
53
|
+
# @return [String, nil]
|
54
|
+
attr_reader :terminator
|
55
|
+
|
56
|
+
#
|
57
|
+
# Initializes the command injection vulnerability.
|
58
|
+
#
|
59
|
+
# @param [URI::HTTP, String] url
|
60
|
+
# The URL to test or exploit.
|
61
|
+
#
|
62
|
+
# @param [String, nil] escape_quote
|
63
|
+
# The optional character to use to escape a quoted string.
|
64
|
+
#
|
65
|
+
# @param [String] escape_operator
|
66
|
+
# The escape character or string to use to escape the command
|
67
|
+
# and execute another.
|
68
|
+
#
|
69
|
+
# @param [String, nil] terminator
|
70
|
+
# The optional terminator character to terminate the injected command
|
71
|
+
# with.
|
72
|
+
#
|
73
|
+
def initialize(url, escape_quote: nil,
|
74
|
+
escape_operator: nil,
|
75
|
+
terminator: nil,
|
76
|
+
**kwargs)
|
77
|
+
super(url,**kwargs)
|
78
|
+
|
79
|
+
@escape_quote = escape_quote
|
80
|
+
@escape_operator = escape_operator
|
81
|
+
@terminator = terminator
|
82
|
+
|
83
|
+
@escape_string = build_escape_string
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
#
|
89
|
+
# Builds the command escape String.
|
90
|
+
#
|
91
|
+
# @return [String, nil]
|
92
|
+
#
|
93
|
+
def build_escape_string
|
94
|
+
if @escape_quote || @escape_operator
|
95
|
+
"#{original_value}#{@escape_quote}#{@escape_operator}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
public
|
100
|
+
|
101
|
+
#
|
102
|
+
# Scans the URL for command injections.
|
103
|
+
#
|
104
|
+
# Tests the URL and a specific query param, header name, cookie param, or
|
105
|
+
# form param for Command Injection by enumerating over various Command
|
106
|
+
# Injection escape syntaxes.
|
107
|
+
#
|
108
|
+
# @param [URI::HTTP] url
|
109
|
+
# The URL to test.
|
110
|
+
#
|
111
|
+
# @param [Array<String, nil>, String, nil] escape_quote
|
112
|
+
# The optional escape quote character(s) to test.
|
113
|
+
#
|
114
|
+
# @param [Array<String, nil>, String, nil] escape_operator
|
115
|
+
# The optional escape operator character(s) to test.
|
116
|
+
#
|
117
|
+
# @param [Array<String, nil>, Stirng, nil] terminator
|
118
|
+
# The optional command termination character(s) to test.
|
119
|
+
#
|
120
|
+
# @param [Ronin::Support::Network::HTTP, nil] http
|
121
|
+
# An HTTP session to use for testing the URL.
|
122
|
+
#
|
123
|
+
# @param [Hash{Symbol => Object}] kwargs
|
124
|
+
# Additional keyword arguments for {#initialize}.
|
125
|
+
#
|
126
|
+
# @option kwargs [Symbol, String, true, nil] :query_param
|
127
|
+
# The query param name to test.
|
128
|
+
#
|
129
|
+
# @option kwargs [Symbol, String, nil] :header_name
|
130
|
+
# The header name to test.
|
131
|
+
#
|
132
|
+
# @option kwargs [Symbol, String, true, nil] :cookie_param
|
133
|
+
# The cookie param name to test.
|
134
|
+
#
|
135
|
+
# @option kwargs [Symbol, String, nil] :form_param
|
136
|
+
# The form param name to test.
|
137
|
+
#
|
138
|
+
# @return [CommandInjection, nil]
|
139
|
+
# The first discovered Command Injection vulnerability for the specific
|
140
|
+
# query param, header name, cookie param, or form param.
|
141
|
+
#
|
142
|
+
# @api private
|
143
|
+
#
|
144
|
+
# @since 0.2.0
|
145
|
+
#
|
146
|
+
def self.test_param(url, escape_quote: [nil, "'", '"', '`'],
|
147
|
+
escape_operator: [';', '|', '&', "\n"],
|
148
|
+
terminator: [nil, ';', '#', "\n"],
|
149
|
+
# keyword arguments for initialize
|
150
|
+
http: , **kwargs)
|
151
|
+
Array(escape_quote).each do |escape_quote_char|
|
152
|
+
Array(escape_operator).each do |escape_operator_char|
|
153
|
+
Array(terminator).each do |terminator_char|
|
154
|
+
vuln = new(url, escape_quote: escape_quote_char,
|
155
|
+
escape_operator: escape_operator_char,
|
156
|
+
terminator: terminator_char,
|
157
|
+
http: http,
|
158
|
+
**kwargs)
|
159
|
+
|
160
|
+
return vuln if vuln.vulnerable?
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
return nil
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# Escapes the given SQL and turns it into a SQL injection.
|
170
|
+
#
|
171
|
+
# @param [#to_s] command
|
172
|
+
# The command to escape.
|
173
|
+
#
|
174
|
+
# @return [String]
|
175
|
+
# The escaped SQL expression.
|
176
|
+
#
|
177
|
+
def escape(command)
|
178
|
+
cmdi = "#{@escape_string}#{command}"
|
179
|
+
|
180
|
+
if @terminator
|
181
|
+
cmdi << @terminator
|
182
|
+
elsif (@escape_quote && cmdi.end_with?(@escape_quote))
|
183
|
+
cmdi.chop!
|
184
|
+
end
|
185
|
+
|
186
|
+
return cmdi
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# Encodes the command injection payload.
|
191
|
+
#
|
192
|
+
# @see #escape
|
193
|
+
#
|
194
|
+
def encode_payload(sql)
|
195
|
+
escape(sql)
|
196
|
+
end
|
197
|
+
|
198
|
+
#
|
199
|
+
# Tests whether the URL is vulnerable to command injection.
|
200
|
+
#
|
201
|
+
# @return [Boolean]
|
202
|
+
#
|
203
|
+
def vulnerable?
|
204
|
+
test_command_output || test_sleep
|
205
|
+
end
|
206
|
+
|
207
|
+
# Regular expression to match the output of the `id` command.
|
208
|
+
ID_OUTPUT_REGEX = /uid=\d+\([^\)]+\) gid=\d+\([^\)]+\) groups=\d+\([^\)]+\)/
|
209
|
+
|
210
|
+
#
|
211
|
+
# Tests whether the URL is vulnerable to command injection, by executing
|
212
|
+
# the `id` command and the output is included in the response body.
|
213
|
+
#
|
214
|
+
# @return [Boolean]
|
215
|
+
#
|
216
|
+
# @api private
|
217
|
+
#
|
218
|
+
def test_command_output
|
219
|
+
response = exploit('id')
|
220
|
+
|
221
|
+
if response.body =~ ID_OUTPUT_REGEX
|
222
|
+
return true
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
#
|
227
|
+
# Tests whether the URL is vulnerable to command injection, by calling the
|
228
|
+
# sleep command to see if it takes longer for the response to be
|
229
|
+
# returned.
|
230
|
+
#
|
231
|
+
# @return [Boolean]
|
232
|
+
#
|
233
|
+
# @api private
|
234
|
+
#
|
235
|
+
def test_sleep
|
236
|
+
start_time = Time.now
|
237
|
+
|
238
|
+
exploit("sleep 5")
|
239
|
+
|
240
|
+
stop_time = Time.now
|
241
|
+
delta = (stop_time - start_time)
|
242
|
+
|
243
|
+
# if the response took more than 5 seconds, our SQL sleep function
|
244
|
+
# probably worked.
|
245
|
+
return delta > 5.0
|
246
|
+
end
|
247
|
+
|
248
|
+
#
|
249
|
+
# Returns the type or kind of vulnerability.
|
250
|
+
#
|
251
|
+
# @return [Symbol]
|
252
|
+
#
|
253
|
+
# @note
|
254
|
+
# This is used internally to map an vulnerability class to a printable
|
255
|
+
# type.
|
256
|
+
#
|
257
|
+
# @api private
|
258
|
+
#
|
259
|
+
# @abstract
|
260
|
+
#
|
261
|
+
def self.vuln_type
|
262
|
+
:command_injection
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|