├── .ruby-version
├── .rspec
├── .document
├── .yardopts
├── spec
├── spec_helper.rb
├── cli
│ ├── commands
│ │ ├── irb_spec.rb
│ │ ├── man_page_example.rb
│ │ ├── completion_spec.rb
│ │ ├── reflected_xss_spec.rb
│ │ ├── open_redirect_spec.rb
│ │ ├── ssti_spec.rb
│ │ ├── sqli_spec.rb
│ │ ├── command_injection_spec.rb
│ │ ├── lfi_spec.rb
│ │ └── rfi_spec.rb
│ ├── ruby_shell_spec.rb
│ └── command_spec.rb
├── root_spec.rb
├── lfi
│ └── test_file_spec.rb
├── sqli
│ └── error_pattern_spec.rb
├── ssti
│ └── test_expression_spec.rb
└── reflected_xss
│ └── test_string_spec.rb
├── .gitignore
├── bin
└── ronin-vulns
├── data
├── rfi_test.php
├── rfi_test.cfm
├── rfi_test.pl
├── rfi_test.jsp
├── rfi_test.aspx
└── rfi_test.asp
├── man
├── ronin-vulns-irb.1.md
├── ronin-vulns.1.md
├── ronin-vulns-completion.1.md
├── ronin-vulns-reflected-xss.1.md
├── ronin-vulns-open-redirect.1.md
├── ronin-vulns-ssti.1.md
├── ronin-vulns-sqli.1.md
├── ronin-vulns-lfi.1.md
├── ronin-vulns-command-injection.1.md
├── ronin-vulns-rfi.1.md
└── ronin-vulns-scan.1.md
├── .rubocop.yml
├── lib
└── ronin
│ ├── vulns
│ ├── version.rb
│ ├── root.rb
│ ├── cli
│ │ ├── command.rb
│ │ ├── ruby_shell.rb
│ │ ├── commands
│ │ │ ├── irb.rb
│ │ │ ├── completion.rb
│ │ │ ├── reflected_xss.rb
│ │ │ ├── open_redirect.rb
│ │ │ ├── ssti.rb
│ │ │ ├── sqli.rb
│ │ │ ├── lfi.rb
│ │ │ ├── command_injection.rb
│ │ │ └── rfi.rb
│ │ ├── importable.rb
│ │ └── printing.rb
│ ├── vuln.rb
│ ├── cli.rb
│ ├── sqli
│ │ └── error_pattern.rb
│ ├── lfi
│ │ └── test_file.rb
│ ├── ssti
│ │ └── test_expression.rb
│ ├── importer.rb
│ ├── reflected_xss
│ │ ├── test_string.rb
│ │ └── context.rb
│ ├── open_redirect.rb
│ ├── reflected_xss.rb
│ ├── ssti.rb
│ └── command_injection.rb
│ └── vulns.rb
├── Rakefile
├── scripts
└── setup
├── .github
└── workflows
│ └── ruby.yml
├── Gemfile
├── gemspec.yml
├── ronin-vulns.gemspec
├── ChangeLog.md
└── COPYING.txt
/.ruby-version:
--------------------------------------------------------------------------------
1 | ruby-3.3
2 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --colour --format documentation
2 |
--------------------------------------------------------------------------------
/.document:
--------------------------------------------------------------------------------
1 | lib/**/*.rb
2 | -
3 | ChangeLog.md
4 | COPYING.txt
5 |
--------------------------------------------------------------------------------
/.yardopts:
--------------------------------------------------------------------------------
1 | --markup markdown --title 'Ronin::Vulns Documentation' --protected
2 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rspec'
2 | require 'simplecov'
3 |
4 | SimpleCov.start
5 |
6 | RSpec.configure do |specs|
7 | specs.filter_run_excluding :network
8 | end
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /data/completions/ronin-vulns
3 | /doc
4 | /pkg
5 | /man/*.[1-9]
6 | /vendor/bundle
7 | /Gemfile.lock
8 | /.bundle
9 | /.yardoc
10 | .DS_Store
11 | *.db
12 | *.log
13 | *.swp
14 | *~
15 |
--------------------------------------------------------------------------------
/spec/cli/commands/irb_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/irb'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::Irb do
6 | include_examples "man_page"
7 | end
8 |
--------------------------------------------------------------------------------
/spec/root_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/root'
3 |
4 | describe 'Ronin::Vulns::ROOT' do
5 | subject { Ronin::Vulns::ROOT }
6 |
7 | it "must be a directory" do
8 | expect(File.directory?(subject)).to be(true)
9 | end
10 |
11 | it "must be the root directory" do
12 | expect(subject).to eq(File.expand_path(File.join(__dir__,'..')))
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/bin/ronin-vulns:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require 'rubygems'
5 |
6 | root = File.expand_path(File.join(File.dirname(__FILE__),'..'))
7 | if File.file?(File.join(root,'Gemfile.lock'))
8 | Dir.chdir(root) do
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
15 | end
16 |
17 | require 'ronin/vulns/cli'
18 | Ronin::Vulns::CLI.start
19 |
--------------------------------------------------------------------------------
/spec/cli/ruby_shell_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/ruby_shell'
3 |
4 | describe Ronin::Vulns::CLI::RubyShell do
5 | describe "#initialize" do
6 | it "must default #name to 'ronin-vulns'" do
7 | expect(subject.name).to eq('ronin-vulns')
8 | end
9 |
10 | it "must default context: to Ronin::Vulns" do
11 | expect(subject.context).to be_a(Object)
12 | expect(subject.context).to be_kind_of(Ronin::Vulns)
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/cli/commands/man_page_example.rb:
--------------------------------------------------------------------------------
1 | require 'rspec'
2 |
3 | RSpec.shared_examples_for "man_page" do
4 | describe "man_page" do
5 | subject { described_class }
6 |
7 | it "must define a man page" do
8 | expect(subject.man_page).to_not be(nil)
9 | end
10 |
11 | it "must map to a markdown man page" do
12 | man_page_md_path = File.join(subject.man_dir,"#{subject.man_page}.md")
13 |
14 | expect(File.file?(man_page_md_path)).to be(true)
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/data/rfi_test.php:
--------------------------------------------------------------------------------
1 |
4 | #rfi-security-alert {
5 | position: relative;
6 | margin: 25vh 25vw 25vh 25vw;
7 | padding: 5em;
8 | color: black;
9 | background-color: white;
10 | border: 4em solid red;
11 | z-index: 10000;
12 | }
13 | #rfi-security-alert p {
14 | text-align: center;
15 | font-weight: bold;
16 | font-size: 4em;
17 | }
18 |
19 | EOS . PHP_EOL;
20 |
21 | echo "
" . PHP_EOL;
22 | echo "
" . strrev("!detceteD )IFR( noisulcnI eliF etomeR :trelA ytiruceS") . "
" . PHP_EOL;
23 | echo "
" . PHP_EOL;
24 | ?>
25 |
--------------------------------------------------------------------------------
/data/rfi_test.cfm:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 |
22 | writeOutput(reverse("!detceteD )IFR( noisulcnI eliF etomeR :trelA ytiruceS"));
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/data/rfi_test.pl:
--------------------------------------------------------------------------------
1 | print <<'EOS';
2 |
18 | EOS
19 |
20 | my $reversed_security_alert = "!detceteD )IFR( noisulcnI eliF etomeR :trelA ytiruceS";
21 | my $security_alert = reverse($reversed_security_alert);
22 |
23 | print "", "\n";
24 | print "
", $security_alert, "
\n";
25 | print "
", "\n";
26 |
--------------------------------------------------------------------------------
/man/ronin-vulns-irb.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-irb 1 "2023-02-01" Ronin Vulns "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-irb - Starts an interactive Ruby shell with ronin-vulns loaded
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns irb` [*options*]
10 |
11 | ## DESCRIPTION
12 |
13 | Starts an interactive Ruby shell with `ronin/vulns` loaded.
14 |
15 | ## OPTIONS
16 |
17 | `-h`, `--help`
18 | : Print help information
19 |
20 | ## AUTHOR
21 |
22 | Postmodern
23 |
24 | ## SEE ALSO
25 |
26 | [ronin-vulns-workers](ronin-vulns-workers.1.md) [ronin-vulns-worker](ronin-vulns-worker.1.md) [ronin-vulns-run](ronin-vulns-run.1.md) [ronin-vulns-test](ronin-vulns-test.1.md)
27 |
--------------------------------------------------------------------------------
/data/rfi_test.jsp:
--------------------------------------------------------------------------------
1 | <%= "" %>
17 | <%= "" %>
18 | <%= "
" + new StringBuffer("!detceteD )IFR( noisulcnI eliF etomeR :trelA ytiruceS").reverse() + "
" %>
19 | <%= "
" %>
20 |
--------------------------------------------------------------------------------
/data/rfi_test.aspx:
--------------------------------------------------------------------------------
1 |
17 | @{
18 | string reversed_security_alert = "!detceteD )IFR( noisulcnI eliF etomeR :trelA ytiruceS";
19 | char[] security_alert_chars = reversed_security_alert.ToCharArray();
20 | Array.Reverse(security_alert_chars);
21 | string security_alert = new string(security_alert_chars);
22 | }
23 |
24 |
@security_alert
25 |
26 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
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'
29 |
--------------------------------------------------------------------------------
/spec/cli/command_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/command'
3 |
4 | describe Ronin::Vulns::CLI::Command do
5 | describe ".man_dir" do
6 | subject { described_class }
7 |
8 | it "must point to the 'man/' directory" do
9 | expect(subject.man_dir).to eq(File.expand_path(File.join(__dir__,'..','..','man')))
10 | end
11 |
12 | it "must exist" do
13 | expect(File.directory?(subject.man_dir)).to be(true)
14 | end
15 | end
16 |
17 | describe ".bug_report_url" do
18 | subject { described_class }
19 |
20 | it "must be 'https://github.com/ronin-rb/ronin-vulns/issues/new'" do
21 | expect(subject.bug_report_url).to eq('https://github.com/ronin-rb/ronin-vulns/issues/new')
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/cli/commands/completion_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/completion'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::Completion do
6 | it "must inherit from Ronin::Core::CLI::CompletionCommand" do
7 | expect(described_class).to be < Ronin::Core::CLI::CompletionCommand
8 | end
9 |
10 | it "must set completion_file" do
11 | expect(described_class.completion_file).to eq(
12 | File.join(Ronin::Vulns::ROOT,'data','completions','ronin-vulns')
13 | )
14 | end
15 |
16 | it "must set man_dir" do
17 | expect(described_class.man_dir).to_not be(nil)
18 | expect(File.directory?(described_class.man_dir)).to be(true)
19 | end
20 |
21 | include_examples "man_page"
22 | end
23 |
--------------------------------------------------------------------------------
/data/rfi_test.asp:
--------------------------------------------------------------------------------
1 | <%
2 | response.write("");
18 | response.write("");
19 | response.write(strReverse("!detceteD )IFR( noisulcnI eliF etomeR :trelA ytiruceS"));
20 | response.write("
");
21 | %>
22 |
--------------------------------------------------------------------------------
/spec/cli/commands/reflected_xss_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/reflected_xss'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::ReflectedXss do
6 | include_examples "man_page"
7 |
8 | let(:url) { 'https://example.com/page.php?id=1' }
9 |
10 | describe "#scan_url" do
11 | it "must call Ronin::Vulns::ReflectedXSS.scan with the URL and #scan_kwargs" do
12 | expect(Ronin::Vulns::ReflectedXSS).to receive(:scan).with(
13 | url, **subject.scan_kwargs
14 | )
15 |
16 | subject.scan_url(url)
17 | end
18 | end
19 |
20 | describe "#test_url" do
21 | it "must call Ronin::Vulns::ReflectedXSS.scan with the URL and #scan_kwargs" do
22 | expect(Ronin::Vulns::ReflectedXSS).to receive(:test).with(
23 | url, **subject.scan_kwargs
24 | )
25 |
26 | subject.test_url(url)
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | module Ronin
22 | module Vulns
23 | # The ronin-vulns version
24 | VERSION = '0.2.1'
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | begin
4 | require 'bundler'
5 | rescue LoadError => e
6 | warn e.message
7 | warn "Run `gem install bundler` to install Bundler"
8 | exit(-1)
9 | end
10 |
11 | begin
12 | Bundler.setup(:development)
13 | rescue Bundler::BundlerError => e
14 | warn e.message
15 | warn "Run `bundle install` to install missing gems"
16 | exit e.status_code
17 | end
18 |
19 | require 'rake'
20 |
21 | require 'rubygems/tasks'
22 | Gem::Tasks.new(sign: {checksum: true, pgp: true})
23 |
24 | require 'rspec/core/rake_task'
25 | RSpec::Core::RakeTask.new
26 | task :test => :spec
27 | task :default => :spec
28 |
29 | require 'yard'
30 | YARD::Rake::YardocTask.new
31 | task :docs => :yard
32 |
33 | require 'kramdown/man/task'
34 | Kramdown::Man::Task.new
35 |
36 | require 'command_kit/completion/task'
37 | CommandKit::Completion::Task.new(
38 | class_file: 'ronin/vulns/cli',
39 | class_name: 'Ronin::Vulns::CLI',
40 | output_file: 'data/completions/ronin-vulns'
41 | )
42 |
43 | task :setup => %w[man command_kit:completion]
44 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/root.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
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
29 |
--------------------------------------------------------------------------------
/scripts/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | #
4 | # Prints a log message.
5 | #
6 | function log()
7 | {
8 | if [[ -t 1 ]]; then
9 | echo -e "\x1b[1m\x1b[32m>>>\x1b[0m \x1b[1m$1\x1b[0m"
10 | else
11 | echo ">>> $1"
12 | fi
13 | }
14 |
15 | #
16 | # Prints a warn message.
17 | #
18 | function warn()
19 | {
20 | if [[ -t 1 ]]; then
21 | echo -e "\x1b[1m\x1b[33m***\x1b[0m \x1b[1m$1\x1b[0m" >&2
22 | else
23 | echo "*** $1" >&2
24 | fi
25 | }
26 |
27 | #
28 | # Prints an error message.
29 | #
30 | function error()
31 | {
32 | if [[ -t 1 ]]; then
33 | echo -e "\x1b[1m\x1b[31m!!!\x1b[0m \x1b[1m$1\x1b[0m" >&2
34 | else
35 | echo "!!! $1" >&2
36 | fi
37 | }
38 |
39 | #
40 | # Prints an error message and exists with -1.
41 | #
42 | function fail()
43 | {
44 | error "$@"
45 | exit -1
46 | }
47 |
48 | # default to installing gems into vendor/bundle
49 | if [[ ! -f .bundle/config ]]; then
50 | bundle config set --local path vendor/bundle >/dev/null || \
51 | fail "Failed to run 'bundle config'"
52 | fi
53 |
54 | log "Installing gems ..."
55 | bundle install || fail "Failed to run 'bundle install'!"
56 |
57 | log "Setting up the project ..."
58 | bundle exec rake setup || "Failed to run 'rake setup'!"
59 |
--------------------------------------------------------------------------------
/lib/ronin/vulns.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative 'vulns/vuln'
22 | require_relative 'vulns/web_vuln'
23 | require_relative 'vulns/lfi'
24 | require_relative 'vulns/rfi'
25 | require_relative 'vulns/sqli'
26 | require_relative 'vulns/ssti'
27 | require_relative 'vulns/command_injection'
28 | require_relative 'vulns/open_redirect'
29 | require_relative 'vulns/reflected_xss'
30 | require_relative 'vulns/url_scanner'
31 | require_relative 'vulns/importer'
32 | require_relative 'vulns/version'
33 |
--------------------------------------------------------------------------------
/spec/cli/commands/open_redirect_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/open_redirect'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::OpenRedirect do
6 | include_examples "man_page"
7 |
8 | let(:url) { 'https://example.com/page.php?id=1' }
9 |
10 | describe "#option_parser" do
11 | context "when the '--test_url' option is parsed" do
12 | let(:test_url) { 'https://example.com/test' }
13 | let(:argv) { ['--test-url', test_url] }
14 |
15 | before { subject.option_parser.parse(argv) }
16 |
17 | it "must set the :test_url key in #scan_kwargs" do
18 | expect(subject.scan_kwargs[:test_url]).to eq(test_url)
19 | end
20 | end
21 | end
22 |
23 | describe "#scan_url" do
24 | it "must call Ronin::Vulns::OpenRedirect.scan with the URL and #scan_kwargs" do
25 | expect(Ronin::Vulns::OpenRedirect).to receive(:scan).with(
26 | url, **subject.scan_kwargs
27 | )
28 |
29 | subject.scan_url(url)
30 | end
31 | end
32 |
33 | describe "#test_url" do
34 | it "must call Ronin::Vulns::OpenRedirect.scan with the URL and #scan_kwargs" do
35 | expect(Ronin::Vulns::OpenRedirect).to receive(:test).with(
36 | url, **subject.scan_kwargs
37 | )
38 |
39 | subject.test_url(url)
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/command.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require 'ronin/core/cli/command'
22 |
23 | require_relative '../root'
24 |
25 | module Ronin
26 | module Vulns
27 | class CLI
28 | #
29 | # Base class for all `ronin-vulns` commands.
30 | #
31 | class Command < Core::CLI::Command
32 |
33 | man_dir File.join(ROOT,'man')
34 |
35 | bug_report_url 'https://github.com/ronin-rb/ronin-vulns/issues/new'
36 |
37 | end
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/cli/commands/ssti_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/ssti'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::Ssti do
6 | include_examples "man_page"
7 |
8 | let(:url) { 'https://example.com/page.php?id=1' }
9 |
10 | describe "#option_parser" do
11 | context "when the '--test-expr' option is parsed" do
12 | let(:test) { '7*7' }
13 | let(:argv) { ['--test-expr', test] }
14 |
15 | before { subject.option_parser.parse(argv) }
16 |
17 | it "must set the :test_expr key in #scan_kwargs" do
18 | kwargs = subject.scan_kwargs
19 |
20 | expect(kwargs[:test_expr]).to be_kind_of(Ronin::Vulns::SSTI::TestExpression)
21 | expect(kwargs[:test_expr].string).to eq(test)
22 | end
23 | end
24 | end
25 |
26 | describe "#scan_url" do
27 | it "must call Ronin::Vulns::SSTI.scan with the URL and #scan_kwargs" do
28 | expect(Ronin::Vulns::SSTI).to receive(:scan).with(
29 | url, **subject.scan_kwargs
30 | )
31 |
32 | subject.scan_url(url)
33 | end
34 | end
35 |
36 | describe "#test_url" do
37 | it "must call Ronin::Vulns::SSTI.scan with the URL and #scan_kwargs" do
38 | expect(Ronin::Vulns::SSTI).to receive(:test).with(
39 | url, **subject.scan_kwargs
40 | )
41 |
42 | subject.test_url(url)
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/.github/workflows/ruby.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [ push, pull_request ]
4 |
5 | jobs:
6 | tests:
7 | runs-on: ubuntu-latest
8 | strategy:
9 | fail-fast: false
10 | matrix:
11 | ruby:
12 | - '3.0'
13 | - '3.1'
14 | - '3.2'
15 | - '3.3'
16 | - '3.4'
17 | # - jruby
18 | - truffleruby
19 | name: Ruby ${{ matrix.ruby }}
20 | steps:
21 | - uses: actions/checkout@v4
22 | - name: Set up Ruby
23 | uses: ruby/setup-ruby@v1
24 | with:
25 | ruby-version: ${{ matrix.ruby }}
26 | bundler-cache: true
27 | bundler: latest
28 | - name: Install libsqlite3
29 | run: |
30 | sudo apt update -y && \
31 | sudo apt install -y --no-install-recommends --no-install-suggests libsqlite3-dev
32 | - name: Install dependencies
33 | run: bundle install --jobs 4 --retry 3
34 | - name: Run tests
35 | run: bundle exec rake test
36 |
37 | # rubocop linting
38 | rubocop:
39 | runs-on: ubuntu-latest
40 | steps:
41 | - uses: actions/checkout@v4
42 | - name: Set up Ruby
43 | uses: ruby/setup-ruby@v1
44 | with:
45 | ruby-version: 3.0
46 | - name: Install dependencies
47 | run: bundle install --jobs 4 --retry 3
48 | - name: Run rubocop
49 | run: bundle exec rubocop --parallel
50 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/vuln.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | module Ronin
22 | module Vulns
23 | #
24 | # Base class for all vulnerability classes.
25 | #
26 | # @abstract
27 | #
28 | class Vuln
29 |
30 | #
31 | # Returns the type or kind of vulnerability.
32 | #
33 | # @return [Symbol]
34 | #
35 | # @note
36 | # This is used internally to map an vulnerability class to a printable
37 | # type.
38 | #
39 | # @api private
40 | #
41 | # @abstract
42 | #
43 | def self.vuln_type
44 | raise(NotImplementedError,"#{self}.vuln_type was not implemented")
45 | end
46 |
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 |
5 | gemspec
6 |
7 | platform :jruby do
8 | gem 'jruby-openssl', '~> 0.7'
9 | gem 'activerecord-jdbcsqlite3-adapter', '~> 70.0'
10 | gem 'activerecord', '< 7.1.0'
11 | end
12 |
13 | # gem 'command_kit', '~> 0.4', github: 'postmodern/command_kit.rb',
14 | # branch: '0.4.0'
15 |
16 | # Ronin dependencies
17 | # gem 'ronin-support', '~> 1.0', github: 'ronin-rb/ronin-support',
18 | # branch: 'main'
19 | # gem 'ronin-core', '~> 0.2', github: 'ronin-rb/ronin-core',
20 | # branch: 'main'
21 | # gem 'ronin-db', '~> 0.2', github: 'ronin-rb/ronin-db',
22 | # branch: 'main'
23 | # gem 'ronin-db-activerecord', '~> 0.2', github: 'ronin-rb/ronin-db-activerecord',
24 | # branch: 'main'
25 |
26 | group :development do
27 | gem 'rake'
28 | gem 'rubygems-tasks', '~> 0.2'
29 |
30 | gem 'rspec', '~> 3.0'
31 | gem 'webmock', '~> 3.0'
32 | gem 'simplecov', '~> 0.20'
33 |
34 | gem 'kramdown', '~> 2.0'
35 | gem 'kramdown-man', '~> 1.0'
36 |
37 | gem 'redcarpet', platform: :mri
38 | gem 'yard', '~> 0.9'
39 | gem 'yard-spellcheck', require: false
40 |
41 | gem 'dead_end', require: false
42 | gem 'sord', require: false, platform: :mri
43 | gem 'stackprof', require: false, platform: :mri
44 | gem 'rubocop', require: false, platform: :mri
45 | gem 'rubocop-ronin', require: false, platform: :mri
46 |
47 | gem 'command_kit-completion', '~> 0.2', require: false
48 | end
49 |
--------------------------------------------------------------------------------
/gemspec.yml:
--------------------------------------------------------------------------------
1 | name: ronin-vulns
2 | summary:
3 | Tests URLs for Local File Inclusion (LFI), Remove File Inclusion (RFI),
4 | SQL injection (SQLi), Cross Site Scripting (XSS), Server Side Template
5 | Injection (SSTI), and Open Redirects.
6 | description: |
7 | ronin-vulns is a Ruby library for blind vulnerability testing.
8 | It currently supports testing for Local File Inclusion (LFI),
9 | Remote File Inclusion (RFI), SQL injection (SQLi), reflective Cross Site
10 | Scripting (XSS), Server Side Template Injection (SSTI), and Open Redirects.
11 |
12 | license: LGPL-3.0-or-later
13 | authors: Postmodern
14 | email: postmodern.mod3@gmail.com
15 | homepage: https://ronin-rb.dev/
16 | has_yard: true
17 |
18 | metadata:
19 | documentation_uri: https://ronin-rb.dev/docs/ronin-vulns
20 | source_code_uri: https://github.com/ronin-rb/ronin-vulns
21 | bug_tracker_uri: https://github.com/ronin-rb/ronin-vulns/issues
22 | changelog_uri: https://github.com/ronin-rb/ronin-vulns/blob/main/ChangeLog.md
23 | rubygems_mfa_required: 'true'
24 |
25 | required_ruby_version: ">= 3.0.0"
26 |
27 | generated_files:
28 | - data/completions/ronin-vulns
29 | - man/ronin-vulns.1
30 | - man/ronin-vulns-completion.1
31 | - man/ronin-vulns-irb.1
32 | - man/ronin-vulns-lfi.1
33 | - man/ronin-vulns-rfi.1
34 | - man/ronin-vulns-sqli.1
35 | - man/ronin-vulns-ssti.1
36 | - man/ronin-vulns-command-injection.1
37 | - man/ronin-vulns-open-redirect.1
38 | - man/ronin-vulns-reflected-xss.1
39 | - man/ronin-vulns-scan.1
40 |
41 | dependencies:
42 | base64: ~> 0.1
43 | # Ronin dependencies:
44 | ronin-support: ~> 1.0, >= 1.0.1
45 | ronin-core: ~> 0.2
46 | ronin-db: ~> 0.2
47 |
48 | development_dependencies:
49 | bundler: ~> 2.0
50 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require 'ronin/core/cli/help/banner'
22 |
23 | require 'command_kit/commands'
24 | require 'command_kit/commands/auto_load'
25 | require 'command_kit/options/version'
26 |
27 | require_relative 'version'
28 |
29 | module Ronin
30 | module Vulns
31 | #
32 | # The `ronin-vulns` command-line interface (CLI).
33 | #
34 | # @api private
35 | #
36 | class CLI
37 |
38 | include CommandKit::Commands
39 | include CommandKit::Commands::AutoLoad.new(
40 | dir: "#{__dir__}/cli/commands",
41 | namespace: "#{self}::Commands"
42 | )
43 | include CommandKit::Options::Version
44 | include Core::CLI::Help::Banner
45 |
46 | command_name 'ronin-vulns'
47 | version Ronin::Vulns::VERSION
48 |
49 | command_aliases['xss'] = 'reflected-xss'
50 | command_aliases['cmdi'] = 'command-injection'
51 |
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/ruby_shell.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require 'ronin/core/cli/ruby_shell'
22 |
23 | module Ronin
24 | module Vulns
25 | class CLI
26 | #
27 | # The interactive Ruby shell for {Ronin::Vulns}.
28 | #
29 | # @since 0.2.0
30 | #
31 | class RubyShell < Core::CLI::RubyShell
32 |
33 | #
34 | # Initializes the `ronin-vulns` Ruby shell.
35 | #
36 | # @param [String] name
37 | # The name of the IRB shell.
38 | #
39 | # @param [Object] context
40 | # Custom context to launch IRB from within.
41 | #
42 | # @param [Hash{Symbol => Object}] kwargs
43 | # Additional keyword arguments for
44 | # `Ronin::Core::CLI::RubyShell#initialize`.
45 | #
46 | def initialize(name: 'ronin-vulns', context: Vulns, **kwargs)
47 | super(name: name, context: context, **kwargs)
48 | end
49 |
50 | end
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/irb.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../command'
22 | require_relative '../ruby_shell'
23 |
24 | module Ronin
25 | module Vulns
26 | class CLI
27 | module Commands
28 | #
29 | # Starts an interactive Ruby shell with `ronin-vulns` loaded.
30 | #
31 | # ## Usage
32 | #
33 | # ronin-vulns irb [options]
34 | #
35 | # ## Options
36 | #
37 | # -h, --help Print help information
38 | #
39 | # @since 0.2.0
40 | #
41 | class Irb < Command
42 |
43 | description "Starts an interactive Ruby shell with ronin-vulns loaded"
44 |
45 | man_page 'ronin-vulns-irb.1'
46 |
47 | #
48 | # Runs the `ronin-vulns irb` command.
49 | #
50 | def run
51 | require 'ronin/vulns'
52 | CLI::RubyShell.start
53 | end
54 |
55 | end
56 | end
57 | end
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/spec/cli/commands/sqli_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/sqli'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::Sqli do
6 | include_examples "man_page"
7 |
8 | let(:url) { 'https://example.com/page.php?id=1' }
9 |
10 | describe "#option_parser" do
11 | context "when the '--escape-quote' option is parsed" do
12 | let(:argv) { %w[--escape-quote] }
13 |
14 | before { subject.option_parser.parse(argv) }
15 |
16 | it "must set the :escape_quote key in #scan_kwargs" do
17 | expect(subject.scan_kwargs[:escape_quote]).to be(true)
18 | end
19 | end
20 |
21 | context "when the '--escape-parens' option is parsed" do
22 | let(:argv) { %w[--escape-parens] }
23 |
24 | before { subject.option_parser.parse(argv) }
25 |
26 | it "must set the :escape_parens key in #scan_kwargs" do
27 | expect(subject.scan_kwargs[:escape_parens]).to be(true)
28 | end
29 | end
30 |
31 | context "when the '--terminate' option is parsed" do
32 | let(:argv) { %w[--terminate] }
33 |
34 | before { subject.option_parser.parse(argv) }
35 |
36 | it "must set the :terminate key in #scan_kwargs" do
37 | expect(subject.scan_kwargs[:terminate]).to be(true)
38 | end
39 | end
40 | end
41 |
42 | describe "#scan_url" do
43 | it "must call Ronin::Vulns::SQLI.scan with the URL and #scan_kwargs" do
44 | expect(Ronin::Vulns::SQLI).to receive(:scan).with(
45 | url, **subject.scan_kwargs
46 | )
47 |
48 | subject.scan_url(url)
49 | end
50 | end
51 |
52 | describe "#test_url" do
53 | it "must call Ronin::Vulns::SQLI.scan with the URL and #scan_kwargs" do
54 | expect(Ronin::Vulns::SQLI).to receive(:test).with(
55 | url, **subject.scan_kwargs
56 | )
57 |
58 | subject.test_url(url)
59 | end
60 | end
61 | end
62 |
--------------------------------------------------------------------------------
/spec/cli/commands/command_injection_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/command_injection'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::CommandInjection do
6 | include_examples "man_page"
7 |
8 | let(:url) { 'https://example.com/page.php?id=1' }
9 |
10 | describe "#scan_kwargs" do
11 | context "when #options[:escape_quote] is set" do
12 | let(:argv) { %w[--escape-quote '] }
13 |
14 | before { subject.option_parser.parse(argv) }
15 |
16 | it "must set the :escape_quote key in #scan_kwargs" do
17 | expect(subject.scan_kwargs[:escape_quote]).to eq("'")
18 | end
19 | end
20 |
21 | context "when #options[:escape_operator] is set" do
22 | let(:argv) { %w[--escape-operator ;] }
23 |
24 | before { subject.option_parser.parse(argv) }
25 |
26 | it "must set the :escape_operator key in #scan_kwargs" do
27 | expect(subject.scan_kwargs[:escape_operator]).to eq(';')
28 | end
29 | end
30 |
31 | context "when #options[:terminator] is set" do
32 | let(:argv) { %w[--terminator ;] }
33 |
34 | before { subject.option_parser.parse(argv) }
35 |
36 | it "must set the :terminator key in #scan_kwargs" do
37 | expect(subject.scan_kwargs[:terminator]).to eq(';')
38 | end
39 | end
40 | end
41 |
42 | describe "#scan_url" do
43 | it "must call Ronin::Vulns::CommandInjection .scan with the URL and #scan_kwargs" do
44 | expect(Ronin::Vulns::CommandInjection).to receive(:scan).with(
45 | url, **subject.scan_kwargs
46 | )
47 |
48 | subject.scan_url(url)
49 | end
50 | end
51 |
52 | describe "#test_url" do
53 | it "must call Ronin::Vulns::CommandInjection.scan with the URL and #scan_kwargs" do
54 | expect(Ronin::Vulns::CommandInjection).to receive(:test).with(
55 | url, **subject.scan_kwargs
56 | )
57 |
58 | subject.test_url(url)
59 | end
60 | end
61 | end
62 |
--------------------------------------------------------------------------------
/man/ronin-vulns.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns 1 "2024-01-01" Ronin Vulns "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns - A library and tool that tests for various web vulnerabilities.
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns` [*options*] [*COMMAND* [...]]
10 |
11 | ## DESCRIPTION
12 |
13 | Runs a `ronin-vulns` *COMMAND*.
14 |
15 | ## ARGUMENTS
16 |
17 | *COMMAND*
18 | : The `ronin-vulns` command to execute.
19 |
20 | ## OPTIONS
21 |
22 | `-V`, `--version`
23 | : Prints the `ronin-vulns` version and exits.
24 |
25 | `-h`, `--help`
26 | : Print help information
27 |
28 | ## COMMANDS
29 |
30 | *command-injection*, *cmdi*
31 | : Scans URL(s) for Command Injection vulnerabilities.
32 |
33 | *completion*
34 | : Manages the shell completion rules for `ronin-vulns`.
35 |
36 | *help*
37 | : Lists available commands or shows help about a specific command.
38 |
39 | *irb*
40 | : Starts an interactive Ruby shell with ronin-vulns loaded.
41 |
42 | *lfi*
43 | : Scans URL(s) for Local File Inclusion (LFI) vulnerabilities.
44 |
45 | *open-redirect*
46 | : Scans URL(s) for Open Redirect vulnerabilities.
47 |
48 | *reflected-xss*, *xss*
49 | : Scans URL(s) for Reflected Cross Site Scripting (XSS) vulnerabilities.
50 |
51 | *rfi*
52 | : Scans URL(s) for Remote File Inclusion (RFI) vulnerabilities.
53 |
54 | *scan*
55 | : Scans URL(s) for web vulnerabilities.
56 |
57 | *sqli*
58 | : Scans URL(s) for SQL injection (SQLi) vulnerabilities.
59 |
60 | *ssti*
61 | : Scans URL(s) for Server Side Template Injection (SSTI) vulnerabilities.
62 |
63 | ## AUTHOR
64 |
65 | Postmodern
66 |
67 | ## SEE ALSO
68 |
69 | [ronin-vulns-command-injection](ronin-vulns-command-injection.1.md) [ronin-vulns-completion](ronin-vulns-completion.1.md) [ronin-vulns-lfi](ronin-vulns-lfi.1.md) [ronin-vulns-open-redirect](ronin-vulns-open-redirect.1.md) [ronin-vulns-reflected-xss](ronin-vulns-reflected-xss.1.md) [ronin-vulns-rfi](ronin-vulns-rfi.1.md) [ronin-vulns-scan](ronin-vulns-scan.1.md) [ronin-vulns-sqli](ronin-vulns-sqli.1.md) [ronin-vulns-ssti](ronin-vulns-ssti.1.md)
70 |
--------------------------------------------------------------------------------
/man/ronin-vulns-completion.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-completion 1 "2024-01-01" Ronin Vulns "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-completion - Manages shell completion rules for `ronin-vulns`
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns completion` [*options*]
10 |
11 | ## DESCRIPTION
12 |
13 | The `ronin-vulns completion` command can print, install, or uninstall shell
14 | completion rules for the `ronin-vulns` command.
15 |
16 | Supports installing completion rules for Bash or Zsh shells.
17 | Completion rules for the Fish shell is currently not supported.
18 |
19 | ### ZSH SUPPORT
20 |
21 | Zsh users will have to add the following lines to their `~/.zshrc` file in
22 | order to enable Zsh's Bash completion compatibility layer:
23 |
24 | autoload -Uz +X compinit && compinit
25 | autoload -Uz +X bashcompinit && bashcompinit
26 |
27 | ## OPTIONS
28 |
29 | `--print`
30 | : Prints the shell completion file.
31 |
32 | `--install`
33 | : Installs the shell completion file.
34 |
35 | `--uninstall`
36 | : Uninstalls the shell completion file.
37 |
38 | `-h`, `--help`
39 | : Prints help information.
40 |
41 | ## ENVIRONMENT
42 |
43 | *PREFIX*
44 | : Specifies the root prefix for the file system.
45 |
46 | *HOME*
47 | : Specifies the home directory of the user. Ronin will search for the
48 | `~/.cache/ronin-vulns` cache directory within the home directory.
49 |
50 | *XDG_DATA_HOME*
51 | : Specifies the data directory to use. Defaults to `$HOME/.local/share`.
52 |
53 | ## FILES
54 |
55 | `~/.local/share/bash-completion/completions/`
56 | : The user-local installation directory for Bash completion files.
57 |
58 | `/usr/local/share/bash-completion/completions/`
59 | : The system-wide installation directory for Bash completions files.
60 |
61 | `/usr/local/share/zsh/site-functions/`
62 | : The installation directory for Zsh completion files.
63 |
64 | ## EXAMPLES
65 |
66 | `ronin-vulns completion --print`
67 | : Prints the shell completion rules instead of installing them.
68 |
69 | `ronin-vulns completion --install`
70 | : Installs the shell completion rules for `ronin-vulns`.
71 |
72 | `ronin-vulns completion --uninstall`
73 | : Uninstalls the shell completion rules for `ronin-vulns`.
74 |
75 | ## AUTHOR
76 |
77 | Postmodern
78 |
79 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/completion.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require 'ronin/core/cli/completion_command'
22 |
23 | require_relative '../../root'
24 |
25 | module Ronin
26 | module Vulns
27 | class CLI
28 | module Commands
29 | #
30 | # Manages the shell completion rules for `ronin-vulns`.
31 | #
32 | # ## Usage
33 | #
34 | # ronin-vulns completion [options]
35 | #
36 | # ## Options
37 | #
38 | # --print Prints the shell completion file
39 | # --install Installs the shell completion file
40 | # --uninstall Uninstalls the shell completion file
41 | # -h, --help Print help information
42 | #
43 | # ## Examples
44 | #
45 | # ronin-vulns completion --print
46 | # ronin-vulns completion --install
47 | # ronin-vulns completion --uninstall
48 | #
49 | # @since 0.2.0
50 | #
51 | class Completion < Core::CLI::CompletionCommand
52 |
53 | completion_file File.join(ROOT,'data','completions','ronin-vulns')
54 |
55 | man_dir File.join(ROOT,'man')
56 | man_page 'ronin-vulns-completion.1'
57 |
58 | description 'Manages the shell completion rules for ronin-vulns'
59 |
60 | end
61 | end
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/ronin-vulns.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'yaml'
4 |
5 | Gem::Specification.new do |gem|
6 | gemspec = YAML.load_file('gemspec.yml')
7 |
8 | gem.name = gemspec.fetch('name')
9 | gem.version = gemspec.fetch('version') do
10 | lib_dir = File.join(File.dirname(__FILE__),'lib')
11 | $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
12 |
13 | require 'ronin/vulns/version'
14 | Ronin::Vulns::VERSION
15 | end
16 |
17 | gem.summary = gemspec['summary']
18 | gem.description = gemspec['description']
19 | gem.licenses = Array(gemspec['license'])
20 | gem.authors = Array(gemspec['authors'])
21 | gem.email = gemspec['email']
22 | gem.homepage = gemspec['homepage']
23 | gem.metadata = gemspec['metadata'] if gemspec['metadata']
24 |
25 | glob = ->(patterns) { gem.files & Dir[*patterns] }
26 |
27 | gem.files = `git ls-files`.split($/)
28 | gem.files = glob[gemspec['files']] if gemspec['files']
29 | gem.files += Array(gemspec['generated_files'])
30 | # exclude test files from the packages gem
31 | gem.files -= glob[gemspec['test_files'] || 'spec/{**/}*']
32 |
33 | gem.executables = gemspec.fetch('executables') do
34 | glob['bin/*'].map { |path| File.basename(path) }
35 | end
36 |
37 | gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
38 | gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
39 |
40 | gem.require_paths = Array(gemspec.fetch('require_paths') {
41 | %w[ext lib].select { |dir| File.directory?(dir) }
42 | })
43 |
44 | gem.requirements = gemspec['requirements']
45 | gem.required_ruby_version = gemspec['required_ruby_version']
46 | gem.required_rubygems_version = gemspec['required_rubygems_version']
47 | gem.post_install_message = gemspec['post_install_message']
48 |
49 | split = ->(string) { string.split(/,\s*/) }
50 |
51 | if gemspec['dependencies']
52 | gemspec['dependencies'].each do |name,versions|
53 | gem.add_dependency(name,split[versions])
54 | end
55 | end
56 |
57 | if gemspec['development_dependencies']
58 | gemspec['development_dependencies'].each do |name,versions|
59 | gem.add_development_dependency(name,split[versions])
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/importable.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative 'printing'
22 | require_relative '../importer'
23 |
24 | require 'ronin/db/cli/database_options'
25 | require 'ronin/db/cli/printing'
26 |
27 | module Ronin
28 | module Vulns
29 | class CLI
30 | #
31 | # Mixin module which adds the ability to import web vulns into the
32 | # [ronin-db] database.
33 | #
34 | # [ronin-db]: https://github.com/ronin-rb/ronin-db#readme
35 | #
36 | # @since 0.2.0
37 | #
38 | module Importable
39 | include DB::CLI::Printing
40 | include Printing
41 |
42 | #
43 | # Includes `Ronin::DB::CLI::DatabaseOptions` into the including command
44 | # class.
45 | #
46 | # @param [Class] command
47 | # The command class including {Importable}.
48 | #
49 | def self.included(command)
50 | command.include DB::CLI::DatabaseOptions
51 | end
52 |
53 | #
54 | # Imports a web vulnerability into the [ronin-db] database.
55 | #
56 | # [ronin-db]: https://github.com/ronin-rb/ronin-db#readme
57 | #
58 | # @param [WebVuln] vuln
59 | # The web vulnerability to import.
60 | #
61 | def import_vuln(vuln)
62 | Importer.import(vuln)
63 |
64 | vuln_type = vuln_type(vuln)
65 | param_type = vuln_param_type(vuln)
66 | param_name = vuln_param_name(vuln)
67 |
68 | if (param_type && param_name)
69 | log_info "Imported #{vuln_type} vulnerability on URL #{vuln.url} and #{param_type} '#{param_name}'"
70 | else
71 | log_info "Imported #{vuln_type} vulnerability on URL #{vuln.url}"
72 | end
73 | end
74 | end
75 | end
76 | end
77 | end
78 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/sqli/error_pattern.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../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] 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 response 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
90 |
--------------------------------------------------------------------------------
/spec/lfi/test_file_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/lfi/test_file'
3 |
4 | describe Ronin::Vulns::LFI::TestFile do
5 | let(:path) { '/etc/foo' }
6 | let(:regexp) { /foo\nbar\nbaz\n/ }
7 |
8 | subject { described_class.new(path,regexp) }
9 |
10 | describe "#initialize" do
11 | it "must set #path" do
12 | expect(subject.path).to eq(path)
13 | end
14 |
15 | it "must set #regexp" do
16 | expect(subject.regexp).to eq(regexp)
17 | end
18 | end
19 |
20 | describe "#match" do
21 | context "when the #regexp matches the response body" do
22 | let(:file) { "foo\nbar\nbaz\n" }
23 | let(:response_body) do
24 | <<~HTML
25 |
26 |
27 | bla bla bla
28 | #{file}
29 | bla bla bla
30 |
31 |
32 | HTML
33 | end
34 |
35 | it "must return MatchData" do
36 | expect(subject.match(response_body)).to be_kind_of(MatchData)
37 | end
38 |
39 | it "must match the given response body against #regexp" do
40 | expect(subject.match(response_body)[0]).to eq(file)
41 | end
42 | end
43 |
44 | context "when the #regexp does not match the response body" do
45 | let(:response_body) do
46 | <<~HTML
47 |
48 |
49 | bla bla bla
50 | bla bla bla
51 | bla bla bla
52 |
53 |
54 | HTML
55 | end
56 |
57 | it "must return nil" do
58 | expect(subject.match(response_body)).to be(nil)
59 | end
60 | end
61 | end
62 |
63 | describe "#=~" do
64 | context "when the #regexp matches the response body" do
65 | let(:file) { "foo\nbar\nbaz\n" }
66 | let(:response_body) do
67 | <<~HTML
68 |
69 |
70 | bla bla bla
71 | #{file}
72 | bla bla bla
73 |
74 |
75 | HTML
76 | end
77 |
78 | it "must return the index of the match" do
79 | expect(subject =~ response_body).to eq(response_body.index(regexp))
80 | end
81 | end
82 |
83 | context "when the #regexp does not match the response body" do
84 | let(:response_body) do
85 | <<~HTML
86 |
87 |
88 | bla bla bla
89 | bla bla bla
90 | bla bla bla
91 |
92 |
93 | HTML
94 | end
95 |
96 | it "must return nil" do
97 | expect(subject =~ response_body).to be(nil)
98 | end
99 | end
100 | end
101 | end
102 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/lfi/test_file.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library to blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln'
22 |
23 | module Ronin
24 | module Vulns
25 | class LFI < WebVuln
26 | #
27 | # Represents a single Local File Inclusion (LFI) test for a given file
28 | # path and a regexp that matches the file.
29 | #
30 | # @api private
31 | #
32 | class TestFile
33 |
34 | # The path of the file to attempt including.
35 | #
36 | # @return [String]
37 | attr_reader :path
38 |
39 | # The regexp to identify a successful Local File Inclusion (LFI)
40 | # of the {#path}.
41 | #
42 | # @return [Regexp]
43 | attr_reader :regexp
44 |
45 | #
46 | # Initializes the Local File Inclusion (LFI) test.
47 | #
48 | # @param [String] path
49 | # The path to attempt including.
50 | #
51 | # @param [Regexp] regexp
52 | # The regexp to identify a successful Local File Inclusion (LFI)
53 | # of the {#path}.
54 | #
55 | def initialize(path,regexp)
56 | @path = path
57 | @regexp = regexp
58 | end
59 |
60 | #
61 | # Tests whether the file was successfully included into the response
62 | # body.
63 | #
64 | # @param [String] response_body
65 | # The HTTP response body.
66 | #
67 | # @return [MatchData, nil]
68 | # The match data if the {#regexp} is found within the response body.
69 | #
70 | def match(response_body)
71 | response_body.match(@regexp)
72 | end
73 |
74 | #
75 | # Tests whether the file was successfully included into the response
76 | # body.
77 | #
78 | # @param [String] response_body
79 | # The HTTP response body.
80 | #
81 | # @return [Integer, nil]
82 | # Indicates whether the {#regexp} was found in the response body.
83 | #
84 | def =~(response_body)
85 | response_body =~ @regexp
86 | end
87 |
88 | end
89 | end
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/man/ronin-vulns-reflected-xss.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-reflected-xss 1 "May 2022" Ronin "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-reflected-xss - Scans URL(s) for Reflected Cross Site Scripting (XSS) vulnerabilities
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns reflected-xss` [*options*] {*URL* ... \| `--input` *FILE*}
10 |
11 | ## DESCRIPTION
12 |
13 | Scans URL(s) for reflected Cross Site Scripting (XSS) vulnerabilities. The URLs
14 | to scan can be given as additional arguments or read from a file using the
15 | `--input` option.
16 |
17 | ## ARGUMENTS
18 |
19 | *URL*
20 | : A URL to scan.
21 |
22 | ## OPTIONS
23 |
24 | `--db` *NAME*
25 | : The database name to connect to. Defaults to `default` if not given.
26 |
27 | `--db-uri` *URI*
28 | : The database URI to connect to
29 | (ex: `postgres://user:password@host/db`).
30 |
31 | `--db-file` *PATH*
32 | : The sqlite3 database file to use.
33 |
34 | `--import`
35 | : Imports discovered vulnerabilities into the database.
36 |
37 | `--first`
38 | : Only find the first vulnerability for each URL.
39 |
40 | `-A`, `--all`
41 | : Find all vulnerabilities for each URL.
42 |
43 | `--print-curl`
44 | : Also prints an example `curl` command for each vulnerability.
45 |
46 | `--print-http`
47 | : Also prints an example HTTP request for each vulnerability.
48 |
49 | `-M`, `--request-method` `COPY`|`DELETE`|`GET`|`HEAD`|`LOCK`|`MKCOL`|`MOVE`|`OPTIONS`|`PATCH`|`POST`|`PROPFIND`|`PROPPATCH`|`PUT`|`TRACE`|`UNLOCK`
50 | : Sets the HTTP request method to use.
51 |
52 | `-H`, `--header` "*Name*: *value*"
53 | : Sets an additional header using the given *Name* and *value*.
54 |
55 | `-U`, `--user-agent-string` *STRING*
56 | : Sets the `User-Agent` header string.
57 |
58 | `-u`, `--user-agent` `chrome-linux`\|`chrome-macos`\|`chrome-windows`\|`chrome-iphone`\|`chrome-ipad`\|`chrome-android`\|`firefox-linux`\|`firefox-macos`\|`firefox-windows`\|`firefox-iphone`\|`firefox-ipad`\|`firefox-android`\|`safari-macos`\|`safari-iphone`\|`safari-ipad`\|`edge`
59 | : Sets the `User-Agent` header.
60 |
61 | `-C`, `--cookie` *COOKIE*
62 | : Sets the raw `Cookie` header.
63 |
64 | `-c`, `--cookie-param` *NAME*`=`*VALUE*
65 | : Sets an additional `Cookie` param using the given *NAME* and *VALUE*.
66 |
67 | `-R`, `--referer` *URL*
68 | : Sets the `Referer` header.
69 |
70 | `-F`, `--form-param` *NAME*`=`*VALUE*
71 | : Sets an additional form param using the given *NAME* and *VALUE*.
72 |
73 | `--test-query-param` *NAME*
74 | : Tests the URL query param name.
75 |
76 | `--test-all-query-params`
77 | : Test all URL query param names.
78 |
79 | `--test-header-name` *NAME*
80 | : Tests the HTTP Header name.
81 |
82 | `--test-cookie-param` *NAME*
83 | : Tests the HTTP Cookie name.
84 |
85 | `--test-all-cookie-params`
86 | : Test all Cookie param names.
87 |
88 | `--test-form-param` *NAME*
89 | : Tests the form param name.
90 |
91 | `-i`, `--input` *FILE*
92 | : Reads URLs from the given *FILE*.
93 |
94 | `-h`, `--help`
95 | : Print help information.
96 |
97 | ## AUTHOR
98 |
99 | Postmodern
100 |
101 | ## SEE ALSO
102 |
103 | [ronin-vulns-scan](ronin-vulns-scan.1.md)
--------------------------------------------------------------------------------
/man/ronin-vulns-open-redirect.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-open-redirect 1 "May 2022" Ronin "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-open-redirect - Scans URL(s) for Open Redirect vulnerabilities
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns open-redirect` [*options*] {*URL* ... \| `--input` *FILE*}
10 |
11 | ## DESCRIPTION
12 |
13 | Scans URL(s) for Open Redirect vulnerabilities. The URLs to scan can be given
14 | as additional arguments or read from a file using the `--input` option.
15 |
16 | ## ARGUMENTS
17 |
18 | *URL*
19 | : A URL to scan.
20 |
21 | ## OPTIONS
22 |
23 | `--db` *NAME*
24 | : The database name to connect to. Defaults to `default` if not given.
25 |
26 | `--db-uri` *URI*
27 | : The database URI to connect to
28 | (ex: `postgres://user:password@host/db`).
29 |
30 | `--db-file` *PATH*
31 | : The sqlite3 database file to use.
32 |
33 | `--import`
34 | : Imports discovered vulnerabilities into the database.
35 |
36 | `--first`
37 | : Only find the first vulnerability for each URL.
38 |
39 | `-A`, `--all`
40 | : Find all vulnerabilities for each URL.
41 |
42 | `--print-curl`
43 | : Also prints an example `curl` command for each vulnerability.
44 |
45 | `--print-http`
46 | : Also prints an example HTTP request for each vulnerability.
47 |
48 | `-M`, `--request-method` `COPY`|`DELETE`|`GET`|`HEAD`|`LOCK`|`MKCOL`|`MOVE`|`OPTIONS`|`PATCH`|`POST`|`PROPFIND`|`PROPPATCH`|`PUT`|`TRACE`|`UNLOCK`
49 | : Sets the HTTP request method to use.
50 |
51 | `-H`, `--header` "*Name*: *value*"
52 | : Sets an additional header using the given *Name* and *value*.
53 |
54 | `-U`, `--user-agent-string` *STRING*
55 | : Sets the `User-Agent` header string.
56 |
57 | `-u`, `--user-agent` `chrome-linux`\|`chrome-macos`\|`chrome-windows`\|`chrome-iphone`\|`chrome-ipad`\|`chrome-android`\|`firefox-linux`\|`firefox-macos`\|`firefox-windows`\|`firefox-iphone`\|`firefox-ipad`\|`firefox-android`\|`safari-macos`\|`safari-iphone`\|`safari-ipad`\|`edge`
58 | : Sets the `User-Agent` header.
59 |
60 | `-C`, `--cookie` *COOKIE*
61 | : Sets the raw `Cookie` header.
62 |
63 | `-c`, `--cookie-param` *NAME*`=`*VALUE*
64 | : Sets an additional `Cookie` param using the given *NAME* and *VALUE*.
65 |
66 | `-R`, `--referer` *URL*
67 | : Sets the `Referer` header.
68 |
69 | `-F`, `--form-param` *NAME*`=`*VALUE*
70 | : Sets an additional form param using the given *NAME* and *VALUE*.
71 |
72 | `--test-query-param` *NAME*
73 | : Tests the URL query param name.
74 |
75 | `--test-all-query-params`
76 | : Test all URL query param names.
77 |
78 | `--test-header-name` *NAME*
79 | : Tests the HTTP Header name.
80 |
81 | `--test-cookie-param` *NAME*
82 | : Tests the HTTP Cookie name.
83 |
84 | `--test-all-cookie-params`
85 | : Test all Cookie param names.
86 |
87 | `--test-form-param` *NAME*
88 | : Tests the form param name.
89 |
90 | `-i`, `--input` *FILE*
91 | : Reads URLs from the given *FILE*.
92 |
93 | `-T`, `--test-url` *URL*
94 | : Optional test *URL* to try to redirect to.
95 |
96 | `-h`, `--help`
97 | : Print help information.
98 |
99 | ## AUTHOR
100 |
101 | Postmodern
102 |
103 | ## SEE ALSO
104 |
105 | [ronin-vulns-scan](ronin-vulns-scan.1.md)
--------------------------------------------------------------------------------
/man/ronin-vulns-ssti.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-lfi 1 "May 2022" Ronin "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-ssti - Scans URL(s) for Server Side Template Injection (SSTI) vulnerabilities
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns lfi` [*options*] {*URL* ... \| `--input` *FILE*}
10 |
11 | ## DESCRIPTION
12 |
13 | Scans URL(s) for Server Side Template Injection (SSTI) vulnerabilities. The URLs
14 | to scan can be given as additional arguments or read from a file using the
15 | `--input` option.
16 |
17 | ## ARGUMENTS
18 |
19 | *URL*
20 | : A URL to scan.
21 |
22 | ## OPTIONS
23 |
24 | `--db` *NAME*
25 | : The database name to connect to. Defaults to `default` if not given.
26 |
27 | `--db-uri` *URI*
28 | : The database URI to connect to
29 | (ex: `postgres://user:password@host/db`).
30 |
31 | `--db-file` *PATH*
32 | : The sqlite3 database file to use.
33 |
34 | `--import`
35 | : Imports discovered vulnerabilities into the database.
36 |
37 | `--first`
38 | : Only find the first vulnerability for each URL.
39 |
40 | `-A`, `--all`
41 | : Find all vulnerabilities for each URL.
42 |
43 | `--print-curl`
44 | : Also prints an example `curl` command for each vulnerability.
45 |
46 | `--print-http`
47 | : Also prints an example HTTP request for each vulnerability.
48 |
49 | `-M`, `--request-method` `COPY`|`DELETE`|`GET`|`HEAD`|`LOCK`|`MKCOL`|`MOVE`|`OPTIONS`|`PATCH`|`POST`|`PROPFIND`|`PROPPATCH`|`PUT`|`TRACE`|`UNLOCK`
50 | : Sets the HTTP request method to use.
51 |
52 | `-H`, `--header` "*Name*: *value*"
53 | : Sets an additional header using the given *Name* and *value*.
54 |
55 | `-U`, `--user-agent-string` *STRING*
56 | : Sets the `User-Agent` header string.
57 |
58 | `-u`, `--user-agent` `chrome-linux`\|`chrome-macos`\|`chrome-windows`\|`chrome-iphone`\|`chrome-ipad`\|`chrome-android`\|`firefox-linux`\|`firefox-macos`\|`firefox-windows`\|`firefox-iphone`\|`firefox-ipad`\|`firefox-android`\|`safari-macos`\|`safari-iphone`\|`safari-ipad`\|`edge`
59 | : Sets the `User-Agent` header.
60 |
61 | `-C`, `--cookie` *COOKIE*
62 | : Sets the raw `Cookie` header.
63 |
64 | `-c`, `--cookie-param` *NAME*`=`*VALUE*
65 | : Sets an additional `Cookie` param using the given *NAME* and *VALUE*.
66 |
67 | `-R`, `--referer` *URL*
68 | : Sets the `Referer` header.
69 |
70 | `-F`, `--form-param` *NAME*`=`*VALUE*
71 | : Sets an additional form param using the given *NAME* and *VALUE*.
72 |
73 | `--test-query-param` *NAME*
74 | : Tests the URL query param name.
75 |
76 | `--test-all-query-params`
77 | : Test all URL query param names.
78 |
79 | `--test-header-name` *NAME*
80 | : Tests the HTTP Header name.
81 |
82 | `--test-cookie-param` *NAME*
83 | : Tests the HTTP Cookie name.
84 |
85 | `--test-all-cookie-params`
86 | : Test all Cookie param names.
87 |
88 | `--test-form-param` *NAME*
89 | : Tests the form param name.
90 |
91 | `-i`, `--input` *FILE*
92 | : Reads URLs from the given *FILE*.
93 |
94 | `-T`, `--test-expr` {*X\*Y* \| *X/Z* \| *X+Y* \| *X-Y*}
95 | : Optional numeric test to use.
96 |
97 | `-h`, `--help`
98 | : Print help information.
99 |
100 | ## AUTHOR
101 |
102 | Postmodern
103 |
104 | ## SEE ALSO
105 |
106 | [ronin-vulns-scan](ronin-vulns-scan.1.md)
--------------------------------------------------------------------------------
/man/ronin-vulns-sqli.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-sqli 1 "May 2022" Ronin "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-sqli - Scans URL(s) for SQL injection (SQLi) vulnerabilities
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns sqli` [*options*] {*URL* ... \| `--input` *FILE*}
10 |
11 | ## DESCRIPTION
12 |
13 | Scans URL(s) for SQL injection (SQLi) vulnerabilities. The URLs to scan
14 | can be given as additional arguments or read from a file using the `--input`
15 | option.
16 |
17 | ## ARGUMENTS
18 |
19 | *URL*
20 | : A URL to scan.
21 |
22 | ## OPTIONS
23 |
24 | `--db` *NAME*
25 | : The database name to connect to. Defaults to `default` if not given.
26 |
27 | `--db-uri` *URI*
28 | : The database URI to connect to
29 | (ex: `postgres://user:password@host/db`).
30 |
31 | `--db-file` *PATH*
32 | : The sqlite3 database file to use.
33 |
34 | `--import`
35 | : Imports discovered vulnerabilities into the database.
36 |
37 | `--first`
38 | : Only find the first vulnerability for each URL.
39 |
40 | `-A`, `--all`
41 | : Find all vulnerabilities for each URL.
42 |
43 | `--print-curl`
44 | : Also prints an example `curl` command for each vulnerability.
45 |
46 | `--print-http`
47 | : Also prints an example HTTP request for each vulnerability.
48 |
49 | `-M`, `--request-method` `COPY`|`DELETE`|`GET`|`HEAD`|`LOCK`|`MKCOL`|`MOVE`|`OPTIONS`|`PATCH`|`POST`|`PROPFIND`|`PROPPATCH`|`PUT`|`TRACE`|`UNLOCK`
50 | : Sets the HTTP request method to use.
51 |
52 | `-H`, `--header` "*Name*: *value*"
53 | : Sets an additional header using the given *Name* and *value*.
54 |
55 | `-U`, `--user-agent-string` *STRING*
56 | : Sets the `User-Agent` header string.
57 |
58 | `-u`, `--user-agent` `chrome-linux`\|`chrome-macos`\|`chrome-windows`\|`chrome-iphone`\|`chrome-ipad`\|`chrome-android`\|`firefox-linux`\|`firefox-macos`\|`firefox-windows`\|`firefox-iphone`\|`firefox-ipad`\|`firefox-android`\|`safari-macos`\|`safari-iphone`\|`safari-ipad`\|`edge`
59 | : Sets the `User-Agent` header.
60 |
61 | `-C`, `--cookie` *COOKIE*
62 | : Sets the raw `Cookie` header.
63 |
64 | `-c`, `--cookie-param` *NAME*`=`*VALUE*
65 | : Sets an additional `Cookie` param using the given *NAME* and *VALUE*.
66 |
67 | `-R`, `--referer` *URL*
68 | : Sets the `Referer` header.
69 |
70 | `-F`, `--form-param` *NAME*`=`*VALUE*
71 | : Sets an additional form param using the given *NAME* and *VALUE*.
72 |
73 | `--test-query-param` *NAME*
74 | : Tests the URL query param name.
75 |
76 | `--test-all-query-params`
77 | : Test all URL query param names.
78 |
79 | `--test-header-name` *NAME*
80 | : Tests the HTTP Header name.
81 |
82 | `--test-cookie-param` *NAME*
83 | : Tests the HTTP Cookie name.
84 |
85 | `--test-all-cookie-params`
86 | : Test all Cookie param names.
87 |
88 | `--test-form-param` *NAME*
89 | : Tests the form param name.
90 |
91 | `-i`, `--input` *FILE*
92 | : Reads URLs from the given *FILE*.
93 |
94 | `-Q`, `--escape-quote`
95 | : Escapes quotation marks.
96 |
97 | `-P`, `--escape-parens`
98 | : Escapes parenthesis.
99 |
100 | `-T`, `--terminate`
101 | : Terminates the SQL expression with a `--`.
102 |
103 | `-h`, `--help`
104 | : Print help information.
105 |
106 | ## AUTHOR
107 |
108 | Postmodern
109 |
110 | ## SEE ALSO
111 |
112 | [ronin-vulns-scan](ronin-vulns-scan.1.md)
--------------------------------------------------------------------------------
/man/ronin-vulns-lfi.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-lfi 1 "May 2022" Ronin "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-lfi - Scans URL(s) for Local File Inclusion (LFI) vulnerabilities
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns lfi` [*options*] {*URL* ... \| `--input` *FILE*}
10 |
11 | ## DESCRIPTION
12 |
13 | Scans URL(s) for Local File Inclusion (LFI) vulnerabilities. The URLs to scan
14 | can be given as additional arguments or read from a file using the `--input`
15 | option.
16 |
17 | ## ARGUMENTS
18 |
19 | *URL*
20 | : A URL to scan.
21 |
22 | ## OPTIONS
23 |
24 | `--db` *NAME*
25 | : The database name to connect to. Defaults to `default` if not given.
26 |
27 | `--db-uri` *URI*
28 | : The database URI to connect to
29 | (ex: `postgres://user:password@host/db`).
30 |
31 | `--db-file` *PATH*
32 | : The sqlite3 database file to use.
33 |
34 | `--import`
35 | : Imports discovered vulnerabilities into the database.
36 |
37 | `--first`
38 | : Only find the first vulnerability for each URL.
39 |
40 | `-A`, `--all`
41 | : Find all vulnerabilities for each URL.
42 |
43 | `--print-curl`
44 | : Also prints an example `curl` command for each vulnerability.
45 |
46 | `--print-http`
47 | : Also prints an example HTTP request for each vulnerability.
48 |
49 | `-M`, `--request-method` `COPY`|`DELETE`|`GET`|`HEAD`|`LOCK`|`MKCOL`|`MOVE`|`OPTIONS`|`PATCH`|`POST`|`PROPFIND`|`PROPPATCH`|`PUT`|`TRACE`|`UNLOCK`
50 | : Sets the HTTP request method to use.
51 |
52 | `-H`, `--header` "*Name*: *value*"
53 | : Sets an additional header using the given *Name* and *value*.
54 |
55 | `-U`, `--user-agent-string` *STRING*
56 | : Sets the `User-Agent` header string.
57 |
58 | `-u`, `--user-agent` `chrome-linux`\|`chrome-macos`\|`chrome-windows`\|`chrome-iphone`\|`chrome-ipad`\|`chrome-android`\|`firefox-linux`\|`firefox-macos`\|`firefox-windows`\|`firefox-iphone`\|`firefox-ipad`\|`firefox-android`\|`safari-macos`\|`safari-iphone`\|`safari-ipad`\|`edge`
59 | : Sets the `User-Agent` header.
60 |
61 | `-C`, `--cookie` *COOKIE*
62 | : Sets the raw `Cookie` header.
63 |
64 | `-c`, `--cookie-param` *NAME*`=`*VALUE*
65 | : Sets an additional `Cookie` param using the given *NAME* and *VALUE*.
66 |
67 | `-R`, `--referer` *URL*
68 | : Sets the `Referer` header.
69 |
70 | `-F`, `--form-param` *NAME*`=`*VALUE*
71 | : Sets an additional form param using the given *NAME* and *VALUE*.
72 |
73 | `--test-query-param` *NAME*
74 | : Tests the URL query param name.
75 |
76 | `--test-all-query-params`
77 | : Test all URL query param names.
78 |
79 | `--test-header-name` *NAME*
80 | : Tests the HTTP Header name.
81 |
82 | `--test-cookie-param` *NAME*
83 | : Tests the HTTP Cookie name.
84 |
85 | `--test-all-cookie-params`
86 | : Test all Cookie param names.
87 |
88 | `--test-form-param` *NAME*
89 | : Tests the form param name.
90 |
91 | `-i`, `--input` *FILE*
92 | : Reads URLs from the given *FILE*.
93 |
94 | `-O`, `--os` `unix`|`windows`
95 | : Sets the OS to test for.
96 |
97 | `-D`, `--depth` *COUNT*
98 | : Sets the directory depth to escape up.
99 |
100 | `-B`, `--filter-bypass` `null_byte`\|`double_escape`\|`base64`\|`rot13`\|`zlib`
101 | : Sets the filter bypass strategy to use.
102 |
103 | `-h`, `--help`
104 | : Print help information.
105 |
106 | ## AUTHOR
107 |
108 | Postmodern
109 |
110 | ## SEE ALSO
111 |
112 | [ronin-vulns-rfi](ronin-vulns-rfi.1.md) [ronin-vulns-scan](ronin-vulns-scan.1.md)
--------------------------------------------------------------------------------
/lib/ronin/vulns/ssti/test_expression.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln'
22 |
23 | module Ronin
24 | module Vulns
25 | #
26 | # Represents a Server Side Template Injection (SSTI) vulnerability.
27 | #
28 | class SSTI < WebVuln
29 | #
30 | # Represents a expression to test SSTI with (ex: `7*7`).
31 | #
32 | class TestExpression
33 |
34 | # The expression string.
35 | #
36 | # @return [String]
37 | attr_reader :string
38 |
39 | # The expected result of the string.
40 | #
41 | # @return [String]
42 | attr_reader :result
43 |
44 | #
45 | # Initializes the test expression.
46 | #
47 | # @param [String] string
48 | # The expression string.
49 | #
50 | # @param [String] result
51 | # The expected result of the expression.
52 | #
53 | def initialize(string,result)
54 | @string = string
55 | @result = result
56 | end
57 |
58 | #
59 | # Parses an expression string and calculates the result.
60 | #
61 | # @param [String] string
62 | # The expression string to parse.
63 | #
64 | # @return [TestExpression]
65 | # The parsed test expression.
66 | #
67 | # @raise [ArgumentError]
68 | # Could not parse the test expression.
69 | #
70 | def self.parse(string)
71 | unless (match = string.match(%r{\A(\d+)\s*([\*/\+\-])\s*(\d+)\z}))
72 | raise(ArgumentError,"could not parse the expression: #{string.inspect}")
73 | end
74 |
75 | lvalue = match[1].to_i
76 | op = match[2]
77 | rvalue = match[3].to_i
78 |
79 | result = case op
80 | when '*' then lvalue * rvalue
81 | when '/' then lvalue / rvalue
82 | when '+' then lvalue + rvalue
83 | when '-' then lvalue - rvalue
84 | else
85 | raise(NotImplementedError,"unsupported expression operator: #{op.inspect}")
86 | end
87 |
88 | return new(string,result.to_s)
89 | end
90 |
91 | #
92 | # The test expression as a String.
93 | #
94 | # @return [String]
95 | # The {#string} value.
96 | #
97 | def to_s
98 | @string
99 | end
100 |
101 | end
102 | end
103 | end
104 | end
105 |
--------------------------------------------------------------------------------
/man/ronin-vulns-command-injection.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-command-injection 1 "May 2023" Ronin "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-command-injection - Scans URL(s) for Command Injection vulnerabilities
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns command-injection` [*options*] {*URL* ... \| `--input` *FILE*}
10 |
11 | ## DESCRIPTION
12 |
13 | Scans URL(s) for Command Injection vulnerabilities. The URLs to scan
14 | can be given as additional arguments or read from a file using the `--input`
15 | option.
16 |
17 | ## ARGUMENTS
18 |
19 | *URL*
20 | : A URL to scan.
21 |
22 | ## OPTIONS
23 |
24 | `--db` *NAME*
25 | : The database name to connect to. Defaults to `default` if not given.
26 |
27 | `--db-uri` *URI*
28 | : The database URI to connect to
29 | (ex: `postgres://user:password@host/db`).
30 |
31 | `--db-file` *PATH*
32 | : The sqlite3 database file to use.
33 |
34 | `--import`
35 | : Imports discovered vulnerabilities into the database.
36 |
37 | `--first`
38 | : Only find the first vulnerability for each URL.
39 |
40 | `-A`, `--all`
41 | : Find all vulnerabilities for each URL.
42 |
43 | `--print-curl`
44 | : Also prints an example `curl` command for each vulnerability.
45 |
46 | `--print-http`
47 | : Also prints an example HTTP request for each vulnerability.
48 |
49 | `-M`, `--request-method` `COPY`|`DELETE`|`GET`|`HEAD`|`LOCK`|`MKCOL`|`MOVE`|`OPTIONS`|`PATCH`|`POST`|`PROPFIND`|`PROPPATCH`|`PUT`|`TRACE`|`UNLOCK`
50 | : Sets the HTTP request method to use.
51 |
52 | `-H`, `--header` "*Name*: *value*"
53 | : Sets an additional header using the given *Name* and *value*.
54 |
55 | `-U`, `--user-agent-string` *STRING*
56 | : Sets the `User-Agent` header string.
57 |
58 | `-u`, `--user-agent` `chrome-linux`\|`chrome-macos`\|`chrome-windows`\|`chrome-iphone`\|`chrome-ipad`\|`chrome-android`\|`firefox-linux`\|`firefox-macos`\|`firefox-windows`\|`firefox-iphone`\|`firefox-ipad`\|`firefox-android`\|`safari-macos`\|`safari-iphone`\|`safari-ipad`\|`edge`
59 | : Sets the `User-Agent` header.
60 |
61 | `-C`, `--cookie` *COOKIE*
62 | : Sets the raw `Cookie` header.
63 |
64 | `-c`, `--cookie-param` *NAME*`=`*VALUE*
65 | : Sets an additional `Cookie` param using the given *NAME* and *VALUE*.
66 |
67 | `-R`, `--referer` *URL*
68 | : Sets the `Referer` header.
69 |
70 | `-F`, `--form-param` *NAME*`=`*VALUE*
71 | : Sets an additional form param using the given *NAME* and *VALUE*.
72 |
73 | `--test-query-param` *NAME*
74 | : Tests the URL query param name.
75 |
76 | `--test-all-query-params`
77 | : Test all URL query param names.
78 |
79 | `--test-header-name` *NAME*
80 | : Tests the HTTP Header name.
81 |
82 | `--test-cookie-param` *NAME*
83 | : Tests the HTTP Cookie name.
84 |
85 | `--test-all-cookie-params`
86 | : Test all Cookie param names.
87 |
88 | `--test-form-param` *NAME*
89 | : Tests the form param name.
90 |
91 | `-i`, `--input` *FILE*
92 | : Reads URLs from the given *FILE*.
93 |
94 | `-Q`, `--escape-quote` *CHAR*
95 | : The string quotation character to use to escape the command.
96 |
97 | `-O`, `--escape-operator` *CHAR*
98 | : The command operator character to use to escape the command.
99 |
100 | `-T`, `--terminator` *CHAR*
101 | : The command termination character to use.
102 |
103 | `-h`, `--help`
104 | : Print help information.
105 |
106 | ## AUTHOR
107 |
108 | Postmodern
109 |
110 | ## SEE ALSO
111 |
112 | [ronin-vulns-scan](ronin-vulns-scan.1.md)
--------------------------------------------------------------------------------
/man/ronin-vulns-rfi.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-rfi 1 "May 2022" Ronin "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-rfi - Scans URL(s) for Remote File Inclusion (RFI) vulnerabilities
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns rfi` [*options*] {*URL* ... \| `--input` *FILE*}
10 |
11 | ## DESCRIPTION
12 |
13 | Scans URL(s) for Remote File Inclusion (RFI) vulnerabilities. The URLs to scan
14 | can be given as additional arguments or read from a file using the `--input`
15 | option.
16 |
17 | ## ARGUMENTS
18 |
19 | *URL*
20 | : A URL to scan.
21 |
22 | ## OPTIONS
23 |
24 | `--db` *NAME*
25 | : The database name to connect to. Defaults to `default` if not given.
26 |
27 | `--db-uri` *URI*
28 | : The database URI to connect to
29 | (ex: `postgres://user:password@host/db`).
30 |
31 | `--db-file` *PATH*
32 | : The sqlite3 database file to use.
33 |
34 | `--import`
35 | : Imports discovered vulnerabilities into the database.
36 |
37 | `--first`
38 | : Only find the first vulnerability for each URL.
39 |
40 | `-A`, `--all`
41 | : Find all vulnerabilities for each URL.
42 |
43 | `--print-curl`
44 | : Also prints an example `curl` command for each vulnerability.
45 |
46 | `--print-http`
47 | : Also prints an example HTTP request for each vulnerability.
48 |
49 | `-M`, `--request-method` `COPY`|`DELETE`|`GET`|`HEAD`|`LOCK`|`MKCOL`|`MOVE`|`OPTIONS`|`PATCH`|`POST`|`PROPFIND`|`PROPPATCH`|`PUT`|`TRACE`|`UNLOCK`
50 | : Sets the HTTP request method to use.
51 |
52 | `-H`, `--header` "*Name*: *value*"
53 | : Sets an additional header using the given *Name* and *value*.
54 |
55 | `-U`, `--user-agent-string` *STRING*
56 | : Sets the `User-Agent` header string.
57 |
58 | `-u`, `--user-agent` `chrome-linux`\|`chrome-macos`\|`chrome-windows`\|`chrome-iphone`\|`chrome-ipad`\|`chrome-android`\|`firefox-linux`\|`firefox-macos`\|`firefox-windows`\|`firefox-iphone`\|`firefox-ipad`\|`firefox-android`\|`safari-macos`\|`safari-iphone`\|`safari-ipad`\|`edge`
59 | : Sets the `User-Agent` header.
60 |
61 | `-C`, `--cookie` *COOKIE*
62 | : Sets the raw `Cookie` header.
63 |
64 | `-c`, `--cookie-param` *NAME*`=`*VALUE*
65 | : Sets an additional `Cookie` param using the given *NAME* and *VALUE*.
66 |
67 | `-R`, `--referer` *URL*
68 | : Sets the `Referer` header.
69 |
70 | `-F`, `--form-param` *NAME*`=`*VALUE*
71 | : Sets an additional form param using the given *NAME* and *VALUE*.
72 |
73 | `--test-query-param` *NAME*
74 | : Tests the URL query param name.
75 |
76 | `--test-all-query-params`
77 | : Test all URL query param names.
78 |
79 | `--test-header-name` *NAME*
80 | : Tests the HTTP Header name.
81 |
82 | `--test-cookie-param` *NAME*
83 | : Tests the HTTP Cookie name.
84 |
85 | `--test-all-cookie-params`
86 | : Test all Cookie param names.
87 |
88 | `--test-form-param` *NAME*
89 | : Tests the form param name.
90 |
91 | `-i`, `--input` *FILE*
92 | : Reads URLs from the given *FILE*.
93 |
94 | `-B`, `--filter-bypass` `double-encode`\|`suffix-escape`\|`null-byte`
95 | : Optional filter-bypass strategy to use.
96 |
97 | `-S`, `--script-lang` `asp\|`asp.net`\|`coldfusion`\|`jsp`\|`php`\|`perl`
98 | : Explicitly specify the scripting language to test for.
99 |
100 | `-T`, `--test-script-url` *URL*
101 | : Use an alternative test script *URL*.
102 |
103 | `-h`, `--help`
104 | : Print help information.
105 |
106 | ## AUTHOR
107 |
108 | Postmodern
109 |
110 | ## SEE ALSO
111 |
112 | [ronin-vulns-scan](ronin-vulns-scan.1.md)
--------------------------------------------------------------------------------
/spec/cli/commands/lfi_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/lfi'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::Lfi do
6 | include_examples "man_page"
7 |
8 | let(:url) { 'https://example.com/page.php?id=1' }
9 |
10 | describe "#option_parser" do
11 | context "when the '--os' option is parsed" do
12 | let(:os) { :windows }
13 | let(:argv) { ['--os', os.to_s] }
14 |
15 | before { subject.option_parser.parse(argv) }
16 |
17 | it "must set the :os key in #scan_kwargs" do
18 | expect(subject.scan_kwargs[:os]).to eq(os)
19 | end
20 | end
21 |
22 | context "when the '--depth' option is parsed" do
23 | let(:depth) { 9 }
24 | let(:argv) { ['--depth', depth.to_s] }
25 |
26 | before { subject.option_parser.parse(argv) }
27 |
28 | it "must set the :depth key in #scan_kwargs" do
29 | expect(subject.scan_kwargs[:depth]).to eq(depth)
30 | end
31 | end
32 |
33 | context "when the '--filter-bypass' option is parsed" do
34 | let(:argv) { ['--filter-bypass', option_value] }
35 |
36 | before { subject.option_parser.parse(argv) }
37 |
38 | context "and it's value is 'null-byte'" do
39 | let(:option_value) { 'null-byte' }
40 | let(:filter_bypass) { :null_byte }
41 |
42 | it "must set the :filter_bypass key in #scan_kwargs to :null_byte" do
43 | expect(subject.scan_kwargs[:filter_bypass]).to eq(filter_bypass)
44 | end
45 | end
46 |
47 | context "and it's value is 'double-escape'" do
48 | let(:option_value) { 'double-escape' }
49 | let(:filter_bypass) { :double_escape }
50 |
51 | it "must set the :filter_bypass key in #scan_kwargs to :double_escape" do
52 | expect(subject.scan_kwargs[:filter_bypass]).to eq(filter_bypass)
53 | end
54 | end
55 |
56 | context "and it's value is 'base64'" do
57 | let(:option_value) { 'base64' }
58 | let(:filter_bypass) { :base64 }
59 |
60 | it "must set the :filter_bypass key in #scan_kwargs to :base64" do
61 | expect(subject.scan_kwargs[:filter_bypass]).to eq(filter_bypass)
62 | end
63 | end
64 |
65 | context "and it's value is 'rot13'" do
66 | let(:option_value) { 'rot13' }
67 | let(:filter_bypass) { :rot13 }
68 |
69 | it "must set the :filter_bypass key in #scan_kwargs to :rot13" do
70 | expect(subject.scan_kwargs[:filter_bypass]).to eq(filter_bypass)
71 | end
72 | end
73 |
74 | context "and it's value is 'zlib'" do
75 | let(:option_value) { 'zlib' }
76 | let(:filter_bypass) { :zlib }
77 |
78 | it "must set the :filter_bypass key in #scan_kwargs to :zlib" do
79 | expect(subject.scan_kwargs[:filter_bypass]).to eq(filter_bypass)
80 | end
81 | end
82 | end
83 | end
84 |
85 | describe "#scan_url" do
86 | it "must call Ronin::Vulns::LFI.scan with the URL and #scan_kwargs" do
87 | expect(Ronin::Vulns::LFI).to receive(:scan).with(
88 | url, **subject.scan_kwargs
89 | )
90 |
91 | subject.scan_url(url)
92 | end
93 | end
94 |
95 | describe "#test_url" do
96 | it "must call Ronin::Vulns::LFI.scan with the URL and #scan_kwargs" do
97 | expect(Ronin::Vulns::LFI).to receive(:test).with(
98 | url, **subject.scan_kwargs
99 | )
100 |
101 | subject.test_url(url)
102 | end
103 | end
104 | end
105 |
--------------------------------------------------------------------------------
/spec/sqli/error_pattern_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/sqli/error_pattern'
3 |
4 | describe Ronin::Vulns::SQLI::ErrorPattern do
5 | let(:regexps) do
6 | [
7 | /PostgreSQL.*ERROR/,
8 | /Warning.*\Wpg_/,
9 | /valid PostgreSQL result/,
10 | /Npgsql\./,
11 | /PG::SyntaxError:/,
12 | /org\.postgresql\.util\.PSQLException/,
13 | /ERROR:\s\ssyntax error at or near/,
14 | /ERROR: parser: parse error at or near/,
15 | /PostgreSQL query failed/,
16 | /org\.postgresql\.jdbc/,
17 | %r{Pdo[\./_\\]Pgsql},
18 | /PSQLException/
19 | ]
20 | end
21 | let(:regexp) { Regexp.union(regexps) }
22 |
23 | subject { described_class.new(regexp) }
24 |
25 | describe "#initialize" do
26 | it "must set #regexp" do
27 | expect(subject.regexp).to eq(regexp)
28 | end
29 | end
30 |
31 | describe ".[]" do
32 | subject { described_class }
33 |
34 | it "must return a #{described_class}" do
35 | expect(subject[*regexps]).to be_kind_of(described_class)
36 | end
37 |
38 | it "must union together the given Regexps and set #regexp" do
39 | expect(subject[*regexps].regexp).to eq(regexp)
40 | end
41 | end
42 |
43 | describe "#match" do
44 | context "when the #regexp matches the response body" do
45 | let(:error) { "PostgreSQL bla bla bla ERROR" }
46 | let(:response_body) do
47 | <<~HTML
48 |
49 |
50 | bla bla bla
51 | #{error} bla bla bla
52 | bla bla bla
53 |
54 |
55 | HTML
56 | end
57 |
58 | it "must return MatchData" do
59 | expect(subject.match(response_body)).to be_kind_of(MatchData)
60 | end
61 |
62 | it "must match the given response body against #regexp" do
63 | expect(subject.match(response_body)[0]).to eq(error)
64 | end
65 | end
66 |
67 | context "when the #regexp does not match the response body" do
68 | let(:response_body) do
69 | <<~HTML
70 |
71 |
72 | bla bla bla
73 | bla bla bla
74 | bla bla bla
75 |
76 |
77 | HTML
78 | end
79 |
80 | it "must return nil" do
81 | expect(subject.match(response_body)).to be(nil)
82 | end
83 | end
84 | end
85 |
86 | describe "#=~" do
87 | context "when the #regexp matches the response body" do
88 | let(:error) { "PostgreSQL bla bla bla ERROR" }
89 | let(:response_body) do
90 | <<~HTML
91 |
92 |
93 | bla bla bla
94 | #{error} bla bla bla
95 | bla bla bla
96 |
97 |
98 | HTML
99 | end
100 |
101 | it "must return the index of the match" do
102 | expect(subject =~ response_body).to eq(response_body.index(regexp))
103 | end
104 | end
105 |
106 | context "when the #regexp does not match the response body" do
107 | let(:response_body) do
108 | <<~HTML
109 |
110 |
111 | bla bla bla
112 | bla bla bla
113 | bla bla bla
114 |
115 |
116 | HTML
117 | end
118 |
119 | it "must return nil" do
120 | expect(subject =~ response_body).to be(nil)
121 | end
122 | end
123 | end
124 | end
125 |
--------------------------------------------------------------------------------
/spec/ssti/test_expression_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/ssti/test_expression'
3 |
4 | describe Ronin::Vulns::SSTI::TestExpression do
5 | let(:string) { '7*7' }
6 | let(:result) { '49' }
7 |
8 | subject { described_class.new(string,result) }
9 |
10 | describe "#initialize" do
11 | it "must set #string" do
12 | expect(subject.string).to eq(string)
13 | end
14 |
15 | it "must set #result" do
16 | expect(subject.result).to eq(result)
17 | end
18 | end
19 |
20 | describe ".parse" do
21 | subject { described_class }
22 |
23 | context "when given 'X*Z'" do
24 | let(:string) { '7*7' }
25 | let(:result) { '49' }
26 |
27 | it "must parse the string and calculate the result" do
28 | expr = subject.parse(string)
29 |
30 | expect(expr).to be_kind_of(described_class)
31 | expect(expr.string).to eq(string)
32 | expect(expr.result).to eq(result)
33 | end
34 | end
35 |
36 | context "when given 'X * Z'" do
37 | let(:string) { '7 * 7' }
38 | let(:result) { '49' }
39 |
40 | it "must parse the string and calculate the result" do
41 | expr = subject.parse(string)
42 |
43 | expect(expr).to be_kind_of(described_class)
44 | expect(expr.string).to eq(string)
45 | expect(expr.result).to eq(result)
46 | end
47 | end
48 |
49 | context "when given 'X/Z'" do
50 | let(:string) { '100/50' }
51 | let(:result) { '2' }
52 |
53 | it "must parse the string and calculate the result" do
54 | expr = subject.parse(string)
55 |
56 | expect(expr).to be_kind_of(described_class)
57 | expect(expr.string).to eq(string)
58 | expect(expr.result).to eq(result)
59 | end
60 | end
61 |
62 | context "when given 'X / Z'" do
63 | let(:string) { '100 / 50' }
64 | let(:result) { '2' }
65 |
66 | it "must parse the string and calculate the result" do
67 | expr = subject.parse(string)
68 |
69 | expect(expr).to be_kind_of(described_class)
70 | expect(expr.string).to eq(string)
71 | expect(expr.result).to eq(result)
72 | end
73 | end
74 |
75 | context "when given 'X+Z'" do
76 | let(:string) { '7+7' }
77 | let(:result) { '14' }
78 |
79 | it "must parse the string and calculate the result" do
80 | expr = subject.parse(string)
81 |
82 | expect(expr).to be_kind_of(described_class)
83 | expect(expr.string).to eq(string)
84 | expect(expr.result).to eq(result)
85 | end
86 | end
87 |
88 | context "when given 'X + Z'" do
89 | let(:string) { '7 + 7' }
90 | let(:result) { '14' }
91 |
92 | it "must parse the string and calculate the result" do
93 | expr = subject.parse(string)
94 |
95 | expect(expr).to be_kind_of(described_class)
96 | expect(expr.string).to eq(string)
97 | expect(expr.result).to eq(result)
98 | end
99 | end
100 |
101 | context "when given 'X-Z'" do
102 | let(:string) { '7-1' }
103 | let(:result) { '6' }
104 |
105 | it "must parse the string and calculate the result" do
106 | expr = subject.parse(string)
107 |
108 | expect(expr).to be_kind_of(described_class)
109 | expect(expr.string).to eq(string)
110 | expect(expr.result).to eq(result)
111 | end
112 | end
113 |
114 | context "when given 'X - Z'" do
115 | let(:string) { '7 - 1' }
116 | let(:result) { '6' }
117 |
118 | it "must parse the string and calculate the result" do
119 | expr = subject.parse(string)
120 |
121 | expect(expr).to be_kind_of(described_class)
122 | expect(expr.string).to eq(string)
123 | expect(expr.result).to eq(result)
124 | end
125 | end
126 | end
127 |
128 | describe "#to_s" do
129 | it "must return #string" do
130 | expect(subject.to_s).to eq(string)
131 | end
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/spec/reflected_xss/test_string_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/reflected_xss/test_string'
3 |
4 | describe Ronin::Vulns::ReflectedXSS::TestString do
5 | let(:string) { "abc" }
6 | let(:regexp) { /(a)(b)(c)/ }
7 |
8 | subject { described_class.new(string,regexp) }
9 |
10 | describe "#initialize" do
11 | it "must set #string" do
12 | expect(subject.string).to eq(string)
13 | end
14 |
15 | it "must set #regexp" do
16 | expect(subject.regexp).to eq(regexp)
17 | end
18 | end
19 |
20 | describe ".build" do
21 | let(:string) { "'\" /><&" }
22 |
23 | subject { described_class.build(string) }
24 |
25 | it "must return a new #{described_class}" do
26 | expect(subject).to be_kind_of(described_class)
27 | end
28 |
29 | it "must set #string" do
30 | expect(subject.string).to eq(string)
31 | end
32 |
33 | it "must build a Regexp that captures the characters but ignores their HTML/URI escaped versions" do
34 | expect(subject.regexp).to eq(
35 | %r{(?:(')|%27|&\#39;|\\')?(?:(")|%22|"|\\")?(?:(\ )|\+|%20| )?(?:(/)|%2F)?(?:(>)|%3E|>)?(?:(<)|%3C|<)?(?:(&)|%26|&)?}
36 | )
37 | end
38 | end
39 |
40 | describe "#wrap" do
41 | let(:prefix) { 'ABC' }
42 | let(:suffix) { 'XYZ' }
43 |
44 | subject { super().wrap(prefix,suffix) }
45 |
46 | it "must return a new #{described_class}" do
47 | expect(subject).to be_kind_of(described_class)
48 | end
49 |
50 | it "must prepend the prefix and append the suffix to the #string" do
51 | expect(subject.string).to eq("#{prefix}#{string}#{suffix}")
52 | end
53 |
54 | it "must prepend the prefix and append the suffix to the #regexp" do
55 | expect(subject.regexp).to eq(/#{prefix}#{regexp}#{suffix}/)
56 | end
57 |
58 | context "when the prefix contains Regexp special characters" do
59 | let(:prefix) { "ABC*CDE" }
60 | let(:escaped) { 'ABC\*CDE' }
61 |
62 | it "must escape the prefix before prepending it to the #regexp" do
63 | expect(subject.regexp).to eq(/#{escaped}#{regexp}#{suffix}/)
64 | end
65 | end
66 |
67 | context "when the suffix contains Regexp special characters" do
68 | let(:suffix) { "ABC*CDE" }
69 | let(:escaped) { 'ABC\*CDE' }
70 |
71 | it "must escape the prefix before prepending it to the #regexp" do
72 | expect(subject.regexp).to eq(/#{prefix}#{regexp}#{escaped}/)
73 | end
74 | end
75 | end
76 |
77 | describe "#match" do
78 | let(:prefix) { 'ABC' }
79 | let(:suffix) { 'XYZ' }
80 |
81 | subject { super().wrap(prefix,suffix) }
82 |
83 | context "when the #regexp matches the response body" do
84 | let(:response_body) do
85 | <<~HTML
86 |
87 |
88 | bla bla bla
89 | ABCabcXYZ
90 | bla bla bla
91 |
92 |
93 | HTML
94 | end
95 |
96 | it "must return MatchData" do
97 | expect(subject.match(response_body)).to be_kind_of(MatchData)
98 | end
99 |
100 | it "must match the given response body against #regexp" do
101 | match = subject.match(response_body)
102 |
103 | expect(match[0]).to eq('ABCabcXYZ')
104 | expect(match[1]).to eq('a')
105 | expect(match[2]).to eq('b')
106 | expect(match[3]).to eq('c')
107 | end
108 | end
109 |
110 | context "when the #regexp does not match the response body" do
111 | let(:response_body) do
112 | <<~HTML
113 |
114 |
115 | bla bla bla
116 | bla bla bla
117 | bla bla bla
118 |
119 |
120 | HTML
121 | end
122 |
123 | it "must return nil" do
124 | expect(subject.match(response_body)).to be(nil)
125 | end
126 | end
127 | end
128 |
129 | describe "#to_s" do
130 | it "must return the string" do
131 | expect(subject.to_s).to eq(string)
132 | end
133 | end
134 | end
135 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/importer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require 'ronin/db'
22 |
23 | module Ronin
24 | module Vulns
25 | #
26 | # Handles importing discovered {WebVuln web vulnerability} objects into
27 | # [ronin-db].
28 | #
29 | # [ronin-db]: https://github.com/ronin-rb/ronin-db#readme
30 | #
31 | # ## Examples
32 | #
33 | # require 'ronin/vulns/url_scanner'
34 | # require 'ronin/vulns/importer'
35 | #
36 | # Ronin::Vulns::URLScanner.scan(url) do |vuln|
37 | # Ronin::Vulns::Importer.import(vuln)
38 | # end
39 | #
40 | # @since 0.2.0
41 | #
42 | module Importer
43 | #
44 | # Imports a web vulnerability into database.
45 | #
46 | # @param [WebVuln] vuln
47 | # The web vulnerability to import.
48 | #
49 | # @yield [imported]
50 | # If a block is given, it will be passed the imported database records.
51 | #
52 | # @yieldparam [Ronin::DB::WebVuln] imported
53 | # The imported web vulnerability record.
54 | #
55 | # @return [Ronin::DB::WebVuln]
56 | # The imported web vuln record.
57 | #
58 | def self.import(vuln)
59 | imported_url = import_url(vuln.url)
60 |
61 | attributes = {
62 | url: imported_url,
63 | type: vuln.class.vuln_type,
64 |
65 | query_param: vuln.query_param,
66 | header_name: vuln.header_name,
67 | cookie_param: vuln.cookie_param,
68 | form_param: vuln.form_param,
69 | request_method: vuln.request_method
70 | }
71 |
72 | case vuln
73 | when LFI
74 | attributes[:lfi_os] = vuln.os
75 | attributes[:lfi_depth] = vuln.depth
76 | attributes[:lfi_filter_bypass] = vuln.filter_bypass
77 | when RFI
78 | attributes[:rfi_script_lang] = vuln.script_lang
79 | attributes[:rfi_filter_bypass] = vuln.filter_bypass
80 | when SQLI
81 | attributes[:sqli_escape_quote] = vuln.escape_quote
82 | attributes[:sqli_escape_parens] = vuln.escape_parens
83 | attributes[:sqli_terminate] = vuln.terminate
84 | when SSTI
85 | attributes[:ssti_escape_type] = vuln.escape_type
86 | when CommandInjection
87 | attributes[:command_injection_escape_quote] = vuln.escape_quote
88 | attributes[:command_injection_escape_operator] = vuln.escape_operator
89 | attributes[:command_injection_terminator] = vuln.terminator
90 | end
91 |
92 | imported_vuln = DB::WebVuln.transaction do
93 | DB::WebVuln.find_or_create_by(attributes)
94 | end
95 |
96 | yield imported_vuln if block_given?
97 | return imported_vuln
98 | end
99 |
100 | #
101 | # Imports a URL into the database.
102 | #
103 | # @param [URI, String] url
104 | # The URL to import.
105 | #
106 | # @return [Ronin::DB::URL]
107 | # The imported URL record.
108 | #
109 | def self.import_url(url)
110 | DB::URL.transaction do
111 | DB::URL.find_or_import(url)
112 | end
113 | end
114 | end
115 | end
116 | end
117 |
--------------------------------------------------------------------------------
/man/ronin-vulns-scan.1.md:
--------------------------------------------------------------------------------
1 | # ronin-vulns-scan 1 "May 2022" Ronin "User Manuals"
2 |
3 | ## NAME
4 |
5 | ronin-vulns-scan - Scans URL(s) for web vulnerabilities
6 |
7 | ## SYNOPSIS
8 |
9 | `ronin-vulns scan` [*options*] {*URL* ... \| `--input` *FILE*}
10 |
11 | ## DESCRIPTION
12 |
13 | Scans URL(s) for web vulnerabilities. The URLs to scan can be given as
14 | additional arguments or read from a file using the `--input` option.
15 |
16 | ## ARGUMENTS
17 |
18 | *URL*
19 | : A URL to scan.
20 |
21 | ## OPTIONS
22 |
23 | `--db` *NAME*
24 | : The database name to connect to. Defaults to `default` if not given.
25 |
26 | `--db-uri` *URI*
27 | : The database URI to connect to
28 | (ex: `postgres://user:password@host/db`).
29 |
30 | `--db-file` *PATH*
31 | : The sqlite3 database file to use.
32 |
33 | `--import`
34 | : Imports discovered vulnerabilities into the database.
35 |
36 | `--first`
37 | : Only find the first vulnerability for each URL.
38 |
39 | `-A`, `--all`
40 | : Find all vulnerabilities for each URL.
41 |
42 | `--print-curl`
43 | : Also prints an example `curl` command for each vulnerability.
44 |
45 | `--print-http`
46 | : Also prints an example HTTP request for each vulnerability.
47 |
48 | `-M`, `--request-method` `COPY`|`DELETE`|`GET`|`HEAD`|`LOCK`|`MKCOL`|`MOVE`|`OPTIONS`|`PATCH`|`POST`|`PROPFIND`|`PROPPATCH`|`PUT`|`TRACE`|`UNLOCK`
49 | : Sets the HTTP request method to use.
50 |
51 | `-H`, `--header` "*Name*: *value*"
52 | : Sets an additional header using the given *Name* and *value*.
53 |
54 | `-U`, `--user-agent-string` *STRING*
55 | : Sets the `User-Agent` header string.
56 |
57 | `-u`, `--user-agent` `chrome-linux`\|`chrome-macos`\|`chrome-windows`\|`chrome-iphone`\|`chrome-ipad`\|`chrome-android`\|`firefox-linux`\|`firefox-macos`\|`firefox-windows`\|`firefox-iphone`\|`firefox-ipad`\|`firefox-android`\|`safari-macos`\|`safari-iphone`\|`safari-ipad`\|`edge`
58 | : Sets the `User-Agent` header.
59 |
60 | `-C`, `--cookie` *COOKIE*
61 | : Sets the raw `Cookie` header.
62 |
63 | `-c`, `--cookie-param` *NAME*`=`*VALUE*
64 | : Sets an additional `Cookie` param using the given *NAME* and *VALUE*.
65 |
66 | `-R`, `--referer` *URL*
67 | : Sets the `Referer` header.
68 |
69 | `-F`, `--form-param` *NAME*`=`*VALUE*
70 | : Sets an additional form param using the given *NAME* and *VALUE*.
71 |
72 | `--test-query-param` *NAME*
73 | : Tests the URL query param name.
74 |
75 | `--test-all-query-params`
76 | : Test all URL query param names.
77 |
78 | `--test-header-name` *NAME*
79 | : Tests the HTTP Header name.
80 |
81 | `--test-cookie-param` *NAME*
82 | : Tests the HTTP Cookie name.
83 |
84 | `--test-all-cookie-params`
85 | : Test all Cookie param names.
86 |
87 | `--test-form-param` *NAME*
88 | : Tests the form param name.
89 |
90 | `-i`, `--input` *FILE*
91 | : Reads URLs from the given *FILE*.
92 |
93 | `--lfi-os` `unix`\|`windows`
94 | : Sets the OS to test for.
95 |
96 | `--lfi-depth` *NUM*
97 | : Sets the directory depth to escape up.
98 |
99 | `--lfi-filter-bypass` `null-byte`\|`double-escape`\|`base64`\|`rot13`\|`zlib`
100 | : Sets the filter bypass strategy to use.
101 |
102 | `--rfi-filter-bypass` `double-encode`\|`suffix-escape`\|`null-byte`
103 | : Optional filter-bypass strategy to use.
104 |
105 | `--rfi-script-lang` `asp`\|`asp.net`\|`coldfusion`\|`jsp`\|`php`\|`perl`
106 | : Explicitly specify the scripting language to test for.
107 |
108 | `--rfi-test-script-url` *URL*
109 | : Use an alternative test script URL.
110 |
111 | `--sqli-escape-quote`
112 | : Escapes quotation marks.
113 |
114 | `--sqli-escape-parens`
115 | : Escapes parenthesis.
116 |
117 | `--sqli-terminate`
118 | : Terminates the SQL expression with a `--`.
119 |
120 | `--ssti-test-expr` {*X*\**Y* \| *X*/*Z* \| *X*+*Y* \| *X*-*Y*}
121 | : Optional numeric test to use.
122 |
123 | `--open-redirect-url` *URL*
124 | : Optional test URL to try to redirect to.
125 |
126 | `-h`, `--help`
127 | : Print help information.
128 |
129 | ## AUTHOR
130 |
131 | Postmodern
132 |
133 | ## SEE ALSO
134 |
135 | [ronin-vulns-lfi](ronin-vulns-lfi.1.md) [ronin-vulns-rfi](ronin-vulns-rfi.1.md) [ronin-vulns-sqli](ronin-vulns-sqli.1.md) [ronin-vulns-ssti](ronin-vulns-ssti.1.md) [ronin-vulns-open-redirect](ronin-vulns-open-redirect.1.md) [ronin-vulns-reflected-xss](ronin-vulns-reflected-xss.1.md)
136 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/reflected_xss/test_string.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln'
22 |
23 | module Ronin
24 | module Vulns
25 | class ReflectedXSS < WebVuln
26 | #
27 | # A test string of characters to determine which special characters are
28 | # escaped/filtered and which are passed through.
29 | #
30 | # @api private
31 | #
32 | class TestString
33 |
34 | # The test string.
35 | #
36 | # @return [String]
37 | attr_reader :string
38 |
39 | # The test regexp to determine which special characters were
40 | # escaped/filtered and which were passed through unescaped.
41 | #
42 | # @return [Regexp]
43 | attr_reader :regexp
44 |
45 | #
46 | # Initializes the test string.
47 | #
48 | # @param [String] string
49 | # The test string.
50 | #
51 | # @param [Regexp] regexp
52 | # The test regexp to determine which special characters were
53 | # escaped/filtered and which were passed through unescaped.
54 | #
55 | def initialize(string,regexp)
56 | @string = string
57 | @regexp = regexp
58 | end
59 |
60 | # Special characters and their common escaped equivalents.
61 | ESCAPED_CHARS = {
62 | "'" => ['%27', ''', "\\'"],
63 | '"' => ['%22', '"', "\\\""],
64 | ' ' => ['+', '%20', ' '],
65 | '=' => ['%3D'],
66 | '/' => ['%2F'],
67 | '<' => ['%3C', '<'],
68 | '>' => ['%3E', '>'],
69 | '&' => ['%26', '&']
70 | }
71 |
72 | #
73 | # Builds a test string from a mapping of characters and their HTML
74 | # escaped equivalents.
75 | #
76 | # @param [String] chars
77 | # The characters for the test string.
78 | #
79 | # @return [TestString]
80 | # The built test string.
81 | #
82 | def self.build(chars)
83 | string = String.new
84 | regexp = String.new
85 |
86 | chars.each_char do |char|
87 | string << char
88 |
89 | regexp << "(?:(#{Regexp.escape(char)})"
90 |
91 | if (escaped_chars = ESCAPED_CHARS[char])
92 | escaped_chars.each do |string|
93 | regexp << "|#{Regexp.escape(string)}"
94 | end
95 | end
96 |
97 | regexp << ')?'
98 | end
99 |
100 | return new(string,Regexp.new(regexp))
101 | end
102 |
103 | #
104 | # Wraps the test string with a prefix and suffix.
105 | #
106 | # @param [String] prefix
107 | # The prefix string to prepend to the test string.
108 | #
109 | # @param [String] suffix
110 | # The suffix string to append to the test string.
111 | #
112 | # @return [TestString]
113 | # The new test string with the prefix and suffix.
114 | #
115 | def wrap(prefix,suffix)
116 | self.class.new(
117 | "#{prefix}#{@string}#{suffix}",
118 | /#{Regexp.escape(prefix)}#{@regexp}#{Regexp.escape(suffix)}/
119 | )
120 | end
121 |
122 | #
123 | # Matches the response body against {#regexp}.
124 | #
125 | # @param [String] body
126 | # The response body to try matching.
127 | #
128 | # @return [MatchData, nil]
129 | # The match data or `nil` if the body did not match {#regexp}.
130 | #
131 | def match(body)
132 | body.match(@regexp)
133 | end
134 |
135 | #
136 | # Converts the test string to a String.
137 | #
138 | # @return [String]
139 | # The {#string}.
140 | #
141 | def to_s
142 | @string
143 | end
144 |
145 | end
146 | end
147 | end
148 | end
149 |
--------------------------------------------------------------------------------
/spec/cli/commands/rfi_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'ronin/vulns/cli/commands/rfi'
3 | require_relative 'man_page_example'
4 |
5 | describe Ronin::Vulns::CLI::Commands::Rfi do
6 | include_examples "man_page"
7 |
8 | let(:url) { 'https://example.com/page.php?id=1' }
9 |
10 | describe "#option_parser" do
11 | context "when the '--filter-bypass' option is parsed" do
12 | let(:argv) { ['--filter-bypass', option_value] }
13 |
14 | before { subject.option_parser.parse(argv) }
15 |
16 | context "when the option value is 'double-encode'" do
17 | let(:option_value) { 'double-encode' }
18 | let(:filter_bypass) { :double_encode }
19 |
20 | it "must set the :filter_bypass key in #scan_kwargs to :double_encode" do
21 | expect(subject.scan_kwargs[:filter_bypass]).to eq(filter_bypass)
22 | end
23 | end
24 |
25 | context "when the option value is 'suffix-escape'" do
26 | let(:option_value) { 'suffix-escape' }
27 | let(:filter_bypass) { :suffix_escape }
28 |
29 | it "must set the :filter_bypass key in #scan_kwargs to :suffix_escape" do
30 | expect(subject.scan_kwargs[:filter_bypass]).to eq(filter_bypass)
31 | end
32 | end
33 |
34 | context "when the option value is 'null-byte'" do
35 | let(:option_value) { 'null-byte' }
36 | let(:filter_bypass) { :null_byte }
37 |
38 | it "must set the :filter_bypass key in #scan_kwargs to :null_byte" do
39 | expect(subject.scan_kwargs[:filter_bypass]).to eq(filter_bypass)
40 | end
41 | end
42 | end
43 |
44 | context "when the '--script-lang' option is parsed" do
45 | let(:argv) { ['--script-lang', option_value] }
46 |
47 | before { subject.option_parser.parse(argv) }
48 |
49 | context "when the option value is 'asp'" do
50 | let(:option_value) { 'asp' }
51 | let(:script_lang) { :asp }
52 |
53 | it "must set the :script_lang key in #scan_kwargs to :asp" do
54 | expect(subject.scan_kwargs[:script_lang]).to eq(script_lang)
55 | end
56 | end
57 |
58 | context "when the option value is 'asp.net'" do
59 | let(:option_value) { 'asp.net' }
60 | let(:script_lang) { :asp_net }
61 |
62 | it "must set the :script_lang key in #scan_kwargs to :asp_net" do
63 | expect(subject.scan_kwargs[:script_lang]).to eq(script_lang)
64 | end
65 | end
66 |
67 | context "when the option value is 'coldfusion'" do
68 | let(:option_value) { 'coldfusion' }
69 | let(:script_lang) { :cold_fusion }
70 |
71 | it "must set the :script_lang key in #scan_kwargs to :cold_fusion" do
72 | expect(subject.scan_kwargs[:script_lang]).to eq(script_lang)
73 | end
74 | end
75 |
76 | context "when the option value is 'jsp'" do
77 | let(:option_value) { 'jsp' }
78 | let(:script_lang) { :jsp }
79 |
80 | it "must set the :script_lang key in #scan_kwargs to :jsp" do
81 | expect(subject.scan_kwargs[:script_lang]).to eq(script_lang)
82 | end
83 | end
84 |
85 | context "when the option value is 'php'" do
86 | let(:option_value) { 'php' }
87 | let(:script_lang) { :php }
88 |
89 | it "must set the :script_lang key in #scan_kwargs to :php" do
90 | expect(subject.scan_kwargs[:script_lang]).to eq(script_lang)
91 | end
92 | end
93 |
94 | context "when the option value is 'perl'" do
95 | let(:option_value) { 'perl' }
96 | let(:script_lang) { :perl }
97 |
98 | it "must set the :script_lang key in #scan_kwargs to :perl" do
99 | expect(subject.scan_kwargs[:script_lang]).to eq(script_lang)
100 | end
101 | end
102 | end
103 |
104 | context "when the '--test-script-url' option is parsed" do
105 | let(:test_script_url) { 'https://other-website.com/path/to/rfi_test.php' }
106 | let(:argv) { ['--test-script-url', test_script_url] }
107 |
108 | before { subject.option_parser.parse(argv) }
109 |
110 | it "must set the :test_script_url key in #scan_kwargs" do
111 | expect(subject.scan_kwargs[:test_script_url]).to eq(test_script_url)
112 | end
113 | end
114 | end
115 |
116 | describe "#scan_url" do
117 | it "must call Ronin::Vulns::RFI.scan with the URL and #scan_kwargs" do
118 | expect(Ronin::Vulns::RFI).to receive(:scan).with(
119 | url, **subject.scan_kwargs
120 | )
121 |
122 | subject.scan_url(url)
123 | end
124 | end
125 |
126 | describe "#test_url" do
127 | it "must call Ronin::Vulns::RFI.scan with the URL and #scan_kwargs" do
128 | expect(Ronin::Vulns::RFI).to receive(:test).with(
129 | url, **subject.scan_kwargs
130 | )
131 |
132 | subject.test_url(url)
133 | end
134 | end
135 | end
136 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/open_redirect.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative 'web_vuln'
22 |
23 | require 'chars'
24 | require 'cgi'
25 |
26 | module Ronin
27 | module Vulns
28 | #
29 | # Represents an Open Redirect vulnerability.
30 | #
31 | # ## Features
32 | #
33 | # * Checks 301, 302, 303, 307, and 308 HTTP redirects.
34 | # * Checks `meta` refresh redirects.
35 | # * Includes random alpha-numeric data in the test values.
36 | #
37 | class OpenRedirect < WebVuln
38 |
39 | # The desired redirect URL to use in the test.
40 | #
41 | # @return [String]
42 | attr_reader :test_url
43 |
44 | #
45 | # Initializes the Open Redirect vulnerability.
46 | #
47 | # @param [String, URI::HTTP] url
48 | # The URL to exploit.
49 | #
50 | # @param [String] test_url
51 | # The desired redirect URL to test the URL with.
52 | #
53 | def initialize(url, test_url: self.class.random_test_url, **kwargs)
54 | super(url,**kwargs)
55 |
56 | @test_url = test_url
57 | end
58 |
59 | #
60 | # Generates a random redirect URL to use in tests.
61 | #
62 | # @return [String]
63 | # A random URL to https://ronin-rb.dev/vulns/open_redirect.html.
64 | #
65 | # @api private
66 | #
67 | def self.random_test_url
68 | "https://ronin-rb.dev/vulns/open_redirect.html?id=#{Chars::ALPHA_NUMERIC.random_string(5)}"
69 | end
70 |
71 | #
72 | # Tests whether the URL has a vulnerable Open Redirect.
73 | #
74 | # @return [Boolean]
75 | #
76 | def vulnerable?
77 | response = exploit(@test_url)
78 |
79 | case response.code
80 | when '301', '302', '303', '307', '308'
81 | if (locations = response.get_fields('Location'))
82 | escaped_test_url = Regexp.escape(@test_url)
83 | regexp = /\A#{escaped_test_url}.*\z/
84 |
85 | locations.last =~ regexp
86 | end
87 | else
88 | content_type = response.content_type
89 |
90 | if content_type && content_type.include?('text/html')
91 | escaped_test_url = Regexp.escape(CGI.escapeHTML(@test_url))
92 |
93 | regexp = %r{
94 | ]*
122 | )
123 | )
124 | \s*
125 | # /> or / >
126 | (?:/\s*)?>
127 | }xi
128 |
129 | response.body =~ regexp
130 | end
131 | end
132 | end
133 |
134 | #
135 | # Returns the type or kind of vulnerability.
136 | #
137 | # @return [Symbol]
138 | #
139 | # @note
140 | # This is used internally to map an vulnerability class to a printable
141 | # type.
142 | #
143 | # @api private
144 | #
145 | # @abstract
146 | #
147 | def self.vuln_type
148 | :open_redirect
149 | end
150 |
151 | end
152 | end
153 | end
154 |
--------------------------------------------------------------------------------
/ChangeLog.md:
--------------------------------------------------------------------------------
1 | ### 0.2.1 / 2025-02-14
2 |
3 | * Added the `base64` gem as a dependency for Bundler and Ruby 3.4.0.
4 | * Added missing `ronin/vulns` Ruby file.
5 | * Use `require_relative` to improve load times.
6 | * Documentation fixes.
7 |
8 | #### CLI
9 |
10 | * Fixed a bug in the `ronin-vulns irb` command where the `ronin/vulns` Ruby file
11 | was missing.
12 |
13 | ### 0.2.0 / 2024-07-22
14 |
15 | * Require [ronin-db] ~> 0.2
16 | * Added {Ronin::Vulns::Importer}.
17 | * Added the `user_agent:` keyword argument to
18 | {Ronin::Vulns::WebVuln#initialize}.
19 | * Added {Ronin::Vulns::WebVuln#user_agent}.
20 | * Added {Ronin::Vulns::CommandInjection}.
21 | * Added the `command_injection:` keyword argument to
22 | {Ronin::Vulns::URLScanner.scan}.
23 | * Added {Ronin::Vulns::RFI#script_lang}.
24 | * Support inferring the {Ronin::Vulns::RFI#script_lang} from the URL given to
25 | {Ronin::Vulns::RFI#initialize}.
26 | * Bruteforce test every different kind of RFI test URL in
27 | {Ronin::Vulns::RFI#vulnerable?} if a test script URL was not given or the
28 | {Ronin::Vulns::RFI#script_lang} cannot be inferred from the given URL.
29 | * Allow the `escape_type:` keyword argument for {Ronin::Vulns::SSTI#initialize}
30 | to accept a Symbol value to specify the specific
31 | Server-Side-Template-Injection interpolation syntax:
32 | * `:double_curly_braces` - `{{expression}}`
33 | * `:dollar_curly_braces` - `${expression}`
34 | * `:dollar_double_curly_braces` - `${{expression}}`
35 | * `:pound_curly_braces` - `#{expression}`
36 | * `:angle_brackets_percent` - `<%= expression %>`
37 |
38 | #### CLI
39 |
40 | * Added the `ronin-vulns command-injection` command.
41 | * Added the `ronin-vulns irb` command.
42 | * Added the `ronin-vulns completion` command to install shell completion files
43 | for all `ronin-vulns` commands for Bash and Zsh shells.
44 | * Added the `-H,--request-method` option to all commands.
45 | * Added the `--user-agent` and `--user-agent-string` options to all commands.
46 | * Added the `--test-all-form-params` option to all commands.
47 | * Added the `--print-curl` and `--print-http` options to all commands.
48 | * Added the `--import` option to all commands.
49 | * Print a summary of all vulnerabilities found after scanning a URL, in addition
50 | to logging messages indicating when a new vulnerability has just been found.
51 | * Use hyphenated values for the `--lfi-filter-bypass` option in the
52 | `ronin-vulns scan` command and `--filter-bypass` option in the
53 | `ronin-vulns lfi` command.
54 |
55 | ### 0.1.5 / 2024-06-19
56 |
57 | * Improve the accuracy of {Ronin::Vulns::OpenRedirect#vulnerable?} when
58 | detecting open redirects in meta-refresh HTML tags.
59 | * Match the test URL when it ends with `?...`, `&...`, or `&...`.
60 | * Detect when the test URL has an additional string appended to it
61 | (ex: `.html`). The appended string can easily be bypassed by adding a
62 | `?`, `&`, or `#` character to the end of the test URL.
63 |
64 | ### 0.1.4 / 2023-09-19
65 |
66 | #### CLI
67 |
68 | * Improved the performance of `ronin-vulns` commands when scanning multiple URLs
69 | or a file of URLs by not rebuilding an identical
70 | {Ronin::Vulns::CLI::WebVulnCommand#scan_kwargs} for each URL.
71 | * Allow the `--cookie "..."` option to be repeated multiple times and merge the
72 | cookie strings together.
73 | * Allow the `--cookie-param NAME=VALUE` option to be used with the
74 | `--cookie "..."` option and merge the cookie values together.
75 | * Print vulnerable param names in single quotes.
76 |
77 | ### 0.1.3 / 2023-07-07
78 |
79 | * Fixed a bug in {Ronin::Vulns::SSTI.scan} where when called without `escape:`
80 | it would not return all found vulnerabilities.
81 | * Fixed a bug in {Ronin::Vulns::SQLI.scan} where repeat requests would be sent
82 | even if `escape_quote:`, `escape_parens:`, or `terminate:` keyword arguments
83 | are given.
84 | * Improved {Ronin::Vulns::ReflectedXSS::Context} to detect when the XSS occurs
85 | after or *inside of* an HTML comment.
86 |
87 | ### 0.1.2 / 2023-03-01
88 |
89 | * Require `ronin-support` ~> 1.0, >= 1.0.1
90 |
91 | #### CLI
92 |
93 | * Validate that given URLs start with either `http://` or `https://`, and print
94 | an error message otherwise.
95 | * Print a `No vulnerabilities found` message when no vulnerabilities were
96 | discovered.
97 |
98 | ### 0.1.1 / 2023-02-02
99 |
100 | * Fixed typo in {Ronin::Vulns::CLI::WebVulnCommand#process_url} which effected
101 | the `ronin-vulns lfi` command and others.
102 |
103 | ### 0.1.0 / 2023-02-01
104 |
105 | * Initial release:
106 | * Require `ruby` >= 3.0.0.
107 | * Supports testing for:
108 | * Local File Inclusion (LFI)
109 | * Remote File Inclusion (RFI)
110 | * PHP
111 | * ASP Class / ASP.NET
112 | * JSP
113 | * ColdFusion
114 | * Perl
115 | * SQL Injection (SQLi)
116 | * Reflected Cross Site Scripting (XSS)
117 | * Server Side Template Injection (SSTI)
118 | * Open Redirects
119 | * Supports testing:
120 | * URL query parameters.
121 | * HTTP Headers.
122 | * HTTP `Cookie` parameters.
123 | * Form parameters.
124 |
125 | [ronin-db]: https://github.com/ronin-rb/ronin-db#readme
126 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/reflected_xss.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln_command'
22 | require_relative '../../reflected_xss'
23 |
24 | module Ronin
25 | module Vulns
26 | class CLI
27 | module Commands
28 | #
29 | # Scans URL(s) for Reflected Cross Site Scripting (XSS) vulnerabilities.
30 | #
31 | # ## Usage
32 | #
33 | # ronin-vulns reflected-xss [options] {URL ... | --input FILE}
34 | #
35 | # ## Options
36 | #
37 | # --db NAME The database to connect to (Default: default)
38 | # --db-uri URI The database URI to connect to
39 | # --db-file PATH The sqlite3 database file to use
40 | # --import Imports discovered vulnerabilities into the database
41 | # --first Only find the first vulnerability for each URL
42 | # -A, --all Find all vulnerabilities for each URL
43 | # --print-curl Also prints an example curl command for each vulnerability
44 | # --print-http Also prints an example HTTP request for each vulnerability
45 | # -M COPY|DELETE|GET|HEAD|LOCK|MKCOL|MOVE|OPTIONS|PATCH|POST|PROPFIND|PROPPATCH|PUT|TRACE|UNLOCK,
46 | # --request-method The HTTP request method to use
47 | # -H, --header "Name: value" Sets an additional header
48 | # -U, --user-agent-string STRING Sets the User-Agent header
49 | # -u chrome-linux|chrome-macos|chrome-windows|chrome-iphone|chrome-ipad|chrome-android|firefox-linux|firefox-macos|firefox-windows|firefox-iphone|firefox-ipad|firefox-android|safari-macos|safari-iphone|safari-ipad|edge,
50 | # --user-agent Sets the User-Agent to use
51 | # -C, --cookie COOKIE Sets the raw Cookie header
52 | # -c, --cookie-param NAME=VALUE Sets an additional cookie param
53 | # -R, --referer URL Sets the Referer header
54 | # -F, --form-param NAME=VALUE Sets an additional form param
55 | # --test-query-param NAME Tests the URL query param name
56 | # --test-all-query-params Test all URL query param names
57 | # --test-header-name NAME Tests the HTTP Header name
58 | # --test-cookie-param NAME Tests the HTTP Cookie name
59 | # --test-all-cookie-params Test all Cookie param names
60 | # --test-form-param NAME Tests the form param name
61 | # --test-all-form-params Test all form param names
62 | # -i, --input FILE Reads URLs from the list file
63 | # -h, --help Print help information
64 | #
65 | # ## Arguments
66 | #
67 | # [URL ...] The URL(s) to scan
68 | #
69 | class ReflectedXss < WebVulnCommand
70 |
71 | usage '[options] {URL ... | --input FILE}'
72 |
73 | description 'Scans URL(s) for Reflected Cross Site Scripting (XSS) vulnerabilities'
74 |
75 | man_page 'ronin-vulns-reflected-xss.1'
76 |
77 | #
78 | # Scans a URL for Reflected XSS vulnerabilities.
79 | #
80 | # @param [String] url
81 | # The URL to scan.
82 | #
83 | # @yield [vuln]
84 | # The given block will be passed each discovered Reflected XSS
85 | # vulnerability.
86 | #
87 | # @yieldparam [Vulns::ReflectedXSS] vuln
88 | # A Reflected XSS vulnerability discovered on the URL.
89 | #
90 | def scan_url(url,&block)
91 | Vulns::ReflectedXSS.scan(url,**scan_kwargs,&block)
92 | end
93 |
94 | #
95 | # Tests a URL for Reflected XSS vulnerabilities.
96 | #
97 | # @param [String] url
98 | # The URL to test.
99 | #
100 | # @return [Vulns::ReflectedXSS, nil]
101 | # The first Reflected XSS vulnerability discovered on the URL.
102 | #
103 | def test_url(url,&block)
104 | Vulns::ReflectedXSS.test(url,**scan_kwargs)
105 | end
106 |
107 | end
108 | end
109 | end
110 | end
111 | end
112 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/open_redirect.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln_command'
22 | require_relative '../../open_redirect'
23 |
24 | module Ronin
25 | module Vulns
26 | class CLI
27 | module Commands
28 | #
29 | # Scans URL(s) for Open Redirect vulnerabilities.
30 | #
31 | # ## Usage
32 | #
33 | # ronin-vulns open-redirect [options] {URL ... | --input FILE}
34 | #
35 | # ## Options
36 | #
37 | # --db NAME The database to connect to (Default: default)
38 | # --db-uri URI The database URI to connect to
39 | # --db-file PATH The sqlite3 database file to use
40 | # --import Imports discovered vulnerabilities into the database
41 | # --first Only find the first vulnerability for each URL
42 | # -A, --all Find all vulnerabilities for each URL
43 | # --print-curl Also prints an example curl command for each vulnerability
44 | # --print-http Also prints an example HTTP request for each vulnerability
45 | # -M COPY|DELETE|GET|HEAD|LOCK|MKCOL|MOVE|OPTIONS|PATCH|POST|PROPFIND|PROPPATCH|PUT|TRACE|UNLOCK,
46 | # --request-method The HTTP request method to use
47 | # -H, --header "Name: value" Sets an additional header
48 | # -U, --user-agent-string STRING Sets the User-Agent header
49 | # -u chrome-linux|chrome-macos|chrome-windows|chrome-iphone|chrome-ipad|chrome-android|firefox-linux|firefox-macos|firefox-windows|firefox-iphone|firefox-ipad|firefox-android|safari-macos|safari-iphone|safari-ipad|edge,
50 | # --user-agent Sets the User-Agent to use
51 | # -C, --cookie COOKIE Sets the raw Cookie header
52 | # -c, --cookie-param NAME=VALUE Sets an additional cookie param
53 | # -R, --referer URL Sets the Referer header
54 | # -F, --form-param NAME=VALUE Sets an additional form param
55 | # --test-query-param NAME Tests the URL query param name
56 | # --test-all-query-params Test all URL query param names
57 | # --test-header-name NAME Tests the HTTP Header name
58 | # --test-cookie-param NAME Tests the HTTP Cookie name
59 | # --test-all-cookie-params Test all Cookie param names
60 | # --test-form-param NAME Tests the form param name
61 | # --test-all-form-params Test all form param names
62 | # -i, --input FILE Reads URLs from the list file
63 | # -T, --test-url URL Optional test URL to try to redirect to
64 | # -h, --help Print help information
65 | #
66 | # ## Arguments
67 | #
68 | # [URL ...] The URL(s) to scan
69 | #
70 | class OpenRedirect < WebVulnCommand
71 |
72 | usage '[options] {URL ... | --input FILE}'
73 |
74 | option :test_url, short: '-T',
75 | value: {
76 | type: String,
77 | usage: 'URL'
78 | },
79 | desc: 'Optional test URL to try to redirect to' do |test_url|
80 | scan_kwargs[:test_url] = test_url
81 | end
82 |
83 | description 'Scans URL(s) for Open Redirect vulnerabilities'
84 |
85 | man_page 'ronin-vulns-open-redirect.1'
86 |
87 | #
88 | # Scans a URL for Open Redirect vulnerabilities.
89 | #
90 | # @param [String] url
91 | # The URL to scan.
92 | #
93 | # @yield [vuln]
94 | # The given block will be passed each discovered OpenRedirect
95 | # vulnerability.
96 | #
97 | # @yieldparam [Vulns::OpenRedirect] vuln
98 | # A OpenRedirect vulnerability discovered on the URL.
99 | #
100 | def scan_url(url,&block)
101 | Vulns::OpenRedirect.scan(url,**scan_kwargs,&block)
102 | end
103 |
104 | #
105 | # Tests a URL for Open Redirect vulnerabilities.
106 | #
107 | # @param [String] url
108 | # The URL to test.
109 | #
110 | # @return [Vulns::OpenRedirect, nil]
111 | # The first Open Redirect vulnerability discovered on the URL.
112 | #
113 | def test_url(url,&block)
114 | Vulns::OpenRedirect.test(url,**scan_kwargs)
115 | end
116 |
117 | end
118 | end
119 | end
120 | end
121 | end
122 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/ssti.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln_command'
22 | require_relative '../../ssti'
23 |
24 | module Ronin
25 | module Vulns
26 | class CLI
27 | module Commands
28 | #
29 | # Scans URL(s) for Server Side Template Injection (SSTI)
30 | # vulnerabilities.
31 | #
32 | # ## Usage
33 | #
34 | # ronin-vulns ssti [options] {URL ... | --input FILE}
35 | #
36 | # ## Options
37 | #
38 | # --db NAME The database to connect to (Default: default)
39 | # --db-uri URI The database URI to connect to
40 | # --db-file PATH The sqlite3 database file to use
41 | # --import Imports discovered vulnerabilities into the database
42 | # --first Only find the first vulnerability for each URL
43 | # -A, --all Find all vulnerabilities for each URL
44 | # --print-curl Also prints an example curl command for each vulnerability
45 | # --print-http Also prints an example HTTP request for each vulnerability
46 | # -M COPY|DELETE|GET|HEAD|LOCK|MKCOL|MOVE|OPTIONS|PATCH|POST|PROPFIND|PROPPATCH|PUT|TRACE|UNLOCK,
47 | # --request-method The HTTP request method to use
48 | # -H, --header "Name: value" Sets an additional header
49 | # -U, --user-agent-string STRING Sets the User-Agent header
50 | # -u chrome-linux|chrome-macos|chrome-windows|chrome-iphone|chrome-ipad|chrome-android|firefox-linux|firefox-macos|firefox-windows|firefox-iphone|firefox-ipad|firefox-android|safari-macos|safari-iphone|safari-ipad|edge,
51 | # --user-agent Sets the User-Agent to use
52 | # -C, --cookie COOKIE Sets the raw Cookie header
53 | # -c, --cookie-param NAME=VALUE Sets an additional cookie param
54 | # -R, --referer URL Sets the Referer header
55 | # -F, --form-param NAME=VALUE Sets an additional form param
56 | # --test-query-param NAME Tests the URL query param name
57 | # --test-all-query-params Test all URL query param names
58 | # --test-header-name NAME Tests the HTTP Header name
59 | # --test-cookie-param NAME Tests the HTTP Cookie name
60 | # --test-all-cookie-params Test all Cookie param names
61 | # --test-form-param NAME Tests the form param name
62 | # --test-all-form-params Test all form param names
63 | # -i, --input FILE Reads URLs from the list file
64 | # -T {X*Y | X/Z | X+Y | X-Y}, Optional numeric test to use
65 | # --test-expr
66 | # -h, --help Print help information
67 | #
68 | # ## Arguments
69 | #
70 | # [URL ...] The URL(s) to scan
71 | #
72 | class Ssti < WebVulnCommand
73 |
74 | usage '[options] {URL ... | --input FILE}'
75 |
76 | option :test_expr, short: '-T',
77 | value: {
78 | type: %r{\A\d+\s*[\*/\+\-]\s*\d+\z},
79 | usage: '{X*Y | X/Z | X+Y | X-Y}'
80 | },
81 | desc: 'Optional numeric test to use' do |expr|
82 | scan_kwargs[:test_expr] = Vulns::SSTI::TestExpression.parse(expr)
83 | end
84 |
85 | description 'Scans URL(s) for Server Side Template Injection (SSTI) vulnerabilities'
86 |
87 | man_page 'ronin-vulns-ssti.1'
88 |
89 | #
90 | # Scans a URL for SSTI vulnerabilities.
91 | #
92 | # @param [String] url
93 | # The URL to scan.
94 | #
95 | # @yield [vuln]
96 | # The given block will be passed each discovered SSTI vulnerability.
97 | #
98 | # @yieldparam [Vulns::SSTI] vuln
99 | # A SSTI vulnerability discovered on the URL.
100 | #
101 | def scan_url(url,&block)
102 | Vulns::SSTI.scan(url,**scan_kwargs,&block)
103 | end
104 |
105 | #
106 | # Tests a URL for SSTI vulnerabilities.
107 | #
108 | # @param [String] url
109 | # The URL to test.
110 | #
111 | # @return [Vulns::SSTI, nil]
112 | # The first SSTI vulnerability discovered on the URL.
113 | #
114 | def test_url(url,&block)
115 | Vulns::SSTI.test(url,**scan_kwargs)
116 | end
117 |
118 | end
119 | end
120 | end
121 | end
122 | end
123 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/sqli.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln_command'
22 | require_relative '../../sqli'
23 |
24 | module Ronin
25 | module Vulns
26 | class CLI
27 | module Commands
28 | #
29 | # Scans URL(s) for SQL injection (SQLi) vulnerabilities.
30 | #
31 | # ## Usage
32 | #
33 | # ronin-vulns sqli [options] {URL ... | --input FILE}
34 | #
35 | # ## Options
36 | #
37 | # --db NAME The database to connect to (Default: default)
38 | # --db-uri URI The database URI to connect to
39 | # --db-file PATH The sqlite3 database file to use
40 | # --import Imports discovered vulnerabilities into the database
41 | # --first Only find the first vulnerability for each URL
42 | # -A, --all Find all vulnerabilities for each URL
43 | # --print-curl Also prints an example curl command for each vulnerability
44 | # --print-http Also prints an example HTTP request for each vulnerability
45 | # -M COPY|DELETE|GET|HEAD|LOCK|MKCOL|MOVE|OPTIONS|PATCH|POST|PROPFIND|PROPPATCH|PUT|TRACE|UNLOCK,
46 | # --request-method The HTTP request method to use
47 | # -H, --header "Name: value" Sets an additional header
48 | # -U, --user-agent-string STRING Sets the User-Agent header
49 | # -u chrome-linux|chrome-macos|chrome-windows|chrome-iphone|chrome-ipad|chrome-android|firefox-linux|firefox-macos|firefox-windows|firefox-iphone|firefox-ipad|firefox-android|safari-macos|safari-iphone|safari-ipad|edge,
50 | # --user-agent Sets the User-Agent to use
51 | # -C, --cookie COOKIE Sets the raw Cookie header
52 | # -c, --cookie-param NAME=VALUE Sets an additional cookie param
53 | # -R, --referer URL Sets the Referer header
54 | # -F, --form-param NAME=VALUE Sets an additional form param
55 | # --test-query-param NAME Tests the URL query param name
56 | # --test-all-query-params Test all URL query param names
57 | # --test-header-name NAME Tests the HTTP Header name
58 | # --test-cookie-param NAME Tests the HTTP Cookie name
59 | # --test-all-cookie-params Test all Cookie param names
60 | # --test-form-param NAME Tests the form param name
61 | # --test-all-form-params Test all form param names
62 | # -i, --input FILE Reads URLs from the list file
63 | # -Q, --escape-quote Escapes quotation marks
64 | # -P, --escape-parens Escapes parenthesis
65 | # -T, --terminate Terminates the SQL expression with a --
66 | # -h, --help Print help information
67 | #
68 | # ## Arguments
69 | #
70 | # [URL ...] The URL(s) to scan
71 | #
72 | class Sqli < WebVulnCommand
73 |
74 | usage '[options] {URL ... | --input FILE}'
75 |
76 | option :escape_quote, short: '-Q', desc: 'Escapes quotation marks' do
77 | scan_kwargs[:escape_quote] = true
78 | end
79 |
80 | option :escape_parens, short: '-P', desc: 'Escapes parenthesis' do
81 | scan_kwargs[:escape_parens] = true
82 | end
83 |
84 | option :terminate, short: '-T', desc: 'Terminates the SQL expression with a --' do
85 | scan_kwargs[:terminate] = true
86 | end
87 |
88 | description 'Scans URL(s) for SQL injection (SQLi) vulnerabilities'
89 |
90 | man_page 'ronin-vulns-sqli.1'
91 |
92 | #
93 | # Scans a URL for SQLi vulnerabilities.
94 | #
95 | # @param [String] url
96 | # The URL to scan.
97 | #
98 | # @yield [vuln]
99 | # The given block will be passed each discovered SQLi vulnerability.
100 | #
101 | # @yieldparam [Vulns::SQLI] vuln
102 | # A SQLi vulnerability discovered on the URL.
103 | #
104 | def scan_url(url,&block)
105 | Vulns::SQLI.scan(url,**scan_kwargs,&block)
106 | end
107 |
108 | #
109 | # Tests a URL for SQLi vulnerabilities.
110 | #
111 | # @param [String] url
112 | # The URL to test.
113 | #
114 | # @return [Vulns::SQLI, nil]
115 | # The first SQLi vulnerability discovered on the URL.
116 | #
117 | def test_url(url,&block)
118 | Vulns::SQLI.test(url,**scan_kwargs)
119 | end
120 |
121 | end
122 | end
123 | end
124 | end
125 | end
126 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/reflected_xss.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative 'web_vuln'
22 | require_relative 'reflected_xss/test_string'
23 | require_relative 'reflected_xss/context'
24 |
25 | require 'set'
26 |
27 | module Ronin
28 | module Vulns
29 | #
30 | # Represents a (Reflected) Cross Site Scripting (XSS) vulnerability.
31 | #
32 | # ## Features
33 | #
34 | # * Tests a URL with just one HTTP request (per param).
35 | # * Tests which HTML special characters are allowed.
36 | # * Identifies the context, tag name, and/or attribute name of the XSS.
37 | # * Determines viability of XSS based on the context.
38 | # * Includes random data in the test values.
39 | #
40 | class ReflectedXSS < WebVuln
41 |
42 | # The characters that are allowed and will not be escaped or filtered.
43 | #
44 | # @return [Set, nil]
45 | attr_reader :allowed_chars
46 |
47 | # The context the XSS occurred in.
48 | #
49 | # @return [Context, nil]
50 | attr_reader :context
51 |
52 | #
53 | # Tests the test string by sending an HTTP request with the test string
54 | # embedded.
55 | #
56 | # @param [TestString] test_string
57 | #
58 | # @yield [body, match]
59 | # If the response was `text/html` and the test string appears (at least
60 | # partially) in the response body, the response body and match data will
61 | # be yielded.
62 | #
63 | # @yieldparam [String] body
64 | # The response body.
65 | #
66 | # @yieldparam [MatchData] match
67 | # The matched data for the test string.
68 | #
69 | # @api private
70 | #
71 | def test_string(test_string)
72 | test_string = test_string.wrap(random_value,random_value)
73 |
74 | response = exploit("#{original_value}#{test_string}")
75 | content_type = response.content_type
76 | body = response.body
77 |
78 | if content_type && content_type.include?('text/html')
79 | if (match = test_string.match(body))
80 | yield body, match
81 | end
82 | end
83 | end
84 |
85 | #
86 | # Tests whether characters in the test string will be escaped/filtered or
87 | # passed through and updates {#allowed_chars}.
88 | #
89 | # @param [TestString] test_string
90 | # The test string to send.
91 | #
92 | # @yield [body, match]
93 | # If a block is given, it will be passed the response body and the
94 | # regular expression match data, if the response contains the test
95 | # string.
96 | #
97 | # @yieldparam [String] body
98 | # The response body.
99 | #
100 | # @yieldparam [MatchData] match
101 | # The matched data for the test string.
102 | #
103 | # @api private
104 | #
105 | def test_chars(test_string)
106 | test_string(test_string) do |body,match|
107 | @allowed_chars ||= Set.new
108 | @allowed_chars.merge(match.captures.compact)
109 |
110 | yield body, match if block_given?
111 | end
112 | end
113 |
114 | # HTML special characters to test.
115 | HTML_TEST_STRING = TestString.build("'\"= /><")
116 |
117 | #
118 | # Tests which HTML characters are accepted or escaped/filtered.
119 | #
120 | # @yield [body, match]
121 | # If a block is given, it will be passed the response body and the
122 | # regular expression match data, if the response contains the test
123 | # string.
124 | #
125 | # @yieldparam [String] body
126 | # The response body.
127 | #
128 | # @yieldparam [MatchData] match
129 | # The matched data for the test string.
130 | #
131 | # @api private
132 | #
133 | def test_html_chars(&block)
134 | test_chars(HTML_TEST_STRING,&block)
135 | end
136 |
137 | #
138 | # Tests whether the URL is vulnerable to (Reflected) Cross Site Scripting
139 | # (XSS).
140 | #
141 | # @return [Boolean]
142 | # Indicates whether the URL is vulnerable to (Reflected) Cross Site
143 | # Scripting (XSS).
144 | #
145 | # @note
146 | # If the URL is vulnerable, {#allowed_chars} and {#context} will be set.
147 | #
148 | def vulnerable?
149 | # test HTML special characters
150 | test_html_chars do |body,match|
151 | xss_index = match.begin(0)
152 |
153 | # determine the contents which the XSS occurs
154 | if (@context = Context.identify(body,xss_index))
155 | # determine whether enough special HTML characters are allowed to
156 | # escape the context which the XSS occurs.
157 | return @context.viable?(@allowed_chars)
158 | end
159 | end
160 |
161 | return false
162 | end
163 |
164 | #
165 | # Returns the type or kind of vulnerability.
166 | #
167 | # @return [Symbol]
168 | #
169 | # @note
170 | # This is used internally to map an vulnerability class to a printable
171 | # type.
172 | #
173 | # @api private
174 | #
175 | # @abstract
176 | #
177 | def self.vuln_type
178 | :reflected_xss
179 | end
180 |
181 | end
182 | end
183 | end
184 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/printing.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require 'ronin/core/cli/logging'
22 |
23 | require 'command_kit/printing/indent'
24 |
25 | module Ronin
26 | module Vulns
27 | class CLI
28 | #
29 | # Mixin that adds methods for logging and printing discovered web
30 | # vulnerabilities.
31 | #
32 | # @since 0.2.0
33 | #
34 | module Printing
35 | include Core::CLI::Logging
36 | include CommandKit::Printing::Indent
37 |
38 | # Known vulnerability types and their printable names.
39 | VULN_TYPES = {
40 | command_injection: 'Command Injection',
41 | open_redirect: 'Open Redirect',
42 | reflected_xss: 'reflected XSS',
43 |
44 | lfi: 'LFI',
45 | rfi: 'RFI',
46 | sqli: 'SQLi',
47 | ssti: 'SSTI'
48 | }
49 |
50 | #
51 | # Returns the printable vulnerability type for the vulnerability object.
52 | #
53 | # @param [Vuln] vuln
54 | #
55 | # @return [String]
56 | #
57 | def vuln_type(vuln)
58 | VULN_TYPES.fetch(vuln.class.vuln_type)
59 | end
60 |
61 | #
62 | # Determines the param type that the web vulnerability occurs in.
63 | #
64 | # @param [WebVuln] vuln
65 | #
66 | # @return [String, nil]
67 | #
68 | def vuln_param_type(vuln)
69 | if vuln.query_param then 'query param'
70 | elsif vuln.header_name then 'Header'
71 | elsif vuln.cookie_param then 'Cookie param'
72 | elsif vuln.form_param then 'form param'
73 | end
74 | end
75 |
76 | #
77 | # Determines the param name that the web vulnerability occurs in.
78 | #
79 | # @param [WebVuln] vuln
80 | #
81 | # @return [String, nil]
82 | #
83 | def vuln_param_name(vuln)
84 | if vuln.query_param then vuln.query_param
85 | elsif vuln.header_name then vuln.header_name
86 | elsif vuln.cookie_param then vuln.cookie_param
87 | elsif vuln.form_param then vuln.form_param
88 | end
89 | end
90 |
91 | #
92 | # Prints a log message about a newly discovered web vulnerability.
93 | #
94 | # @param [WebVuln] vuln
95 | # The web vulnerability to log.
96 | #
97 | def log_vuln(vuln)
98 | vuln_type = vuln_type(vuln)
99 | param_type = vuln_param_type(vuln)
100 | param_name = vuln_param_name(vuln)
101 |
102 | if (param_type && param_name)
103 | log_warn "Found #{vuln_type} on #{vuln.url} via #{param_type} '#{param_name}'!"
104 | else
105 | log_warn "Found #{vuln_type} on #{vuln.url}!"
106 | end
107 | end
108 |
109 | #
110 | # Prints detailed information about a discovered web vulnerability.
111 | #
112 | # @param [WebVuln] vuln
113 | # The web vulnerability to log.
114 | #
115 | # @param [Boolean] print_curl
116 | # Prints an example `curl` command to trigger the web vulnerability.
117 | #
118 | # @param [Boolean] print_http
119 | # Prints an example HTTP request to trigger the web vulnerability.
120 | #
121 | # @since 0.2.0
122 | #
123 | def print_vuln(vuln, print_curl: false, print_http: false)
124 | vuln_type = vuln_type(vuln)
125 | param_type = vuln_param_type(vuln)
126 | param_name = vuln_param_name(vuln)
127 |
128 | if (param_type && param_name)
129 | puts "#{colors.bold(colors.bright_red(vuln_type))} on #{colors.bold(colors.bright_white(vuln.url))} via #{colors.bold(colors.bright_white(param_type))} '#{colors.bold(colors.bright_red(param_name))}'"
130 | else
131 | puts "#{colors.bold(colors.red(vuln_type))} on #{colors.bold(colors.bright_white(vuln.url))}"
132 | end
133 |
134 | if print_curl || print_http
135 | puts
136 |
137 | if print_curl
138 | puts " #{vuln.to_curl}"
139 | puts
140 | end
141 |
142 | if print_http
143 | vuln.to_http.each_line(chomp: true) do |line|
144 | puts " #{line}"
145 | end
146 | puts
147 | end
148 | end
149 | end
150 |
151 | #
152 | # Print a summary of all web vulnerabilities found.
153 | #
154 | # @param [Array] vulns
155 | # The discovered web vulnerabilities.
156 | #
157 | # @param [Boolean] print_curl
158 | # Prints an example `curl` command to trigger the web vulnerability.
159 | #
160 | # @param [Boolean] print_http
161 | # Prints an example HTTP request to trigger the web vulnerability.
162 | #
163 | # @since 0.2.0
164 | #
165 | def print_vulns(vulns, print_curl: false, print_http: false)
166 | if vulns.empty?
167 | puts colors.green("No vulnerabilities found")
168 | else
169 | puts colors.bold(colors.bright_red('Vulnerabilities found!'))
170 | puts
171 |
172 | indent do
173 | vulns.each do |vuln|
174 | print_vuln(vuln, print_curl: print_curl,
175 | print_http: print_http)
176 | end
177 | end
178 | puts unless (print_curl || print_http)
179 | end
180 | end
181 | end
182 | end
183 | end
184 | end
185 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/lfi.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln_command'
22 | require_relative '../../lfi'
23 |
24 | module Ronin
25 | module Vulns
26 | class CLI
27 | module Commands
28 | #
29 | # Scans URL(s) for Local File Inclusion (LFI) vulnerabilities
30 | #
31 | # ## Usage
32 | #
33 | # ronin-vulns lfi [options] {URL ... | --input FILE}
34 | #
35 | # ## Options
36 | #
37 | # --db NAME The database to connect to (Default: default)
38 | # --db-uri URI The database URI to connect to
39 | # --db-file PATH The sqlite3 database file to use
40 | # --import Imports discovered vulnerabilities into the database
41 | # --first Only find the first vulnerability for each URL
42 | # -A, --all Find all vulnerabilities for each URL
43 | # --print-curl Also prints an example curl command for each vulnerability
44 | # --print-http Also prints an example HTTP request for each vulnerability
45 | # -M COPY|DELETE|GET|HEAD|LOCK|MKCOL|MOVE|OPTIONS|PATCH|POST|PROPFIND|PROPPATCH|PUT|TRACE|UNLOCK,
46 | # --request-method The HTTP request method to use
47 | # -H, --header "Name: value" Sets an additional header
48 | # -U, --user-agent-string STRING Sets the User-Agent header
49 | # -u chrome-linux|chrome-macos|chrome-windows|chrome-iphone|chrome-ipad|chrome-android|firefox-linux|firefox-macos|firefox-windows|firefox-iphone|firefox-ipad|firefox-android|safari-macos|safari-iphone|safari-ipad|edge,
50 | # --user-agent Sets the User-Agent to use
51 | # -C, --cookie COOKIE Sets the raw Cookie header
52 | # -c, --cookie-param NAME=VALUE Sets an additional cookie param
53 | # -R, --referer URL Sets the Referer header
54 | # -F, --form-param NAME=VALUE Sets an additional form param
55 | # --test-query-param NAME Tests the URL query param name
56 | # --test-all-query-params Test all URL query param names
57 | # --test-header-name NAME Tests the HTTP Header name
58 | # --test-cookie-param NAME Tests the HTTP Cookie name
59 | # --test-all-cookie-params Test all Cookie param names
60 | # --test-form-param NAME Tests the form param name
61 | # --test-all-form-params Test all form param names
62 | # -i, --input FILE Reads URLs from the list file
63 | # -O, --os unix|windows Sets the OS to test for
64 | # -D, --depth COUNT Sets the directory depth to escape up
65 | # -B null-byte|double-escape|base64|rot13|zlib,
66 | # --filter-bypass Sets the filter bypass strategy to use
67 | # -h, --help Print help information
68 | #
69 | # ## Arguments
70 | #
71 | # [URL ...] The URL(s) to scan
72 | #
73 | class Lfi < WebVulnCommand
74 |
75 | usage '[options] {URL ... | --input FILE}'
76 |
77 | option :os, short: '-O',
78 | value: {
79 | type: [:unix, :windows]
80 | },
81 | desc: 'Sets the OS to test for' do |os|
82 | scan_kwargs[:os] = os
83 | end
84 |
85 | option :depth, short: '-D',
86 | value: {
87 | type: Integer,
88 | usage: 'COUNT'
89 | },
90 | desc: 'Sets the directory depth to escape up' do |depth|
91 | scan_kwargs[:depth] = depth
92 | end
93 |
94 | option :filter_bypass, short: '-B',
95 | value: {
96 | type: {
97 | 'null-byte' => :null_byte,
98 | 'double-escape' => :double_escape,
99 | 'base64' => :base64,
100 | 'rot13' => :rot13,
101 | 'zlib' => :zlib
102 | }
103 | },
104 | desc: 'Sets the filter bypass strategy to use' do |filter_bypass|
105 | scan_kwargs[:filter_bypass] = filter_bypass
106 | end
107 |
108 | description 'Scans URL(s) for Local File Inclusion (LFI) vulnerabilities'
109 |
110 | man_page 'ronin-vulns-lfi.1'
111 |
112 | #
113 | # Scans a URL for LFI vulnerabilities.
114 | #
115 | # @param [String] url
116 | # The URL to scan.
117 | #
118 | # @yield [vuln]
119 | # The given block will be passed each discovered LFI vulnerability.
120 | #
121 | # @yieldparam [Vulns::LFI] vuln
122 | # A LFI vulnerability discovered on the URL.
123 | #
124 | def scan_url(url,&block)
125 | Vulns::LFI.scan(url,**scan_kwargs,&block)
126 | end
127 |
128 | #
129 | # Tests a URL for LFI vulnerabilities.
130 | #
131 | # @param [String] url
132 | # The URL to test.
133 | #
134 | # @return [Vulns::LFI, nil]
135 | # The first LFI vulnerability discovered on the URL.
136 | #
137 | def test_url(url,&block)
138 | Vulns::LFI.test(url,**scan_kwargs)
139 | end
140 |
141 | end
142 | end
143 | end
144 | end
145 | end
146 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/command_injection.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln_command'
22 | require_relative '../../command_injection'
23 |
24 | module Ronin
25 | module Vulns
26 | class CLI
27 | module Commands
28 | #
29 | # Scans URL(s) for Command Injection vulnerabilities.
30 | #
31 | # ## Usage
32 | #
33 | # ronin-vulns command-injection [options] {URL ... | --input FILE}
34 | #
35 | # ## Options
36 | #
37 | # --db NAME The database to connect to (Default: default)
38 | # --db-uri URI The database URI to connect to
39 | # --db-file PATH The sqlite3 database file to use
40 | # --import Imports discovered vulnerabilities into the database
41 | # --first Only find the first vulnerability for each URL
42 | # -A, --all Find all vulnerabilities for each URL
43 | # --print-curl Also prints an example curl command for each vulnerability
44 | # --print-http Also prints an example HTTP request for each vulnerability
45 | # -M COPY|DELETE|GET|HEAD|LOCK|MKCOL|MOVE|OPTIONS|PATCH|POST|PROPFIND|PROPPATCH|PUT|TRACE|UNLOCK,
46 | # --request-method The HTTP request method to use
47 | # -H, --header "Name: value" Sets an additional header
48 | # -U, --user-agent-string STRING Sets the User-Agent header
49 | # -u chrome-linux|chrome-macos|chrome-windows|chrome-iphone|chrome-ipad|chrome-android|firefox-linux|firefox-macos|firefox-windows|firefox-iphone|firefox-ipad|firefox-android|safari-macos|safari-iphone|safari-ipad|edge,
50 | # --user-agent Sets the User-Agent to use
51 | # -C, --cookie COOKIE Sets the raw Cookie header
52 | # -c, --cookie-param NAME=VALUE Sets an additional cookie param
53 | # -R, --referer URL Sets the Referer header
54 | # -F, --form-param NAME=VALUE Sets an additional form param
55 | # --test-query-param NAME Tests the URL query param name
56 | # --test-all-query-params Test all URL query param names
57 | # --test-header-name NAME Tests the HTTP Header name
58 | # --test-cookie-param NAME Tests the HTTP Cookie name
59 | # --test-all-cookie-params Test all Cookie param names
60 | # --test-form-param NAME Tests the form param name
61 | # -i, --input FILE Reads URLs from the list file
62 | # -Q, --escape-quote CHAR The string quotation character to use to escape the command
63 | # -O, --escape-operator CHAR The command operator character to use to escape the command
64 | # -T, --terminator CHAR The command termination character to use
65 | # -h, --help Print help information
66 | #
67 | # ## Arguments
68 | #
69 | # [URL ...] The URL(s) to scan
70 | #
71 | # @since 0.2.0
72 | #
73 | class CommandInjection < WebVulnCommand
74 |
75 | usage '[options] {URL ... | --input FILE}'
76 |
77 | # Regex for matching a single `CHAR` option value.
78 | CHAR_REGEX = /./
79 |
80 | option :escape_quote, short: '-Q',
81 | value: {
82 | type: CHAR_REGEX,
83 | usage: 'CHAR'
84 | },
85 | desc: 'The string quotation character to use to escape the command'
86 |
87 | option :escape_operator, short: '-O',
88 | value: {
89 | type: CHAR_REGEX,
90 | usage: 'CHAR'
91 | },
92 | desc: 'The command operator character to use to escape the command'
93 |
94 | option :terminator, short: '-T',
95 | value: {
96 | type: CHAR_REGEX,
97 | usage: 'CHAR'
98 | },
99 | desc: 'The command termination character to use'
100 |
101 | description 'Scans URL(s) for Command Injection vulnerabilities'
102 |
103 | man_page 'ronin-vulns-command-injection.1'
104 |
105 | #
106 | # Keyword arguments for `Vulns::CommandInjection.scan` and
107 | # `Vulns::CommandInjection.test`.
108 | #
109 | # @return [Hash{Symbol => Object}]
110 | #
111 | def scan_kwargs
112 | kwargs = super()
113 |
114 | if options[:escape_quote]
115 | kwargs[:escape_quote] = options[:escape_quote]
116 | end
117 |
118 | if options[:escape_operator]
119 | kwargs[:escape_operator] = options[:escape_operator]
120 | end
121 |
122 | if options[:terminator]
123 | kwargs[:terminator] = options[:terminator]
124 | end
125 |
126 | return kwargs
127 | end
128 |
129 | #
130 | # Scans a URL for Command Injection vulnerabilities.
131 | #
132 | # @param [String] url
133 | # The URL to scan.
134 | #
135 | # @yield [vuln]
136 | # The given block will be passed each discovered Command Injection
137 | # vulnerability.
138 | #
139 | # @yieldparam [Vulns::CommandInjection] vuln
140 | # A Command Injection vulnerability discovered on the URL.
141 | #
142 | def scan_url(url,&block)
143 | Vulns::CommandInjection.scan(url,**scan_kwargs,&block)
144 | end
145 |
146 | #
147 | # Tests a URL for Command Injection vulnerabilities.
148 | #
149 | # @param [String] url
150 | # The URL to test.
151 | #
152 | # @return [Vulns::CommandInjection, nil]
153 | # The first Command Injection vulnerability discovered on the URL.
154 | #
155 | def test_url(url,&block)
156 | Vulns::CommandInjection.test(url,**scan_kwargs)
157 | end
158 |
159 | end
160 | end
161 | end
162 | end
163 | end
164 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/cli/commands/rfi.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln_command'
22 | require_relative '../../rfi'
23 |
24 | module Ronin
25 | module Vulns
26 | class CLI
27 | module Commands
28 | #
29 | # Scans URL(s) for Remote File Inclusion (RFI) vulnerabilities.
30 | #
31 | # ## Usage
32 | #
33 | # ronin-vulns rfi [options] {URL ... | --input FILE}
34 | #
35 | # ## Options
36 | #
37 | # --db NAME The database to connect to (Default: default)
38 | # --db-uri URI The database URI to connect to
39 | # --db-file PATH The sqlite3 database file to use
40 | # --import Imports discovered vulnerabilities into the database
41 | # --first Only find the first vulnerability for each URL
42 | # -A, --all Find all vulnerabilities for each URL
43 | # --print-curl Also prints an example curl command for each vulnerability
44 | # --print-http Also prints an example HTTP request for each vulnerability
45 | # -M COPY|DELETE|GET|HEAD|LOCK|MKCOL|MOVE|OPTIONS|PATCH|POST|PROPFIND|PROPPATCH|PUT|TRACE|UNLOCK,
46 | # --request-method The HTTP request method to use
47 | # -H, --header "Name: value" Sets an additional header
48 | # -U, --user-agent-string STRING Sets the User-Agent header
49 | # -u chrome-linux|chrome-macos|chrome-windows|chrome-iphone|chrome-ipad|chrome-android|firefox-linux|firefox-macos|firefox-windows|firefox-iphone|firefox-ipad|firefox-android|safari-macos|safari-iphone|safari-ipad|edge,
50 | # --user-agent Sets the User-Agent to use
51 | # -C, --cookie COOKIE Sets the raw Cookie header
52 | # -c, --cookie-param NAME=VALUE Sets an additional cookie param
53 | # -R, --referer URL Sets the Referer header
54 | # -F, --form-param NAME=VALUE Sets an additional form param
55 | # --test-query-param NAME Tests the URL query param name
56 | # --test-all-query-params Test all URL query param names
57 | # --test-header-name NAME Tests the HTTP Header name
58 | # --test-cookie-param NAME Tests the HTTP Cookie name
59 | # --test-all-cookie-params Test all Cookie param names
60 | # --test-form-param NAME Tests the form param name
61 | # --test-all-form-params Test all form param names
62 | # -i, --input FILE Reads URLs from the list file
63 | # -B double-encode|suffix-escape|null-byte,
64 | # --filter-bypass Optional filter-bypass strategy to use
65 | # -S asp|asp.net|coldfusion|jsp|php|perl,
66 | # --script-lang Explicitly specify the scripting language to test for
67 | # -T, --test-script-url URL Use an alternative test script URL
68 | # -h, --help Print help information
69 | #
70 | # ## Arguments
71 | #
72 | # [URL ...] The URL(s) to scan
73 | #
74 | class Rfi < WebVulnCommand
75 |
76 | usage '[options] {URL ... | --input FILE}'
77 |
78 | option :filter_bypass, short: '-B',
79 | value: {
80 | type: {
81 | 'double-encode' => :double_encode,
82 | 'suffix-escape' => :suffix_escape,
83 | 'null-byte' => :null_byte
84 | }
85 | },
86 | desc: 'Optional filter-bypass strategy to use' do |filter_bypass|
87 | scan_kwargs[:filter_bypass] = filter_bypass
88 | end
89 |
90 | option :script_lang, short: '-S',
91 | value: {
92 | type: {
93 | 'asp' => :asp,
94 | 'asp.net' => :asp_net,
95 | 'coldfusion' => :cold_fusion,
96 | 'jsp' => :jsp,
97 | 'php' => :php,
98 | 'perl' => :perl
99 | }
100 | },
101 | desc: 'Explicitly specify the scripting language to test for' do |script_lang|
102 | scan_kwargs[:script_lang] = script_lang
103 | end
104 |
105 | option :test_script_url, short: '-T',
106 | value: {
107 | type: String,
108 | usage: 'URL'
109 | },
110 | desc: 'Use an alternative test script URL' do |test_script_url|
111 | scan_kwargs[:test_script_url] = test_script_url
112 | end
113 |
114 | description 'Scans URL(s) for Remote File Inclusion (RFI) vulnerabilities'
115 |
116 | man_page 'ronin-vulns-rfi.1'
117 |
118 | #
119 | # Scans a URL for RFI vulnerabilities.
120 | #
121 | # @param [String] url
122 | # The URL to scan.
123 | #
124 | # @yield [vuln]
125 | # The given block will be passed each discovered RFI vulnerability.
126 | #
127 | # @yieldparam [Vulns::RFI] vuln
128 | # A RFI vulnerability discovered on the URL.
129 | #
130 | def scan_url(url,&block)
131 | Vulns::RFI.scan(url,**scan_kwargs,&block)
132 | end
133 |
134 | #
135 | # Tests a URL for RFI vulnerabilities.
136 | #
137 | # @param [String] url
138 | # The URL to test.
139 | #
140 | # @return [Vulns::RFI, nil]
141 | # The first RFI vulnerability discovered on the URL.
142 | #
143 | def test_url(url,&block)
144 | Vulns::RFI.test(url,**scan_kwargs)
145 | end
146 |
147 | end
148 | end
149 | end
150 | end
151 | end
152 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/ssti.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative 'web_vuln'
22 | require_relative 'ssti/test_expression'
23 |
24 | module Ronin
25 | module Vulns
26 | #
27 | # Represents a Server Side Template Injection (SSTI) vulnerability.
28 | #
29 | class SSTI < WebVuln
30 |
31 | # List of common Server Side Template Injection (SSTI) escapes.
32 | #
33 | # @api private
34 | ESCAPES = {
35 | nil => nil, # does not escape the expression
36 |
37 | double_curly_braces: ->(expression) { "{{#{expression}}}" },
38 | dollar_curly_braces: ->(expression) { "${#{expression}}" },
39 | dollar_double_curly_braces: ->(expression) { "${{#{expression}}}" },
40 | pound_curly_braces: ->(expression) { "\#{#{expression}}" },
41 | angle_brackets_percent: ->(expression) { "<%= #{expression} %>" }
42 | }
43 |
44 | # The type of SSTI escape used.
45 | #
46 | # @return [:double_curly_braces, :dollar_curly_braces, :dollar_double_curly_braces, :pound_curly_braces, :angle_brackets_percent, :custom, nil]
47 | #
48 | # @since 0.2.0
49 | attr_reader :escape_type
50 |
51 | # How to escape the payload so that it's executed.
52 | #
53 | # @return [Proc, nil]
54 | # The proc that will accept a String and return a String, or `nil` to
55 | # indicate that the payload will not be escaped.
56 | attr_reader :escape
57 |
58 | # The test expression to use when testing the URL for SSTI.
59 | #
60 | # @return [TestExpression]
61 | attr_reader :test_expr
62 |
63 | #
64 | # Initializes the Server Side Template Injection (SSTI) vulnerability.
65 | #
66 | # @param [String, URI::HTTP] url
67 | # The URL to exploit.
68 | #
69 | # @param [:double_curly_braces, :dollar_curly_braces, :dollar_double_curly_braces, :pound_curly_braces, :angle_brackets_percent, :custom, Proc, nil] escape
70 | # How to escape a given payload. Either a proc that will accept a String
71 | # and return a String, a Symbol describing the template syntax to use,
72 | # or `nil` to indicate that the payload will not be escaped.
73 | #
74 | # @param [TestExpression] test_expr
75 | # The test payload and expected result to check for when testing the URL
76 | # for SSTI.
77 | #
78 | # @raise [ArgumentError]
79 | # An unknown `escape_type:` or `escape:` value was given, or no
80 | # `test_expr:` was given.
81 | #
82 | def initialize(url, escape: nil,
83 | test_expr: self.class.random_test,
84 | **kwargs)
85 | super(url,**kwargs)
86 |
87 | case escape
88 | when Symbol
89 | @escape_type = escape
90 | @escape = ESCAPES.fetch(escape) do
91 | raise(ArgumentError,"unknown template syntax: #{escape_type.inspect}")
92 | end
93 | when Proc
94 | @escape_type = :custom
95 | @escape = escape
96 | when nil # no-op
97 | else
98 | raise(ArgumentError,"invalid escape type, must be a Symbol, Proc, or nil: #{escape.inspect}")
99 | end
100 |
101 | @test_expr = test_expr
102 |
103 | unless @test_expr
104 | raise(ArgumentError,"must specify both a test expression")
105 | end
106 | end
107 |
108 | #
109 | # Generates a random `N*M` SSTI test.
110 | #
111 | # @return [TestExpression]
112 | # A random test expression.
113 | #
114 | def self.random_test
115 | int1 = rand(1_000..1_999)
116 | int2 = rand(1_000..1_999)
117 |
118 | string = "#{int1}*#{int2}"
119 | result = (int1 * int2).to_s
120 |
121 | return TestExpression.new(string,result)
122 | end
123 |
124 | #
125 | # Tests the URL and a specific query param, header name, cookie param, or
126 | # form param for a Server Side Template Injection (SSTI) vulnerability
127 | # by enumerating over various SSTI syntaxes.
128 | #
129 | # @param [URI::HTTP] url
130 | # The URL to test.
131 | #
132 | # @param [Array, Symbol, Proc, nil] escape
133 | # The escape method to use. If `escape:` is not given, then all escapes
134 | # names in {ESCAPES} will be tested..
135 | #
136 | # @param [Ronin::Support::Network::HTTP] http
137 | # The HTTP session to use for testing the URL.
138 | #
139 | # @param [Hash{Symbol => Object}] kwargs
140 | # Additional keyword arguments for {#initialize}.
141 | #
142 | # @option kwargs [Symbol, String, true, nil] :query_param
143 | # The query param name to test.
144 | #
145 | # @option kwargs [Symbol, String, nil] :header_name
146 | # The header name to test.
147 | #
148 | # @option kwargs [Symbol, String, true, nil] :cookie_param
149 | # The cookie param name to test.
150 | #
151 | # @option kwargs [Symbol, String, nil] :form_param
152 | # The form param name to test.
153 | #
154 | # @return [SSTI, nil]
155 | # The first discovered web vulnerability for the specific query param,
156 | # header name, cookie param, or form param.
157 | #
158 | # @api private
159 | #
160 | # @since 0.2.0
161 | #
162 | def self.test_param(url, escape: ESCAPES.keys,
163 | # initialize keyword arguments
164 | http: , **kwargs)
165 | Array(escape).each do |escape_value|
166 | vuln = new(url, escape: escape_value, http: http, **kwargs)
167 |
168 | return vuln if vuln.vulnerable?
169 | end
170 |
171 | return nil
172 | end
173 |
174 | #
175 | # Escapes the payload using {#escape}.
176 | #
177 | # @param [String] payload
178 | #
179 | # @return [String]
180 | #
181 | def encode_payload(payload)
182 | if @escape then @escape.call(payload)
183 | else payload
184 | end
185 | end
186 |
187 | #
188 | # Determine whether the URL is vulnerable to Server Side Template
189 | # Injection (SSTI).
190 | #
191 | # @return [Boolean]
192 | #
193 | def vulnerable?
194 | response = exploit(@test_expr.string)
195 | body = response.body
196 |
197 | return body.include?(@test_expr.result)
198 | end
199 |
200 | #
201 | # Returns the type or kind of vulnerability.
202 | #
203 | # @return [Symbol]
204 | #
205 | # @note
206 | # This is used internally to map an vulnerability class to a printable
207 | # type.
208 | #
209 | # @api private
210 | #
211 | # @abstract
212 | #
213 | def self.vuln_type
214 | :ssti
215 | end
216 |
217 | end
218 | end
219 | end
220 |
--------------------------------------------------------------------------------
/COPYING.txt:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/command_injection.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '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] escape_quote
112 | # The optional escape quote character(s) to test.
113 | #
114 | # @param [Array, String, nil] escape_operator
115 | # The optional escape operator character(s) to test.
116 | #
117 | # @param [Array, String, 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
268 |
--------------------------------------------------------------------------------
/lib/ronin/vulns/reflected_xss/context.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # ronin-vulns - A Ruby library for blind vulnerability testing.
4 | #
5 | # Copyright (c) 2022-2025 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 .
19 | #
20 |
21 | require_relative '../web_vuln'
22 |
23 | module Ronin
24 | module Vulns
25 | class ReflectedXSS < WebVuln
26 | #
27 | # Represents information about the context which the XSS occurs within.
28 | #
29 | class Context
30 |
31 | # Where in the HTML the XSS occurs.
32 | #
33 | # @return [:double_quoted_attr_value, :single_quoted_attr_value, :unquoted_attr_value, :attr_name, :attr_list, :tag_name, :tag_body, :comment]
34 | # The context which the XSS occurs in.
35 | # * `:tag_body` occurred within a tag's body (ex: `XSS...`)
36 | # * `:double_quoted_attr_value` - occurred in a double quoted
37 | # attribute value (ex: `...`)
38 | # * `:single_quoted_attr_value` - occurred in a single quoted
39 | # attribute value (ex: `...`)
40 | # * `:unquoted_attr_value` - occurred in an unquoted attribute value
41 | # (ex: `...`)
42 | # * `:attr_name` - occurred in an attribute name
43 | # (ex: ``)
44 | # * `:attr_list` - occurred in the attribute list
45 | # (ex: `...`)
46 | # * `:tag_name` - occurred in the tag name (ex: `...`)
47 | # * `:comment` - occurred in a comment (ex: ``)
48 | #
49 | # @api public
50 | attr_reader :location
51 |
52 | # The name of the parent tag which the XSS occurs in.
53 | #
54 | # @return [String, nil]
55 | #
56 | # @api public
57 | attr_reader :tag
58 |
59 | # The attribute name that the XSS occurs in.
60 | #
61 | # @return [String, nil]
62 | #
63 | # @api public
64 | attr_reader :attr
65 |
66 | #
67 | # Initializes the context.
68 | #
69 | # @param [:double_quoted_attr_value, :single_quoted_attr_value, :unquoted_attr_value, :attr_name, :attr_list, :tag_name, :tag_body, :comment] location
70 | #
71 | # @param [String, nil] tag
72 | #
73 | # @param [String, nil] attr
74 | #
75 | # @api private
76 | #
77 | def initialize(location, tag: nil, attr: nil)
78 | @location = location
79 |
80 | @tag = tag
81 | @attr = attr
82 | end
83 |
84 | # HTML identifier regexp
85 | #
86 | # @api private
87 | IDENTIFIER = /[A-Za-z0-9_-]+/
88 |
89 | # HTML attribute name regexp.
90 | #
91 | # @api private
92 | ATTR_NAME = IDENTIFIER
93 |
94 | # HTML attribute regexp.
95 | #
96 | # @api private
97 | ATTR = /#{ATTR_NAME}(?:\s*=\s*"[^"]+"|\s*=\s*'[^']+'|=[^"'\s]+)?/
98 |
99 | # HTML attribute list regexp.
100 | #
101 | # @api private
102 | ATTR_LIST = /(?:\s+#{ATTR})*/
103 |
104 | # HTML comment regexp.
105 | #
106 | # @api private
107 | COMMENT = /]*>/
108 |
109 | # HTML tag name regexp.
110 | #
111 | # @api private
112 | TAG_NAME = IDENTIFIER
113 |
114 | # Regexp matching when an XSS occurs within a tag's inner HTML.
115 | #
116 | # @api private
117 | IN_TAG_BODY = %r{<(#{TAG_NAME})#{ATTR_LIST}\s*(?:>|/>)([^<>]|#{COMMENT})*\z}
118 |
119 | # Regexp matching when an XSS occurs within a double-quoted attribute
120 | # value.
121 | #
122 | # @api private
123 | IN_DOUBLE_QUOTED_ATTR_VALUE = /<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\s*=\s*"[^"]+\z/
124 |
125 | # Regexp matching when an XSS occurs within a single-quoted attribute
126 | # value.
127 | #
128 | # @api private
129 | IN_SINGLE_QUOTED_ATTR_VALUE = /<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\s*=\s*'[^']+\z/
130 |
131 | # Regexp matching when an XSS occurs within an unquoted attribute value.
132 | #
133 | # @api private
134 | IN_UNQUOTED_ATTR_VALUE = /<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})=[^"'\s]+\z/
135 |
136 | # Regexp matching when an XSS occurs within an attribute's name.
137 | #
138 | # @api private
139 | IN_ATTR_NAME = /<(#{TAG_NAME})#{ATTR_LIST}\s+(#{ATTR_NAME})\z/
140 |
141 | # Regexp matching when an XSS occurs within a tag's attribute list.
142 | #
143 | # @api private
144 | IN_ATTR_LIST = /<(#{TAG_NAME})#{ATTR_LIST}\s+\z/
145 |
146 | # Regexp matching when an XSS occurs within a tag's name.
147 | #
148 | # @api private
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/
155 |
156 | #
157 | # Determine the context of the XSS by checking the characters that come
158 | # before the given index.
159 | #
160 | # @param [String] body
161 | # The HTML response body to inspect.
162 | #
163 | # @param [Integer] index
164 | # The index which the XSS occurs at.
165 | #
166 | # @return [Context]
167 | # The context which the XSS occurs in.
168 | #
169 | # @api private
170 | #
171 | def self.identify(body,index)
172 | prefix = body[0,index]
173 |
174 | if (match = prefix.match(IN_TAG_BODY))
175 | new(:tag_body, tag: match[1])
176 | elsif (match = prefix.match(IN_DOUBLE_QUOTED_ATTR_VALUE))
177 | new(:double_quoted_attr_value, tag: match[1], attr: match[2])
178 | elsif (match = prefix.match(IN_SINGLE_QUOTED_ATTR_VALUE))
179 | new(:single_quoted_attr_value, tag: match[1], attr: match[2])
180 | elsif (match = prefix.match(IN_UNQUOTED_ATTR_VALUE))
181 | new(:unquoted_attr_value, tag: match[1], attr: match[2])
182 | elsif (match = prefix.match(IN_ATTR_NAME))
183 | new(:attr_name, tag: match[1], attr: match[2])
184 | elsif (match = prefix.match(IN_ATTR_LIST))
185 | new(:attr_list, tag: match[1])
186 | elsif (match = prefix.match(IN_TAG_NAME))
187 | new(:tag_name, tag: match[1])
188 | elsif prefix.match?(IN_COMMENT)
189 | new(:comment)
190 | end
191 | end
192 |
193 | # The minimum set of required characters needed for an XSS.
194 | #
195 | # @api private
196 | MINIMAL_REQUIRED_CHARS = Set['>', ' ', '/', '<']
197 |
198 | # The mapping of contexts and their required characters.
199 | #
200 | # @api private
201 | REQUIRED_CHARS = {
202 | double_quoted_attr_value: MINIMAL_REQUIRED_CHARS + ['"'],
203 | single_quoted_attr_value: MINIMAL_REQUIRED_CHARS + ["'"],
204 | unquoted_attr_value: MINIMAL_REQUIRED_CHARS,
205 |
206 | attr_name: MINIMAL_REQUIRED_CHARS,
207 | attr_list: MINIMAL_REQUIRED_CHARS,
208 | tag_name: MINIMAL_REQUIRED_CHARS,
209 | tag_body: MINIMAL_REQUIRED_CHARS,
210 | comment: MINIMAL_REQUIRED_CHARS
211 | }
212 |
213 | #
214 | # Determines if the XSS is viable, given the context and the allowed
215 | # characters.
216 | #
217 | # @param [Set] allowed_chars
218 | # The allowed characters.
219 | #
220 | # @return [Boolean]
221 | # Specifies whether enough characters are allowed to perform an XSS in
222 | # the given context.
223 | #
224 | # @api private
225 | #
226 | def viable?(allowed_chars)
227 | required_chars = REQUIRED_CHARS.fetch(@location) do
228 | raise(NotImplementedError,"cannot determine viability for unknown XSS location type: #{@location.inspect}")
229 | end
230 |
231 | allowed_chars.superset?(required_chars)
232 | end
233 |
234 | end
235 | end
236 | end
237 | end
238 |
--------------------------------------------------------------------------------