ronin-vulns 0.1.5 → 0.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|