HELLO v3!!!

This commit is contained in:
Ryan Dewhurst
2018-09-26 21:12:01 +02:00
parent 28b9c15256
commit d268a86795
1871 changed files with 988118 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
module WPScan
module Finders
module DynamicFinder
module Version
# Version finder using Body Pattern method. Tipically used when the response is not
# an HTML doc and Xpath can't be used
class BodyPattern < WPScan::Finders::DynamicFinder::Version::Finder
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(PATTERN: nil, CONFIDENCE: 60)
end
# @param [ Typhoeus::Response ] response
# @param [ Hash ] opts
# @return [ Version ]
def find(response, _opts = {})
return unless response.body =~ self.class::PATTERN
create_version(
Regexp.last_match[:v],
interesting_entries: ["#{response.effective_url}, Match: '#{Regexp.last_match}'"]
)
end
end
end
end
end
end

View File

@@ -0,0 +1,16 @@
module WPScan
module Finders
module DynamicFinder
module Version
# Version finder in Comment, which is basically an Xpath one with a default
# Xpath of //comment()
class Comment < WPScan::Finders::DynamicFinder::Version::Xpath
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(PATTERN: nil, XPATH: '//comment()')
end
end
end
end
end
end

View File

@@ -0,0 +1,56 @@
module WPScan
module Finders
module DynamicFinder
module Version
# Version finder using by parsing config files, such as composer.json
# and so on
class ConfigParser < WPScan::Finders::DynamicFinder::Version::Finder
ALLOWED_PARSERS = [JSON, YAML].freeze
def self.child_class_constants
@child_class_constants ||= super.merge(
PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 70
)
end
# @param [ String ] body
# @return [ Hash, nil ] The parsed body, with an available parser, if possible
def parse(body)
parsers = ALLOWED_PARSERS.include?(self.class::PARSER) ? [self.class::PARSER] : ALLOWED_PARSERS
parsers.each do |parser|
begin
parsed = parser.respond_to?(:safe_load) ? parser.safe_load(body) : parser.load(body)
return parsed if parsed.is_a?(Hash) || parsed.is_a?(Array)
rescue StandardError
next
end
end
nil # Make sure nil is returned in case none of the parsers managed to parse the body correctly
end
# No Passive way
def passive(opts = {}); end
# @param [ Typhoeus::Response ] response
# @param [ Hash ] opts
# @return [ Version ]
def find(response, _opts = {})
parsed_body = parse(response.body)
# Create indexes for the #dig, digits are converted to integers
indexes = self.class::KEY.split(':').map { |e| e == e.to_i.to_s ? e.to_i : e }
return unless (data = parsed_body&.dig(*indexes)) && data =~ self.class::PATTERN
create_version(
Regexp.last_match[:v],
interesting_entries: ["#{response.effective_url}, Match: '#{Regexp.last_match}'"]
)
end
end
end
end
end
end

View File

@@ -0,0 +1,29 @@
module WPScan
module Finders
module DynamicFinder
module Version
# To be used as a base when creating
# a dynamic finder to find the version of a WP Item (such as theme/plugin)
class Finder < Finders::DynamicFinder::Finder
protected
# @param [ String ] number
# @param [ Hash ] finding_opts
# @return [ WPScan::Version ]
def create_version(number, finding_opts)
WPScan::Version.new(number, version_finding_opts(finding_opts))
end
# @param [ Hash ] opts
# @retutn [ Hash ]
def version_finding_opts(opts)
opts[:found_by] ||= found_by
opts[:confidence] ||= self.class::CONFIDENCE
opts
end
end
end
end
end
end

View File

@@ -0,0 +1,28 @@
module WPScan
module Finders
module DynamicFinder
module Version
# Version finder using Header Pattern method
class HeaderPattern < WPScan::Finders::DynamicFinder::Version::Finder
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(HEADER: nil, PATTERN: nil, CONFIDENCE: 60)
end
# @param [ Typhoeus::Response ] response
# @param [ Hash ] opts
# @return [ Version ]
def find(response, _opts = {})
return unless response.headers && response.headers[self.class::HEADER]
return unless response.headers[self.class::HEADER].to_s =~ self.class::PATTERN
create_version(
Regexp.last_match[:v],
interesting_entries: ["#{response.effective_url}, Match: '#{Regexp.last_match}'"]
)
end
end
end
end
end
end

View File

@@ -0,0 +1,56 @@
module WPScan
module Finders
module DynamicFinder
module Version
# Version finder using JavaScript Variable method
class JavascriptVar < WPScan::Finders::DynamicFinder::Version::Finder
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(
XPATH: '//script[not(@src)]', VERSION_KEY: nil,
PATTERN: nil, CONFIDENCE: 60
)
end
# @param [ Typhoeus::Response ] response
# @param [ Hash ] opts
# @return [ Version ]
def find(response, _opts = {})
target.xpath_pattern_from_page(
self.class::XPATH, self.class::PATTERN, response
) do |match_data, _node|
next unless (version_number = version_number_from_match_data(match_data))
# If the text to be output in the interesting_entries is > 50 chars,
# get 20 chars before and after (when possible) the detected version instead
match = match_data.to_s
match = match[/.*?(.{,20}#{Regexp.escape(version_number)}.{,20}).*/, 1] if match.size > 50
return create_version(
version_number,
interesting_entries: ["#{response.effective_url}, Match: '#{match.strip}'"]
)
end
nil
end
# @param [ MatchData ] match_data
# @return [ String ]
def version_number_from_match_data(match_data)
if self.class::VERSION_KEY
begin
json = JSON.parse("{#{match_data[:json].strip.chomp(',').tr("'", '"')}}")
rescue JSON::ParserError
return
end
json.dig(*self.class::VERSION_KEY.split(':'))
else
match_data[:v]
end
end
end
end
end
end
end

View File

@@ -0,0 +1,63 @@
module WPScan
module Finders
module DynamicFinder
module Version
# Version finder using QueryParameter method
class QueryParameter < WPScan::Finders::DynamicFinder::Version::Finder
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)\=(?<v>\d+\.[\.\d]+)/i, CONFIDENCE_PER_OCCURENCE: 10
)
end
# @param [ Typhoeus::Response ] response
# @param [ Hash ] opts
# @return [ Array<Version>, nil ]
def find(response, _opts = {})
found = []
scan_response(response).each do |version_number, occurences|
found << create_version(
version_number,
confidence: self.class::CONFIDENCE_PER_OCCURENCE * occurences.size,
interesting_entries: occurences
)
end
found.compact
end
# @param [ Typhoeus::Response ] response
# @return [ Hash ]
def scan_response(response)
found = {}
target.in_scope_urls(response, xpath) do |url, _tag|
uri = Addressable::URI.parse(url)
next unless uri.path =~ path_pattern && uri.query&.match(self.class::PATTERN)
version = Regexp.last_match[:v].to_s
found[version] ||= []
found[version] << url
end
found
end
# @return [ String ]
def xpath
@xpath ||= self.class::XPATH || '//link[@href]/@href|//script[@src]/@src'
end
# @return [ Regexp ]
def path_pattern
@path_pattern ||= %r{/(?:#{self.class::FILES.join('|')})\z}i
end
end
end
end
end
end

View File

@@ -0,0 +1,34 @@
module WPScan
module Finders
module DynamicFinder
module Version
# Version finder using Xpath method
class Xpath < WPScan::Finders::DynamicFinder::Version::Finder
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(
XPATH: nil, PATTERN: /\A(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 60
)
end
# @param [ Typhoeus::Response ] response
# @param [ Hash ] opts
# @return [ Version ]
def find(response, _opts = {})
target.xpath_pattern_from_page(
self.class::XPATH, self.class::PATTERN, response
) do |match_data, _node|
next unless match_data[:v]
return create_version(
match_data[:v],
interesting_entries: ["#{response.effective_url}, Match: '#{match_data}'"]
)
end
nil
end
end
end
end
end
end