Moves Models into their own namespace - Ref #1315
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
module WPScan
|
||||
# Config Backup
|
||||
class ConfigBackup < InterestingFinding
|
||||
module Model
|
||||
# Config Backup
|
||||
class ConfigBackup < InterestingFinding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module WPScan
|
||||
# DB Export
|
||||
class DbExport < InterestingFinding
|
||||
module Model
|
||||
# DB Export
|
||||
class DbExport < InterestingFinding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,48 +1,50 @@
|
||||
module WPScan
|
||||
# Custom class to include the WPScan::References module
|
||||
class InterestingFinding < CMSScanner::InterestingFinding
|
||||
include References
|
||||
end
|
||||
module Model
|
||||
# Custom class to include the WPScan::References module
|
||||
class InterestingFinding < CMSScanner::Model::InterestingFinding
|
||||
include References
|
||||
end
|
||||
|
||||
#
|
||||
# Empty classes for the #type to be correctly displayed (as taken from the self.class from the parent)
|
||||
#
|
||||
class BackupDB < InterestingFinding
|
||||
end
|
||||
#
|
||||
# Empty classes for the #type to be correctly displayed (as taken from the self.class from the parent)
|
||||
#
|
||||
class BackupDB < InterestingFinding
|
||||
end
|
||||
|
||||
class DebugLog < InterestingFinding
|
||||
end
|
||||
class DebugLog < InterestingFinding
|
||||
end
|
||||
|
||||
class DuplicatorInstallerLog < InterestingFinding
|
||||
end
|
||||
class DuplicatorInstallerLog < InterestingFinding
|
||||
end
|
||||
|
||||
class EmergencyPwdResetScript < InterestingFinding
|
||||
end
|
||||
class EmergencyPwdResetScript < InterestingFinding
|
||||
end
|
||||
|
||||
class FullPathDisclosure < InterestingFinding
|
||||
end
|
||||
class FullPathDisclosure < InterestingFinding
|
||||
end
|
||||
|
||||
class MuPlugins < InterestingFinding
|
||||
end
|
||||
class MuPlugins < InterestingFinding
|
||||
end
|
||||
|
||||
class Multisite < InterestingFinding
|
||||
end
|
||||
class Multisite < InterestingFinding
|
||||
end
|
||||
|
||||
class Readme < InterestingFinding
|
||||
end
|
||||
class Readme < InterestingFinding
|
||||
end
|
||||
|
||||
class Registration < InterestingFinding
|
||||
end
|
||||
class Registration < InterestingFinding
|
||||
end
|
||||
|
||||
class TmmDbMigrate < InterestingFinding
|
||||
end
|
||||
class TmmDbMigrate < InterestingFinding
|
||||
end
|
||||
|
||||
class UploadDirectoryListing < InterestingFinding
|
||||
end
|
||||
class UploadDirectoryListing < InterestingFinding
|
||||
end
|
||||
|
||||
class UploadSQLDump < InterestingFinding
|
||||
end
|
||||
class UploadSQLDump < InterestingFinding
|
||||
end
|
||||
|
||||
class WPCron < InterestingFinding
|
||||
class WPCron < InterestingFinding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module WPScan
|
||||
# Media
|
||||
class Media < InterestingFinding
|
||||
module Model
|
||||
# Media
|
||||
class Media < InterestingFinding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
module WPScan
|
||||
# WordPress Plugin
|
||||
class Plugin < WpItem
|
||||
# See WpItem
|
||||
def initialize(slug, blog, opts = {})
|
||||
super(slug, blog, opts)
|
||||
module Model
|
||||
# WordPress Plugin
|
||||
class Plugin < WpItem
|
||||
# See WpItem
|
||||
def initialize(slug, blog, opts = {})
|
||||
super(slug, blog, opts)
|
||||
|
||||
@uri = Addressable::URI.parse(blog.url("wp-content/plugins/#{slug}/"))
|
||||
end
|
||||
@uri = Addressable::URI.parse(blog.url("wp-content/plugins/#{slug}/"))
|
||||
end
|
||||
|
||||
# @return [ JSON ]
|
||||
def db_data
|
||||
DB::Plugin.db_data(slug)
|
||||
end
|
||||
# @return [ JSON ]
|
||||
def db_data
|
||||
DB::Plugin.db_data(slug)
|
||||
end
|
||||
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ WPScan::Version, false ]
|
||||
def version(opts = {})
|
||||
@version = Finders::PluginVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ Model::Version, false ]
|
||||
def version(opts = {})
|
||||
@version = Finders::PluginVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
|
||||
|
||||
@version
|
||||
@version
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,99 +1,101 @@
|
||||
module WPScan
|
||||
# WordPress Theme
|
||||
class Theme < WpItem
|
||||
attr_reader :style_url, :style_name, :style_uri, :author, :author_uri, :template, :description,
|
||||
:license, :license_uri, :tags, :text_domain
|
||||
module Model
|
||||
# WordPress Theme
|
||||
class Theme < WpItem
|
||||
attr_reader :style_url, :style_name, :style_uri, :author, :author_uri, :template, :description,
|
||||
:license, :license_uri, :tags, :text_domain
|
||||
|
||||
# See WpItem
|
||||
def initialize(slug, blog, opts = {})
|
||||
super(slug, blog, opts)
|
||||
# See WpItem
|
||||
def initialize(slug, blog, opts = {})
|
||||
super(slug, blog, opts)
|
||||
|
||||
@uri = Addressable::URI.parse(blog.url("wp-content/themes/#{slug}/"))
|
||||
@style_url = opts[:style_url] || url('style.css')
|
||||
@uri = Addressable::URI.parse(blog.url("wp-content/themes/#{slug}/"))
|
||||
@style_url = opts[:style_url] || url('style.css')
|
||||
|
||||
parse_style
|
||||
end
|
||||
|
||||
# @return [ JSON ]
|
||||
def db_data
|
||||
DB::Theme.db_data(slug)
|
||||
end
|
||||
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ WPScan::Version, false ]
|
||||
def version(opts = {})
|
||||
@version = Finders::ThemeVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
|
||||
|
||||
@version
|
||||
end
|
||||
|
||||
# @return [ Theme ]
|
||||
def parent_theme
|
||||
return unless template
|
||||
return unless style_body =~ /^@import\surl\(["']?([^"'\)]+)["']?\);\s*$/i
|
||||
|
||||
opts = detection_opts.merge(
|
||||
style_url: url(Regexp.last_match[1]),
|
||||
found_by: 'Parent Themes (Passive Detection)',
|
||||
confidence: 100
|
||||
).merge(version_detection: version_detection_opts)
|
||||
|
||||
self.class.new(template, blog, opts)
|
||||
end
|
||||
|
||||
# @param [ Integer ] depth
|
||||
#
|
||||
# @retun [ Array<Theme> ]
|
||||
def parent_themes(depth = 3)
|
||||
theme = self
|
||||
found = []
|
||||
|
||||
(1..depth).each do |_|
|
||||
parent = theme.parent_theme
|
||||
|
||||
break unless parent
|
||||
|
||||
found << parent
|
||||
theme = parent
|
||||
parse_style
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
def style_body
|
||||
@style_body ||= Browser.get(style_url).body
|
||||
end
|
||||
|
||||
def parse_style
|
||||
{
|
||||
style_name: 'Theme Name',
|
||||
style_uri: 'Theme URI',
|
||||
author: 'Author',
|
||||
author_uri: 'Author URI',
|
||||
template: 'Template',
|
||||
description: 'Description',
|
||||
license: 'License',
|
||||
license_uri: 'License URI',
|
||||
tags: 'Tags',
|
||||
text_domain: 'Text Domain'
|
||||
}.each do |attribute, tag|
|
||||
instance_variable_set(:"@#{attribute}", parse_style_tag(style_body, tag))
|
||||
# @return [ JSON ]
|
||||
def db_data
|
||||
DB::Theme.db_data(slug)
|
||||
end
|
||||
end
|
||||
|
||||
# @param [ String ] bofy
|
||||
# @param [ String ] tag
|
||||
#
|
||||
# @return [ String ]
|
||||
def parse_style_tag(body, tag)
|
||||
value = body[/^\s*#{Regexp.escape(tag)}:[\t ]*([^\r\n]+)/i, 1]
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ Model::Version, false ]
|
||||
def version(opts = {})
|
||||
@version = Finders::ThemeVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
|
||||
|
||||
value && !value.strip.empty? ? value.strip : nil
|
||||
end
|
||||
@version
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
super(other) && style_url == other.style_url
|
||||
# @return [ Theme ]
|
||||
def parent_theme
|
||||
return unless template
|
||||
return unless style_body =~ /^@import\surl\(["']?([^"'\)]+)["']?\);\s*$/i
|
||||
|
||||
opts = detection_opts.merge(
|
||||
style_url: url(Regexp.last_match[1]),
|
||||
found_by: 'Parent Themes (Passive Detection)',
|
||||
confidence: 100
|
||||
).merge(version_detection: version_detection_opts)
|
||||
|
||||
self.class.new(template, blog, opts)
|
||||
end
|
||||
|
||||
# @param [ Integer ] depth
|
||||
#
|
||||
# @retun [ Array<Theme> ]
|
||||
def parent_themes(depth = 3)
|
||||
theme = self
|
||||
found = []
|
||||
|
||||
(1..depth).each do |_|
|
||||
parent = theme.parent_theme
|
||||
|
||||
break unless parent
|
||||
|
||||
found << parent
|
||||
theme = parent
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
def style_body
|
||||
@style_body ||= Browser.get(style_url).body
|
||||
end
|
||||
|
||||
def parse_style
|
||||
{
|
||||
style_name: 'Theme Name',
|
||||
style_uri: 'Theme URI',
|
||||
author: 'Author',
|
||||
author_uri: 'Author URI',
|
||||
template: 'Template',
|
||||
description: 'Description',
|
||||
license: 'License',
|
||||
license_uri: 'License URI',
|
||||
tags: 'Tags',
|
||||
text_domain: 'Text Domain'
|
||||
}.each do |attribute, tag|
|
||||
instance_variable_set(:"@#{attribute}", parse_style_tag(style_body, tag))
|
||||
end
|
||||
end
|
||||
|
||||
# @param [ String ] bofy
|
||||
# @param [ String ] tag
|
||||
#
|
||||
# @return [ String ]
|
||||
def parse_style_tag(body, tag)
|
||||
value = body[/^\s*#{Regexp.escape(tag)}:[\t ]*([^\r\n]+)/i, 1]
|
||||
|
||||
value && !value.strip.empty? ? value.strip : nil
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
super(other) && style_url == other.style_url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,71 +1,73 @@
|
||||
module WPScan
|
||||
# Timthumb
|
||||
class Timthumb < InterestingFinding
|
||||
include Vulnerable
|
||||
module Model
|
||||
# Timthumb
|
||||
class Timthumb < InterestingFinding
|
||||
include Vulnerable
|
||||
|
||||
attr_reader :version_detection_opts
|
||||
attr_reader :version_detection_opts
|
||||
|
||||
# @param [ String ] url
|
||||
# @param [ Hash ] opts
|
||||
# @option opts [ Symbol ] :mode The mode to use to detect the version
|
||||
def initialize(url, opts = {})
|
||||
super(url, opts)
|
||||
# @param [ String ] url
|
||||
# @param [ Hash ] opts
|
||||
# @option opts [ Symbol ] :mode The mode to use to detect the version
|
||||
def initialize(url, opts = {})
|
||||
super(url, opts)
|
||||
|
||||
@version_detection_opts = opts[:version_detection] || {}
|
||||
end
|
||||
@version_detection_opts = opts[:version_detection] || {}
|
||||
end
|
||||
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ WPScan::Version, false ]
|
||||
def version(opts = {})
|
||||
@version = Finders::TimthumbVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
|
||||
# @param [ Hash ] opts
|
||||
#
|
||||
# @return [ Model::Version, false ]
|
||||
def version(opts = {})
|
||||
@version = Finders::TimthumbVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
|
||||
|
||||
@version
|
||||
end
|
||||
@version
|
||||
end
|
||||
|
||||
# @return [ Array<Vulnerability> ]
|
||||
def vulnerabilities
|
||||
vulns = []
|
||||
# @return [ Array<Vulnerability> ]
|
||||
def vulnerabilities
|
||||
vulns = []
|
||||
|
||||
vulns << rce_webshot_vuln if version == false || version > '1.35' && version < '2.8.14' && webshot_enabled?
|
||||
vulns << rce_132_vuln if version == false || version < '1.33'
|
||||
vulns << rce_webshot_vuln if version == false || version > '1.35' && version < '2.8.14' && webshot_enabled?
|
||||
vulns << rce_132_vuln if version == false || version < '1.33'
|
||||
|
||||
vulns
|
||||
end
|
||||
vulns
|
||||
end
|
||||
|
||||
# @return [ Vulnerability ] The RCE in the <= 1.32
|
||||
def rce_132_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 1.32 Remote Code Execution',
|
||||
{ exploitdb: ['17602'] },
|
||||
'RCE',
|
||||
'1.33'
|
||||
)
|
||||
end
|
||||
# @return [ Vulnerability ] The RCE in the <= 1.32
|
||||
def rce_132_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 1.32 Remote Code Execution',
|
||||
{ exploitdb: ['17602'] },
|
||||
'RCE',
|
||||
'1.33'
|
||||
)
|
||||
end
|
||||
|
||||
# @return [ Vulnerability ] The RCE due to the WebShot in the > 1.35 (or >= 2.0) and <= 2.8.13
|
||||
def rce_webshot_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 2.8.13 WebShot Remote Code Execution',
|
||||
{
|
||||
url: ['http://seclists.org/fulldisclosure/2014/Jun/117', 'https://github.com/wpscanteam/wpscan/issues/519'],
|
||||
cve: '2014-4663'
|
||||
},
|
||||
'RCE',
|
||||
'2.8.14'
|
||||
)
|
||||
end
|
||||
# @return [ Vulnerability ] The RCE due to the WebShot in the > 1.35 (or >= 2.0) and <= 2.8.13
|
||||
def rce_webshot_vuln
|
||||
Vulnerability.new(
|
||||
'Timthumb <= 2.8.13 WebShot Remote Code Execution',
|
||||
{
|
||||
url: ['http://seclists.org/fulldisclosure/2014/Jun/117', 'https://github.com/wpscanteam/wpscan/issues/519'],
|
||||
cve: '2014-4663'
|
||||
},
|
||||
'RCE',
|
||||
'2.8.14'
|
||||
)
|
||||
end
|
||||
|
||||
# @return [ Boolean ]
|
||||
def webshot_enabled?
|
||||
res = Browser.get(url, params: { webshot: 1, src: "http://#{default_allowed_domains.sample}" })
|
||||
# @return [ Boolean ]
|
||||
def webshot_enabled?
|
||||
res = Browser.get(url, params: { webshot: 1, src: "http://#{default_allowed_domains.sample}" })
|
||||
|
||||
res.body =~ /WEBSHOT_ENABLED == true/ ? false : true
|
||||
end
|
||||
res.body =~ /WEBSHOT_ENABLED == true/ ? false : true
|
||||
end
|
||||
|
||||
# @return [ Array<String> ] The default allowed domains (between the 2.0 and 2.8.13)
|
||||
def default_allowed_domains
|
||||
%w[flickr.com picasa.com img.youtube.com upload.wikimedia.org]
|
||||
# @return [ Array<String> ] The default allowed domains (between the 2.0 and 2.8.13)
|
||||
def default_allowed_domains
|
||||
%w[flickr.com picasa.com img.youtube.com upload.wikimedia.org]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,158 +1,160 @@
|
||||
module WPScan
|
||||
# WpItem (superclass of Plugin & Theme)
|
||||
class WpItem
|
||||
include Vulnerable
|
||||
include Finders::Finding
|
||||
include CMSScanner::Target::Platform::PHP
|
||||
include CMSScanner::Target::Server::Generic
|
||||
module Model
|
||||
# WpItem (superclass of Plugin & Theme)
|
||||
class WpItem
|
||||
include Vulnerable
|
||||
include Finders::Finding
|
||||
include CMSScanner::Target::Platform::PHP
|
||||
include CMSScanner::Target::Server::Generic
|
||||
|
||||
READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
|
||||
CHANGELOGS = %w[changelog.txt CHANGELOG.md changelog.md].freeze
|
||||
READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
|
||||
CHANGELOGS = %w[changelog.txt CHANGELOG.md changelog.md].freeze
|
||||
|
||||
attr_reader :uri, :slug, :detection_opts, :version_detection_opts, :blog, :db_data
|
||||
attr_reader :uri, :slug, :detection_opts, :version_detection_opts, :blog, :db_data
|
||||
|
||||
delegate :homepage_res, :xpath_pattern_from_page, :in_scope_urls, to: :blog
|
||||
delegate :homepage_res, :xpath_pattern_from_page, :in_scope_urls, to: :blog
|
||||
|
||||
# @param [ String ] slug The plugin/theme slug
|
||||
# @param [ Target ] blog The targeted blog
|
||||
# @param [ Hash ] opts
|
||||
# @option opts [ Symbol ] :mode The detection mode to use
|
||||
# @option opts [ Hash ] :version_detection The options to use when looking for the version
|
||||
# @option opts [ String ] :url The URL of the item
|
||||
def initialize(slug, blog, opts = {})
|
||||
@slug = URI.decode(slug)
|
||||
@blog = blog
|
||||
@uri = Addressable::URI.parse(opts[:url]) if opts[:url]
|
||||
# @param [ String ] slug The plugin/theme slug
|
||||
# @param [ Target ] blog The targeted blog
|
||||
# @param [ Hash ] opts
|
||||
# @option opts [ Symbol ] :mode The detection mode to use
|
||||
# @option opts [ Hash ] :version_detection The options to use when looking for the version
|
||||
# @option opts [ String ] :url The URL of the item
|
||||
def initialize(slug, blog, opts = {})
|
||||
@slug = URI.decode(slug)
|
||||
@blog = blog
|
||||
@uri = Addressable::URI.parse(opts[:url]) if opts[:url]
|
||||
|
||||
@detection_opts = { mode: opts[:mode] }
|
||||
@version_detection_opts = opts[:version_detection] || {}
|
||||
@detection_opts = { mode: opts[:mode] }
|
||||
@version_detection_opts = opts[:version_detection] || {}
|
||||
|
||||
parse_finding_options(opts)
|
||||
end
|
||||
|
||||
# @return [ Array<Vulnerabily> ]
|
||||
def vulnerabilities
|
||||
return @vulnerabilities if @vulnerabilities
|
||||
|
||||
@vulnerabilities = []
|
||||
|
||||
[*db_data['vulnerabilities']].each do |json_vuln|
|
||||
vulnerability = Vulnerability.load_from_json(json_vuln)
|
||||
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
|
||||
parse_finding_options(opts)
|
||||
end
|
||||
|
||||
@vulnerabilities
|
||||
end
|
||||
# @return [ Array<Vulnerabily> ]
|
||||
def vulnerabilities
|
||||
return @vulnerabilities if @vulnerabilities
|
||||
|
||||
# Checks if the wp_item is vulnerable to a specific vulnerability
|
||||
#
|
||||
# @param [ Vulnerability ] vuln Vulnerability to check the item against
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def vulnerable_to?(vuln)
|
||||
return true unless version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
|
||||
@vulnerabilities = []
|
||||
|
||||
version < vuln.fixed_in
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def latest_version
|
||||
@latest_version ||= db_data['latest_version'] ? WPScan::Version.new(db_data['latest_version']) : nil
|
||||
end
|
||||
|
||||
# Not used anywhere ATM
|
||||
# @return [ Boolean ]
|
||||
def popular?
|
||||
@popular ||= db_data['popular']
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def last_updated
|
||||
@last_updated ||= db_data['last_updated']
|
||||
end
|
||||
|
||||
# @return [ Boolean ]
|
||||
def outdated?
|
||||
@outdated ||= if version && latest_version
|
||||
version < latest_version
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# URI.encode is preferered over Addressable::URI.encode as it will encode
|
||||
# leading # character:
|
||||
# URI.encode('#t#') => %23t%23
|
||||
# Addressable::URI.encode('#t#') => #t%23
|
||||
#
|
||||
# @param [ String ] path Optional path to merge with the uri
|
||||
#
|
||||
# @return [ String ]
|
||||
def url(path = nil)
|
||||
return unless @uri
|
||||
return @uri.to_s unless path
|
||||
|
||||
@uri.join(URI.encode(path)).to_s
|
||||
end
|
||||
|
||||
# @return [ Boolean ]
|
||||
def ==(other)
|
||||
self.class == other.class && slug == other.slug
|
||||
end
|
||||
|
||||
def to_s
|
||||
slug
|
||||
end
|
||||
|
||||
# @return [ Symbol ] The Class symbol associated to the item
|
||||
def classify
|
||||
@classify ||= classify_slug(slug)
|
||||
end
|
||||
|
||||
# @return [ String ] The readme url if found
|
||||
def readme_url
|
||||
return if detection_opts[:mode] == :passive
|
||||
|
||||
if @readme_url.nil?
|
||||
READMES.each do |path|
|
||||
return @readme_url = url(path) if Browser.get(url(path)).code == 200
|
||||
[*db_data['vulnerabilities']].each do |json_vuln|
|
||||
vulnerability = Vulnerability.load_from_json(json_vuln)
|
||||
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
|
||||
end
|
||||
|
||||
@vulnerabilities
|
||||
end
|
||||
|
||||
@readme_url
|
||||
end
|
||||
# Checks if the wp_item is vulnerable to a specific vulnerability
|
||||
#
|
||||
# @param [ Vulnerability ] vuln Vulnerability to check the item against
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def vulnerable_to?(vuln)
|
||||
return true unless version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
|
||||
|
||||
# @return [ String, false ] The changelog urr if found
|
||||
def changelog_url
|
||||
return if detection_opts[:mode] == :passive
|
||||
version < vuln.fixed_in
|
||||
end
|
||||
|
||||
if @changelog_url.nil?
|
||||
CHANGELOGS.each do |path|
|
||||
return @changelog_url = url(path) if Browser.get(url(path)).code == 200
|
||||
# @return [ String ]
|
||||
def latest_version
|
||||
@latest_version ||= db_data['latest_version'] ? Model::Version.new(db_data['latest_version']) : nil
|
||||
end
|
||||
|
||||
# Not used anywhere ATM
|
||||
# @return [ Boolean ]
|
||||
def popular?
|
||||
@popular ||= db_data['popular']
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def last_updated
|
||||
@last_updated ||= db_data['last_updated']
|
||||
end
|
||||
|
||||
# @return [ Boolean ]
|
||||
def outdated?
|
||||
@outdated ||= if version && latest_version
|
||||
version < latest_version
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# URI.encode is preferered over Addressable::URI.encode as it will encode
|
||||
# leading # character:
|
||||
# URI.encode('#t#') => %23t%23
|
||||
# Addressable::URI.encode('#t#') => #t%23
|
||||
#
|
||||
# @param [ String ] path Optional path to merge with the uri
|
||||
#
|
||||
# @return [ String ]
|
||||
def url(path = nil)
|
||||
return unless @uri
|
||||
return @uri.to_s unless path
|
||||
|
||||
@uri.join(URI.encode(path)).to_s
|
||||
end
|
||||
|
||||
# @return [ Boolean ]
|
||||
def ==(other)
|
||||
self.class == other.class && slug == other.slug
|
||||
end
|
||||
|
||||
def to_s
|
||||
slug
|
||||
end
|
||||
|
||||
# @return [ Symbol ] The Class symbol associated to the item
|
||||
def classify
|
||||
@classify ||= classify_slug(slug)
|
||||
end
|
||||
|
||||
# @return [ String ] The readme url if found
|
||||
def readme_url
|
||||
return if detection_opts[:mode] == :passive
|
||||
|
||||
if @readme_url.nil?
|
||||
READMES.each do |path|
|
||||
return @readme_url = url(path) if Browser.get(url(path)).code == 200
|
||||
end
|
||||
end
|
||||
|
||||
@readme_url
|
||||
end
|
||||
|
||||
@changelog_url
|
||||
end
|
||||
# @return [ String, false ] The changelog urr if found
|
||||
def changelog_url
|
||||
return if detection_opts[:mode] == :passive
|
||||
|
||||
# @param [ String ] path
|
||||
# @param [ Hash ] params The request params
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def directory_listing?(path = nil, params = {})
|
||||
return if detection_opts[:mode] == :passive
|
||||
if @changelog_url.nil?
|
||||
CHANGELOGS.each do |path|
|
||||
return @changelog_url = url(path) if Browser.get(url(path)).code == 200
|
||||
end
|
||||
end
|
||||
|
||||
super(path, params)
|
||||
end
|
||||
@changelog_url
|
||||
end
|
||||
|
||||
# @param [ String ] path
|
||||
# @param [ Hash ] params The request params
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def error_log?(path = 'error_log', params = {})
|
||||
return if detection_opts[:mode] == :passive
|
||||
# @param [ String ] path
|
||||
# @param [ Hash ] params The request params
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def directory_listing?(path = nil, params = {})
|
||||
return if detection_opts[:mode] == :passive
|
||||
|
||||
super(path, params)
|
||||
super(path, params)
|
||||
end
|
||||
|
||||
# @param [ String ] path
|
||||
# @param [ Hash ] params The request params
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def error_log?(path = 'error_log', params = {})
|
||||
return if detection_opts[:mode] == :passive
|
||||
|
||||
super(path, params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,64 +1,66 @@
|
||||
module WPScan
|
||||
# WP Version
|
||||
class WpVersion < CMSScanner::Version
|
||||
include Vulnerable
|
||||
module Model
|
||||
# WP Version
|
||||
class WpVersion < CMSScanner::Model::Version
|
||||
include Vulnerable
|
||||
|
||||
def initialize(number, opts = {})
|
||||
raise Error::InvalidWordPressVersion unless WpVersion.valid?(number.to_s)
|
||||
def initialize(number, opts = {})
|
||||
raise Error::InvalidWordPressVersion unless WpVersion.valid?(number.to_s)
|
||||
|
||||
super(number, opts)
|
||||
end
|
||||
super(number, opts)
|
||||
end
|
||||
|
||||
# @param [ String ] number
|
||||
#
|
||||
# @return [ Boolean ] true if the number is a valid WP version, false otherwise
|
||||
def self.valid?(number)
|
||||
all.include?(number)
|
||||
end
|
||||
# @param [ String ] number
|
||||
#
|
||||
# @return [ Boolean ] true if the number is a valid WP version, false otherwise
|
||||
def self.valid?(number)
|
||||
all.include?(number)
|
||||
end
|
||||
|
||||
# @return [ Array<String> ] All the version numbers
|
||||
def self.all
|
||||
return @all_numbers if @all_numbers
|
||||
# @return [ Array<String> ] All the version numbers
|
||||
def self.all
|
||||
return @all_numbers if @all_numbers
|
||||
|
||||
@all_numbers = []
|
||||
@all_numbers = []
|
||||
|
||||
DB::Fingerprints.wp_fingerprints.each_value do |fp|
|
||||
fp.each_value do |versions|
|
||||
versions.each do |version|
|
||||
@all_numbers << version unless @all_numbers.include?(version)
|
||||
DB::Fingerprints.wp_fingerprints.each_value do |fp|
|
||||
fp.each_value do |versions|
|
||||
versions.each do |version|
|
||||
@all_numbers << version unless @all_numbers.include?(version)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@all_numbers.sort! { |a, b| Gem::Version.new(b) <=> Gem::Version.new(a) }
|
||||
end
|
||||
|
||||
@all_numbers.sort! { |a, b| Gem::Version.new(b) <=> Gem::Version.new(a) }
|
||||
end
|
||||
|
||||
# @return [ JSON ]
|
||||
def db_data
|
||||
DB::Version.db_data(number)
|
||||
end
|
||||
|
||||
# @return [ Array<Vulnerability> ]
|
||||
def vulnerabilities
|
||||
return @vulnerabilities if @vulnerabilities
|
||||
|
||||
@vulnerabilities = []
|
||||
|
||||
[*db_data['vulnerabilities']].each do |json_vuln|
|
||||
@vulnerabilities << Vulnerability.load_from_json(json_vuln)
|
||||
# @return [ JSON ]
|
||||
def db_data
|
||||
DB::Version.db_data(number)
|
||||
end
|
||||
|
||||
@vulnerabilities
|
||||
end
|
||||
# @return [ Array<Vulnerability> ]
|
||||
def vulnerabilities
|
||||
return @vulnerabilities if @vulnerabilities
|
||||
|
||||
# @return [ String ]
|
||||
def release_date
|
||||
@release_date ||= db_data['release_date'] || 'Unknown'
|
||||
end
|
||||
@vulnerabilities = []
|
||||
|
||||
# @return [ String ]
|
||||
def status
|
||||
@status ||= db_data['status'] || 'Unknown'
|
||||
[*db_data['vulnerabilities']].each do |json_vuln|
|
||||
@vulnerabilities << Vulnerability.load_from_json(json_vuln)
|
||||
end
|
||||
|
||||
@vulnerabilities
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def release_date
|
||||
@release_date ||= db_data['release_date'] || 'Unknown'
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def status
|
||||
@status ||= db_data['status'] || 'Unknown'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
module WPScan
|
||||
# Override of the CMSScanner::XMLRPC to include the references
|
||||
class XMLRPC < CMSScanner::XMLRPC
|
||||
include References # To be able to use the :wpvulndb reference if needed
|
||||
module Model
|
||||
# Override of the CMSScanner::XMLRPC to include the references
|
||||
class XMLRPC < CMSScanner::Model::XMLRPC
|
||||
include References # To be able to use the :wpvulndb reference if needed
|
||||
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
{
|
||||
url: ['http://codex.wordpress.org/XML-RPC_Pingback_API'],
|
||||
metasploit: [
|
||||
'auxiliary/scanner/http/wordpress_ghost_scanner',
|
||||
'auxiliary/dos/http/wordpress_xmlrpc_dos',
|
||||
'auxiliary/scanner/http/wordpress_xmlrpc_login',
|
||||
'auxiliary/scanner/http/wordpress_pingback_access'
|
||||
]
|
||||
}
|
||||
# @return [ Hash ]
|
||||
def references
|
||||
{
|
||||
url: ['http://codex.wordpress.org/XML-RPC_Pingback_API'],
|
||||
metasploit: [
|
||||
'auxiliary/scanner/http/wordpress_ghost_scanner',
|
||||
'auxiliary/dos/http/wordpress_xmlrpc_dos',
|
||||
'auxiliary/scanner/http/wordpress_xmlrpc_login',
|
||||
'auxiliary/scanner/http/wordpress_pingback_access'
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user