wpscan 3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile.lock +139 -0
- data/LICENSE +74 -0
- data/README.md +146 -0
- data/app/app.rb +3 -0
- data/app/controllers.rb +6 -0
- data/app/controllers/brute_force.rb +126 -0
- data/app/controllers/core.rb +104 -0
- data/app/controllers/custom_directories.rb +23 -0
- data/app/controllers/enumeration.rb +53 -0
- data/app/controllers/enumeration/cli_options.rb +126 -0
- data/app/controllers/enumeration/enum_methods.rb +157 -0
- data/app/controllers/main_theme.rb +27 -0
- data/app/controllers/wp_version.rb +30 -0
- data/app/finders.rb +13 -0
- data/app/finders/config_backups.rb +17 -0
- data/app/finders/config_backups/known_filenames.rb +46 -0
- data/app/finders/interesting_findings.rb +33 -0
- data/app/finders/interesting_findings/backup_db.rb +25 -0
- data/app/finders/interesting_findings/debug_log.rb +20 -0
- data/app/finders/interesting_findings/duplicator_installer_log.rb +23 -0
- data/app/finders/interesting_findings/full_path_disclosure.rb +23 -0
- data/app/finders/interesting_findings/mu_plugins.rb +48 -0
- data/app/finders/interesting_findings/multisite.rb +29 -0
- data/app/finders/interesting_findings/readme.rb +26 -0
- data/app/finders/interesting_findings/registration.rb +31 -0
- data/app/finders/interesting_findings/tmm_db_migrate.rb +24 -0
- data/app/finders/interesting_findings/upload_directory_listing.rb +24 -0
- data/app/finders/interesting_findings/upload_sql_dump.rb +28 -0
- data/app/finders/main_theme.rb +22 -0
- data/app/finders/main_theme/css_style.rb +43 -0
- data/app/finders/main_theme/urls_in_homepage.rb +25 -0
- data/app/finders/main_theme/woo_framework_meta_generator.rb +22 -0
- data/app/finders/medias.rb +17 -0
- data/app/finders/medias/attachment_brute_forcing.rb +44 -0
- data/app/finders/plugin_version.rb +44 -0
- data/app/finders/plugin_version/layer_slider/translation_file.rb +40 -0
- data/app/finders/plugin_version/readme.rb +79 -0
- data/app/finders/plugin_version/revslider/release_log.rb +35 -0
- data/app/finders/plugin_version/sitepress_multilingual_cms/meta_generator.rb +27 -0
- data/app/finders/plugin_version/sitepress_multilingual_cms/version_parameter.rb +31 -0
- data/app/finders/plugin_version/w3_total_cache/headers.rb +28 -0
- data/app/finders/plugins.rb +24 -0
- data/app/finders/plugins/comments.rb +31 -0
- data/app/finders/plugins/headers.rb +36 -0
- data/app/finders/plugins/known_locations.rb +48 -0
- data/app/finders/plugins/urls_in_homepage.rb +29 -0
- data/app/finders/theme_version.rb +41 -0
- data/app/finders/theme_version/style.rb +43 -0
- data/app/finders/theme_version/woo_framework_meta_generator.rb +19 -0
- data/app/finders/themes.rb +20 -0
- data/app/finders/themes/known_locations.rb +48 -0
- data/app/finders/themes/urls_in_homepage.rb +23 -0
- data/app/finders/timthumb_version.rb +17 -0
- data/app/finders/timthumb_version/bad_request.rb +21 -0
- data/app/finders/timthumbs.rb +17 -0
- data/app/finders/timthumbs/known_locations.rb +56 -0
- data/app/finders/users.rb +24 -0
- data/app/finders/users/author_id_brute_forcing.rb +111 -0
- data/app/finders/users/author_posts.rb +61 -0
- data/app/finders/users/login_error_messages.rb +50 -0
- data/app/finders/users/wp_json_api.rb +31 -0
- data/app/finders/wp_items.rb +1 -0
- data/app/finders/wp_items/urls_in_homepage.rb +68 -0
- data/app/finders/wp_version.rb +34 -0
- data/app/finders/wp_version/atom_generator.rb +40 -0
- data/app/finders/wp_version/meta_generator.rb +27 -0
- data/app/finders/wp_version/opml_generator.rb +23 -0
- data/app/finders/wp_version/rdf_generator.rb +38 -0
- data/app/finders/wp_version/readme.rb +28 -0
- data/app/finders/wp_version/rss_generator.rb +43 -0
- data/app/finders/wp_version/sitemap_generator.rb +23 -0
- data/app/finders/wp_version/stylesheets.rb +55 -0
- data/app/finders/wp_version/unique_fingerprinting.rb +64 -0
- data/app/models.rb +10 -0
- data/app/models/config_backup.rb +5 -0
- data/app/models/interesting_finding.rb +6 -0
- data/app/models/media.rb +5 -0
- data/app/models/plugin.rb +25 -0
- data/app/models/theme.rb +99 -0
- data/app/models/timthumb.rb +74 -0
- data/app/models/user.rb +31 -0
- data/app/models/wp_item.rb +142 -0
- data/app/models/wp_version.rb +49 -0
- data/app/models/xml_rpc.rb +19 -0
- data/app/views/cli/brute_force/error.erb +1 -0
- data/app/views/cli/brute_force/found.erb +2 -0
- data/app/views/cli/brute_force/users.erb +9 -0
- data/app/views/cli/core/banner.erb +14 -0
- data/app/views/cli/core/db_update_finished.erb +8 -0
- data/app/views/cli/core/db_update_started.erb +1 -0
- data/app/views/cli/core/not_fully_configured.erb +1 -0
- data/app/views/cli/enumeration/config_backups.erb +11 -0
- data/app/views/cli/enumeration/medias.erb +11 -0
- data/app/views/cli/enumeration/plugins.erb +35 -0
- data/app/views/cli/enumeration/themes.erb +11 -0
- data/app/views/cli/enumeration/timthumbs.erb +18 -0
- data/app/views/cli/enumeration/users.erb +11 -0
- data/app/views/cli/finding.erb +32 -0
- data/app/views/cli/info.erb +1 -0
- data/app/views/cli/main_theme/theme.erb +6 -0
- data/app/views/cli/notice.erb +1 -0
- data/app/views/cli/theme.erb +64 -0
- data/app/views/cli/usage.erb +3 -0
- data/app/views/cli/vulnerability.erb +14 -0
- data/app/views/cli/wp_version/version.erb +6 -0
- data/app/views/json/brute_force/users.erb +10 -0
- data/app/views/json/core/banner.erb +12 -0
- data/app/views/json/core/db_update_finished.erb +2 -0
- data/app/views/json/core/db_update_started.erb +1 -0
- data/app/views/json/core/not_fully_configured.erb +1 -0
- data/app/views/json/enumeration/config_backups.erb +10 -0
- data/app/views/json/enumeration/medias.erb +10 -0
- data/app/views/json/enumeration/plugins.erb +25 -0
- data/app/views/json/enumeration/themes.erb +10 -0
- data/app/views/json/enumeration/timthumbs.erb +19 -0
- data/app/views/json/enumeration/users.erb +11 -0
- data/app/views/json/finding.erb +26 -0
- data/app/views/json/main_theme/theme.erb +7 -0
- data/app/views/json/theme.erb +38 -0
- data/app/views/json/wp_version/version.erb +8 -0
- data/bin/wpscan +15 -0
- data/coverage/assets/0.10.0/application.css +799 -0
- data/coverage/assets/0.10.0/application.js +1707 -0
- data/coverage/assets/0.10.0/colorbox/border.png +0 -0
- data/coverage/assets/0.10.0/colorbox/controls.png +0 -0
- data/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
- data/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.10.0/favicon_green.png +0 -0
- data/coverage/assets/0.10.0/favicon_red.png +0 -0
- data/coverage/assets/0.10.0/favicon_yellow.png +0 -0
- data/coverage/assets/0.10.0/loading.gif +0 -0
- data/coverage/assets/0.10.0/magnify.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/index.html +27510 -0
- data/lib/wpscan.rb +44 -0
- data/lib/wpscan/browser.rb +16 -0
- data/lib/wpscan/controller.rb +8 -0
- data/lib/wpscan/controllers.rb +8 -0
- data/lib/wpscan/db.rb +28 -0
- data/lib/wpscan/db/dynamic_finders.rb +63 -0
- data/lib/wpscan/db/plugin.rb +11 -0
- data/lib/wpscan/db/plugins.rb +11 -0
- data/lib/wpscan/db/schema.rb +39 -0
- data/lib/wpscan/db/theme.rb +11 -0
- data/lib/wpscan/db/themes.rb +11 -0
- data/lib/wpscan/db/updater.rb +148 -0
- data/lib/wpscan/db/wp_item.rb +18 -0
- data/lib/wpscan/db/wp_items.rb +21 -0
- data/lib/wpscan/db/wp_version.rb +11 -0
- data/lib/wpscan/errors/http.rb +34 -0
- data/lib/wpscan/errors/update.rb +8 -0
- data/lib/wpscan/errors/wordpress.rb +22 -0
- data/lib/wpscan/finders.rb +14 -0
- data/lib/wpscan/finders/finder/plugin_version/comments.rb +25 -0
- data/lib/wpscan/finders/finder/wp_version/smart_url_checker.rb +23 -0
- data/lib/wpscan/helper.rb +6 -0
- data/lib/wpscan/references.rb +31 -0
- data/lib/wpscan/target.rb +81 -0
- data/lib/wpscan/target/platform/wordpress.rb +74 -0
- data/lib/wpscan/target/platform/wordpress/custom_directories.rb +93 -0
- data/lib/wpscan/version.rb +4 -0
- data/lib/wpscan/vulnerability.rb +25 -0
- data/lib/wpscan/vulnerable.rb +10 -0
- data/wpscan-v3.sublime-project +8 -0
- data/wpscan-v3.sublime-workspace +895 -0
- data/wpscan.gemspec +55 -0
- metadata +419 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# Atom Generator Version Finder
|
5
|
+
class AtomGenerator < CMSScanner::Finders::Finder
|
6
|
+
include Finder::WpVersion::SmartURLChecker
|
7
|
+
|
8
|
+
def process_urls(urls, _opts = {})
|
9
|
+
found = Findings.new
|
10
|
+
|
11
|
+
urls.each do |url|
|
12
|
+
res = Browser.get_and_follow_location(url)
|
13
|
+
|
14
|
+
res.html.css('generator').each do |node|
|
15
|
+
next unless node.text.to_s.strip.casecmp('wordpress').zero?
|
16
|
+
|
17
|
+
found << create_version(
|
18
|
+
node['version'],
|
19
|
+
found_by: found_by,
|
20
|
+
entries: ["#{res.effective_url}, #{node}"]
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
found
|
26
|
+
end
|
27
|
+
|
28
|
+
def passive_urls_xpath
|
29
|
+
'//link[@rel="alternate" and @type="application/atom+xml"]'
|
30
|
+
end
|
31
|
+
|
32
|
+
def aggressive_urls(_opts = {})
|
33
|
+
%w(feed/atom/ ?feed=atom).reduce([]) do |a, uri|
|
34
|
+
a << target.url(uri)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# Meta Generator Version Finder
|
5
|
+
class MetaGenerator < CMSScanner::Finders::Finder
|
6
|
+
# @return [ WpVersion ]
|
7
|
+
def passive(_opts = {})
|
8
|
+
target.homepage_res.html.css('meta[name="generator"]').each do |node|
|
9
|
+
next unless node.attribute('content').to_s =~ /wordpress ([0-9\.]+)/i
|
10
|
+
|
11
|
+
number = Regexp.last_match(1)
|
12
|
+
|
13
|
+
next unless WPScan::WpVersion.valid?(number)
|
14
|
+
|
15
|
+
return WPScan::WpVersion.new(
|
16
|
+
number,
|
17
|
+
found_by: 'Meta Generator (Passive detection)',
|
18
|
+
confidence: 80,
|
19
|
+
interesting_entries: ["#{target.url}, Match: '#{node}'"]
|
20
|
+
)
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# Sitemap Generator Version Finder
|
5
|
+
class OpmlGenerator < CMSScanner::Finders::Finder
|
6
|
+
# @return [ WpVersion ]
|
7
|
+
def aggressive(_opts = {})
|
8
|
+
target.comments_from_page(%r{\Agenerator="wordpress/([^"]+)"\z}i, 'wp-links-opml.php') do |match, node|
|
9
|
+
next unless WPScan::WpVersion.valid?(match[1])
|
10
|
+
|
11
|
+
return WPScan::WpVersion.new(
|
12
|
+
match[1],
|
13
|
+
found_by: 'OPML Generator (Aggressive Detection)',
|
14
|
+
confidence: 80,
|
15
|
+
interesting_entries: ["#{target.url('wp-links-opml.php')}, Match: '#{node}'"]
|
16
|
+
)
|
17
|
+
end
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# RDF Generator Version Finder
|
5
|
+
class RDFGenerator < CMSScanner::Finders::Finder
|
6
|
+
include Finder::WpVersion::SmartURLChecker
|
7
|
+
|
8
|
+
def process_urls(urls, _opts = {})
|
9
|
+
found = Findings.new
|
10
|
+
|
11
|
+
urls.each do |url|
|
12
|
+
res = Browser.get_and_follow_location(url)
|
13
|
+
|
14
|
+
res.html.xpath('//generatoragent').each do |node|
|
15
|
+
next unless node['rdf:resource'] =~ %r{\Ahttps?://wordpress\.(?:[a-z.]+)/\?v=(.*)\z}i
|
16
|
+
|
17
|
+
found << create_version(
|
18
|
+
Regexp.last_match[1],
|
19
|
+
found_by: found_by,
|
20
|
+
entries: ["#{res.effective_url}, #{node}"]
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
found
|
26
|
+
end
|
27
|
+
|
28
|
+
def passive_urls_xpath
|
29
|
+
'//a[contains(@href, "rdf")]'
|
30
|
+
end
|
31
|
+
|
32
|
+
def aggressive_urls(_opts = {})
|
33
|
+
[target.url('feed/rdf/')]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# Readme Version Finder
|
5
|
+
class Readme < CMSScanner::Finders::Finder
|
6
|
+
# @return [ WpVersion ]
|
7
|
+
def aggressive(_opts = {})
|
8
|
+
readme_url = target.url('readme.html') # Maybe move this into the Target ?
|
9
|
+
|
10
|
+
node = Browser.get(readme_url).html.css('h1#logo').last
|
11
|
+
|
12
|
+
return unless node && node.text.to_s.strip =~ /\AVersion (.*)\z/i
|
13
|
+
|
14
|
+
number = Regexp.last_match(1)
|
15
|
+
|
16
|
+
return unless WPScan::WpVersion.valid?(number)
|
17
|
+
|
18
|
+
WPScan::WpVersion.new(
|
19
|
+
number,
|
20
|
+
found_by: 'Readme (Aggressive Detection)',
|
21
|
+
confidence: 90,
|
22
|
+
interesting_entries: ["#{readme_url}, Match: '#{node.text.to_s.strip}'"]
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# RSS Generator Version Finder
|
5
|
+
class RSSGenerator < CMSScanner::Finders::Finder
|
6
|
+
include Finder::WpVersion::SmartURLChecker
|
7
|
+
|
8
|
+
def process_urls(urls, _opts = {})
|
9
|
+
found = Findings.new
|
10
|
+
|
11
|
+
urls.each do |url|
|
12
|
+
res = Browser.get_and_follow_location(url)
|
13
|
+
|
14
|
+
res.html.xpath('//comment()[contains(., "wordpress")] | //generator').each do |node|
|
15
|
+
node_text = node.text.to_s.strip
|
16
|
+
|
17
|
+
next unless node_text =~ %r{\Ahttps?://wordpress\.(?:[a-z]+)/\?v=(.*)\z}i ||
|
18
|
+
node_text =~ %r{\Agenerator="wordpress/([^"]+)"\z}i
|
19
|
+
|
20
|
+
found << create_version(
|
21
|
+
Regexp.last_match[1],
|
22
|
+
found_by: found_by,
|
23
|
+
entries: ["#{res.effective_url}, #{node}"]
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
found
|
29
|
+
end
|
30
|
+
|
31
|
+
def passive_urls_xpath
|
32
|
+
'//link[@rel="alternate" and @type="application/rss+xml"]'
|
33
|
+
end
|
34
|
+
|
35
|
+
def aggressive_urls(_opts = {})
|
36
|
+
%w(feed/ comments/feed/ feed/rss/ feed/rss2/).reduce([]) do |a, uri|
|
37
|
+
a << target.url(uri)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# Sitemap Generator Version Finder
|
5
|
+
class SitemapGenerator < CMSScanner::Finders::Finder
|
6
|
+
# @return [ WpVersion ]
|
7
|
+
def aggressive(_opts = {})
|
8
|
+
target.comments_from_page(%r{\Agenerator="wordpress/([^"]+)"\z}i, 'sitemap.xml') do |match, node|
|
9
|
+
next unless WPScan::WpVersion.valid?(match[1])
|
10
|
+
|
11
|
+
return WPScan::WpVersion.new(
|
12
|
+
match[1],
|
13
|
+
found_by: 'Sitemap Generator (Aggressive Detection)',
|
14
|
+
confidence: 80,
|
15
|
+
interesting_entries: ["#{target.url('sitemap.xml')}, #{node}"]
|
16
|
+
)
|
17
|
+
end
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# Stylesheets Version Finder
|
5
|
+
class Stylesheets < CMSScanner::Finders::Finder
|
6
|
+
# @return [ WpVersion ]
|
7
|
+
def passive(_opts = {})
|
8
|
+
found = []
|
9
|
+
|
10
|
+
scan_page(target.homepage_url).each do |version_number, occurences|
|
11
|
+
next unless WPScan::WpVersion.valid?(version_number) # Skip invalid versions
|
12
|
+
|
13
|
+
found << WPScan::WpVersion.new(
|
14
|
+
version_number,
|
15
|
+
found_by: 'Stylesheet Numbers (Passive Detection)',
|
16
|
+
confidence: 5 * occurences,
|
17
|
+
interesting_entries: [target.homepage_url]
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
found
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
# TODO: use target.in_scope_urls to get the URLs
|
27
|
+
# @param [ String ] url
|
28
|
+
#
|
29
|
+
# @return [ Hash ]
|
30
|
+
def scan_page(url)
|
31
|
+
found = {}
|
32
|
+
pattern = /\bver=([0-9\.]+)/i
|
33
|
+
|
34
|
+
Browser.get(url).html.css('link,script').each do |tag|
|
35
|
+
%w(href src).each do |attribute|
|
36
|
+
attr_value = tag.attribute(attribute).to_s
|
37
|
+
|
38
|
+
next if attr_value.nil? || attr_value.empty?
|
39
|
+
|
40
|
+
uri = Addressable::URI.parse(attr_value)
|
41
|
+
next unless uri.query && uri.query.match(pattern)
|
42
|
+
|
43
|
+
version = Regexp.last_match[1].to_s
|
44
|
+
|
45
|
+
found[version] ||= 0
|
46
|
+
found[version] += 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
found
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module WPScan
|
2
|
+
module Finders
|
3
|
+
module WpVersion
|
4
|
+
# Unique Fingerprinting Version Finder
|
5
|
+
class UniqueFingerprinting < CMSScanner::Finders::Finder
|
6
|
+
include CMSScanner::Finders::Finder::Fingerprinter
|
7
|
+
|
8
|
+
QUERY = 'SELECT md5_hash, path_id, version_id, ' \
|
9
|
+
'versions.number AS version,' \
|
10
|
+
'paths.value AS path ' \
|
11
|
+
'FROM fingerprints ' \
|
12
|
+
'LEFT JOIN versions ON version_id = versions.id ' \
|
13
|
+
'LEFT JOIN paths on path_id = paths.id ' \
|
14
|
+
'WHERE md5_hash IN ' \
|
15
|
+
'(SELECT md5_hash FROM fingerprints GROUP BY md5_hash HAVING COUNT(*) = 1) ' \
|
16
|
+
'ORDER BY version DESC'.freeze
|
17
|
+
|
18
|
+
# @return [ WpVersion ]
|
19
|
+
def aggressive(opts = {})
|
20
|
+
fingerprint(unique_fingerprints, opts) do |version_number, url, md5sum|
|
21
|
+
hydra.abort
|
22
|
+
progress_bar.finish
|
23
|
+
|
24
|
+
return WPScan::WpVersion.new(
|
25
|
+
version_number,
|
26
|
+
found_by: 'Unique Fingerprinting (Aggressive Detection)',
|
27
|
+
confidence: 100,
|
28
|
+
interesting_entries: ["#{url} md5sum is #{md5sum}"]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [ Hash ] The unique fingerprints across all versions in the DB
|
35
|
+
#
|
36
|
+
# Format returned:
|
37
|
+
# {
|
38
|
+
# file_path_1: {
|
39
|
+
# md5_hash_1: version_1,
|
40
|
+
# md5_hash_2: version_2
|
41
|
+
# },
|
42
|
+
# file_path_2: {
|
43
|
+
# md5_hash_3: version_1,
|
44
|
+
# md5_hash_4: version_3
|
45
|
+
# }
|
46
|
+
# }
|
47
|
+
def unique_fingerprints
|
48
|
+
fingerprints = {}
|
49
|
+
|
50
|
+
repository(:default).adapter.select(QUERY).each do |f|
|
51
|
+
fingerprints[f.path] ||= {}
|
52
|
+
fingerprints[f.path][f.md5_hash] = f.version
|
53
|
+
end
|
54
|
+
|
55
|
+
fingerprints
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_progress_bar(opts = {})
|
59
|
+
super(opts.merge(title: 'Fingerprinting the version -'))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/app/models.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative 'models/interesting_finding'
|
2
|
+
require_relative 'models/wp_version'
|
3
|
+
require_relative 'models/xml_rpc'
|
4
|
+
require_relative 'models/wp_item'
|
5
|
+
require_relative 'models/timthumb'
|
6
|
+
require_relative 'models/media'
|
7
|
+
require_relative 'models/user'
|
8
|
+
require_relative 'models/plugin'
|
9
|
+
require_relative 'models/theme'
|
10
|
+
require_relative 'models/config_backup'
|
data/app/models/media.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module WPScan
|
2
|
+
# WordPress Plugin
|
3
|
+
class Plugin < WpItem
|
4
|
+
# See WpItem
|
5
|
+
def initialize(name, target, opts = {})
|
6
|
+
super(name, target, opts)
|
7
|
+
|
8
|
+
@uri = Addressable::URI.parse(target.url("wp-content/plugins/#{name}/"))
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [ JSON ]
|
12
|
+
def db_data
|
13
|
+
DB::Plugin.db_data(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [ Hash ] opts
|
17
|
+
#
|
18
|
+
# @return [ WPScan::Version, false ]
|
19
|
+
def version(opts = {})
|
20
|
+
@version = Finders::PluginVersion::Base.find(self, detection_opts.merge(opts)) if @version.nil?
|
21
|
+
|
22
|
+
@version
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/app/models/theme.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
module WPScan
|
2
|
+
# WordPress Theme
|
3
|
+
class Theme < WpItem
|
4
|
+
attr_reader :style_url, :style_name, :style_uri, :author, :author_uri, :template, :description,
|
5
|
+
:license, :license_uri, :tags, :text_domain
|
6
|
+
|
7
|
+
# See WpItem
|
8
|
+
def initialize(name, target, opts = {})
|
9
|
+
super(name, target, opts)
|
10
|
+
|
11
|
+
@uri = Addressable::URI.parse(target.url("wp-content/themes/#{name}/"))
|
12
|
+
@style_url = opts[:style_url] || url('style.css')
|
13
|
+
|
14
|
+
parse_style
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [ JSON ]
|
18
|
+
def db_data
|
19
|
+
DB::Theme.db_data(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [ Hash ] opts
|
23
|
+
#
|
24
|
+
# @return [ WPScan::Version, false ]
|
25
|
+
def version(opts = {})
|
26
|
+
@version = Finders::ThemeVersion::Base.find(self, detection_opts.merge(opts)) if @version.nil?
|
27
|
+
|
28
|
+
@version
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [ Theme ]
|
32
|
+
def parent_theme
|
33
|
+
return unless template
|
34
|
+
return unless style_body =~ /^@import\surl\(["']?([^"'\)]+)["']?\);\s*$/i
|
35
|
+
|
36
|
+
opts = detection_opts.merge(
|
37
|
+
style_url: url(Regexp.last_match[1]),
|
38
|
+
found_by: 'Parent Themes (Passive Detection)',
|
39
|
+
confidence: 100
|
40
|
+
)
|
41
|
+
|
42
|
+
self.class.new(template, target, opts)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [ Integer ] depth
|
46
|
+
#
|
47
|
+
# @retun [ Array<Theme> ]
|
48
|
+
def parent_themes(depth = 3)
|
49
|
+
theme = self
|
50
|
+
found = []
|
51
|
+
|
52
|
+
(1..depth).each do |_|
|
53
|
+
parent = theme.parent_theme
|
54
|
+
|
55
|
+
break unless parent
|
56
|
+
|
57
|
+
found << parent
|
58
|
+
theme = parent
|
59
|
+
end
|
60
|
+
|
61
|
+
found
|
62
|
+
end
|
63
|
+
|
64
|
+
def style_body
|
65
|
+
@style_body ||= Browser.get(style_url).body
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse_style
|
69
|
+
{
|
70
|
+
style_name: 'Theme Name',
|
71
|
+
style_uri: 'Theme URI',
|
72
|
+
author: 'Author',
|
73
|
+
author_uri: 'Author URI',
|
74
|
+
template: 'Template',
|
75
|
+
description: 'Description',
|
76
|
+
license: 'License',
|
77
|
+
license_uri: 'License URI',
|
78
|
+
tags: 'Tags',
|
79
|
+
text_domain: 'Text Domain'
|
80
|
+
}.each do |attribute, tag|
|
81
|
+
instance_variable_set(:"@#{attribute}", parse_style_tag(style_body, tag))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param [ String ] bofy
|
86
|
+
# @param [ String ] tag
|
87
|
+
#
|
88
|
+
# @return [ String ]
|
89
|
+
def parse_style_tag(body, tag)
|
90
|
+
value = body[/^\s*#{Regexp.escape(tag)}:[\t ]*([^\r\n]+)/i, 1]
|
91
|
+
|
92
|
+
value && !value.strip.empty? ? value.strip : nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def ==(other)
|
96
|
+
super(other) && style_url == other.style_url
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|