├── .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 | --------------------------------------------------------------------------------