lib/ronin/vulns/cli/web_vuln_command.rb in ronin-vulns-0.1.5 vs lib/ronin/vulns/cli/web_vuln_command.rb in ronin-vulns-0.2.0.rc1
- old
+ new
@@ -1,10 +1,10 @@
# frozen_string_literal: true
#
# ronin-vulns - A Ruby library for blind vulnerability testing.
#
-# Copyright (c) 2022-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
+# Copyright (c) 2022-2024 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-vulns is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
@@ -17,13 +17,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-vulns. If not, see <https://www.gnu.org/licenses/>.
#
require 'ronin/vulns/cli/command'
-require 'ronin/vulns/cli/logging'
+require 'ronin/vulns/cli/importable'
+require 'ronin/vulns/cli/printing'
require 'ronin/support/network/http/cookie'
+require 'ronin/support/network/http/user_agents'
require 'set'
module Ronin
module Vulns
@@ -31,22 +33,52 @@
#
# Base class for all web vulnerability commands.
#
class WebVulnCommand < Command
- include Logging
+ include Printing
+ include Importable
+ option :import, desc: 'Imports discovered vulnerabilities into the database'
option :first, short: '-F',
desc: 'Only find the first vulnerability for each URL' do
@scan_mode = :first
end
option :all, short: '-A',
desc: 'Find all vulnerabilities for each URL' do
@scan_mode = :all
end
+ option :print_curl, desc: 'Also prints an example curl command for each vulnerability'
+
+ option :print_http, desc: 'Also prints an example HTTP request for each vulnerability'
+
+ option :request_method, short: '-M',
+ value: {
+ type: {
+ 'COPY' => :copy,
+ 'DELETE' => :delete,
+ 'GET' => :get,
+ 'HEAD' => :head,
+ 'LOCK' => :lock,
+ 'MKCOL' => :mkcol,
+ 'MOVE' => :move,
+ 'OPTIONS' => :options,
+ 'PATCH' => :patch,
+ 'POST' => :post,
+ 'PROPFIND' => :propfind,
+ 'PROPPATCH' => :proppatch,
+ 'PUT' => :put,
+ 'TRACE' => :trace,
+ 'UNLOCK' => :unlock
+ }
+ },
+ desc: 'The HTTP request method to use' do |verb|
+ self.request_method = verb
+ end
+
option :header, short: '-H',
value: {
type: /[A-Za-z0-9-]+:\s*\w+/,
usage: '"Name: value"'
},
@@ -54,10 +86,29 @@
name, value = header.split(/:\s*/,2)
self.headers[name] = value
end
+ option :user_agent_string, short: '-U',
+ value: {
+ type: String,
+ usage: 'STRING'
+ },
+ desc: 'Sets the User-Agent header' do |ua|
+ self.user_agent = ua
+ end
+
+ option :user_agent, short: '-u',
+ value: {
+ type: Support::Network::HTTP::UserAgents::ALIASES.transform_keys { |key|
+ key.to_s.tr('_','-')
+ }
+ },
+ desc: 'Sets the User-Agent to use' do |name|
+ self.user_agent = name
+ end
+
option :cookie, short: '-C',
value: {
type: String,
usage: 'COOKIE'
},
@@ -146,10 +197,14 @@
},
desc: 'Tests the form param name' do |name|
self.test_form_params << name
end
+ option :test_all_form_params, desc: 'Tests all form param names' do
+ self.test_form_params = true
+ end
+
option :input, short: '-i',
value: {
type: String,
usage: 'FILE'
},
@@ -195,73 +250,202 @@
unless (options[:input] || !urls.empty?)
print_error "must specify URL(s) or --input"
exit(-1)
end
- vulns_discovered = false
+ db_connect if options[:import]
+ vulns = []
+
if options[:input]
File.open(options[:input]) do |file|
file.each_line(chomp: true) do |url|
- vulns_discovered ||= process_url(url)
+ process_url(url) do |vuln|
+ vulns << vuln
+ end
end
end
elsif !urls.empty?
urls.each do |url|
- vulns_discovered ||= process_url(url)
+ process_url(url) do |vuln|
+ vulns << vuln
+ end
end
end
- unless vulns_discovered
- puts colors.green("No vulnerabilities found")
- end
+ puts unless vulns.empty?
+ print_vulns(vulns)
end
#
+ # Print a summary of all web vulnerabilities found.
+ #
+ # @param [Array<WebVuln>] vulns
+ # The discovered web vulnerabilities.
+ #
+ # @param [Boolean] print_curl
+ # Prints an example `curl` command to trigger the web vulnerability.
+ #
+ # @param [Boolean] print_http
+ # Prints an example HTTP request to trigger the web vulnerability.
+ #
+ # @since 0.2.0
+ #
+ def print_vulns(vulns, print_curl: options[:print_curl],
+ print_http: options[:print_http])
+ super(vulns, print_curl: print_curl,
+ print_http: print_http)
+ end
+
+ #
+ # Prints detailed information about a discovered web vulnerability.
+ #
+ # @param [WebVuln] vuln
+ # The web vulnerability to log.
+ #
+ # @param [Boolean] print_curl
+ # Prints an example `curl` command to trigger the web vulnerability.
+ #
+ # @param [Boolean] print_http
+ # Prints an example HTTP request to trigger the web vulnerability.
+ #
+ # @since 0.2.0
+ #
+ def print_vuln(vuln, print_curl: options[:print_curl],
+ print_http: options[:print_http])
+ super(vuln, print_curl: print_curl,
+ print_http: print_http)
+ end
+
+ #
# Processes a URL.
#
# @param [String] url
# A URL to scan.
#
- # @return [Boolean]
- # Indicates whether a vulnerability was discovered in the URL.
+ # @yield [vuln]
+ # The given block will be passed each newly discovered web
+ # vulnerability.
#
+ # @yieldparam [WebVuln] vuln
+ # A newly discovered web vulnerability.
+ #
def process_url(url)
unless url.start_with?('http://') || url.start_with?('https://')
print_error("URL must start with http:// or https://: #{url.inspect}")
exit(-1)
end
- vuln_discovered = false
-
if @scan_mode == :first
if (first_vuln = test_url(url))
- log_vuln(first_vuln)
-
- vuln_discovered = true
+ process_vuln(first_vuln)
+ yield first_vuln
end
else
scan_url(url) do |vuln|
- log_vuln(vuln)
-
- vuln_discovered = true
+ process_vuln(vuln)
+ yield vuln
end
end
+ end
- return vuln_discovered
+ #
+ # Logs and optioanlly imports a new discovered web vulnerability.
+ #
+ # @param [WebVuln] vuln
+ # The discovered web vulnerability.
+ #
+ # @since 0.2.0
+ #
+ def process_vuln(vuln)
+ log_vuln(vuln)
+ import_vuln(vuln) if options[:import]
end
#
+ # The HTTP request method to use.
+ #
+ # @return [:copy, :delete, :get, :head, :lock, :mkcol, :move,
+ # :options, :patch, :post, :propfind, :proppatch, :put,
+ # :trace, :unlock]
+ #
+ # @since 0.2.0
+ #
+ def request_method
+ @scan_kwargs[:request_method]
+ end
+
+ #
+ # Sets the HTTP request method to use.
+ #
+ # @param [:copy, :delete, :get, :head, :lock, :mkcol, :move,
+ # :options, :patch, :post, :propfind, :proppatch, :put,
+ # :trace, :unlock] new_request_method
+ #
+ # @return [:copy, :delete, :get, :head, :lock, :mkcol, :move,
+ # :options, :patch, :post, :propfind, :proppatch, :put,
+ # :trace, :unlock]
+ #
+ # @since 0.2.0
+ #
+ def request_method=(new_request_method)
+ @scan_kwargs[:request_method] = new_request_method
+ end
+
+ #
# Additional headers.
#
# @return [Hash{String => String}]
#
def headers
@scan_kwargs[:headers] ||= {}
end
#
+ # The optional HTTP `User-Agent` header to send.
+ #
+ # @return [String, :random, :chrome, :chrome_linux, :chrome_macos,
+ # :chrome_windows, :chrome_iphone, :chrome_ipad,
+ # :chrome_android, :firefox, :firefox_linux, :firefox_macos,
+ # :firefox_windows, :firefox_iphone, :firefox_ipad,
+ # :firefox_android, :safari, :safari_macos, :safari_iphone,
+ # :safari_ipad, :edge, :linux, :macos, :windows, :iphone,
+ # :ipad, :android, nil]
+ #
+ # @since 0.2.0
+ #
+ def user_agent
+ @scan_kwargs[:user_agent]
+ end
+
+ #
+ # Sets the HTTP `User-Agent` header.
+ #
+ # @param [String, :random, :chrome, :chrome_linux, :chrome_macos,
+ # :chrome_windows, :chrome_iphone, :chrome_ipad,
+ # :chrome_android, :firefox, :firefox_linux, :firefox_macos,
+ # :firefox_windows, :firefox_iphone, :firefox_ipad,
+ # :firefox_android, :safari, :safari_macos, :safari_iphone,
+ # :safari_ipad, :edge, :linux, :macos, :windows, :iphone,
+ # :ipad, :android] new_user_agent
+ # The new `User-Agent` value to send.
+ #
+ # @return [String, :random, :chrome, :chrome_linux, :chrome_macos,
+ # :chrome_windows, :chrome_iphone, :chrome_ipad,
+ # :chrome_android, :firefox, :firefox_linux, :firefox_macos,
+ # :firefox_windows, :firefox_iphone, :firefox_ipad,
+ # :firefox_android, :safari, :safari_macos, :safari_iphone,
+ # :safari_ipad, :edge, :linux, :macos, :windows, :iphone,
+ # :ipad, :android]
+ #
+ # @since 0.2.0
+ #
+ def user_agent=(new_user_agent)
+ @scan_kwargs[:user_agent] = new_user_agent
+ end
+
+ #
# The optional `Cookie` header to send.
#
# @return [Ronin::Support::Network::HTTP::Cookie]
#
def cookie
@@ -354,9 +538,21 @@
#
# @return [Set<String>, nil]
#
def test_form_params
@scan_kwargs[:form_params] ||= Set.new
+ end
+
+ #
+ # Sets the form params to test.
+ #
+ # @param [Set<String>, true] new_form_params
+ # The new form param names to test.
+ #
+ # @return [Set<String>, true]
+ #
+ def test_form_params=(new_form_params)
+ @scan_kwargs[:form_params] = new_form_params
end
#
# Scans a URL for web vulnerabilities.
#