Added checks for old ruby. Otherwise there will be syntax errors
This commit is contained in:
@@ -124,10 +124,6 @@ def banner
|
||||
puts ' Sponsored by the RandomStorm Open Source Initiative'
|
||||
puts '_____________________________________________________'
|
||||
puts
|
||||
if RUBY_VERSION < '1.9'
|
||||
puts '[WARNING] Ruby < 1.9 not officially supported, please upgrade.'
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
def colorize(text, color_code)
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#++
|
||||
|
||||
require 'rubygems'
|
||||
|
||||
if Gem::Version.create(RUBY_VERSION) < Gem::Version.create(1.9)
|
||||
puts "Ruby >= 1.9 required to run wpscan (You have #{RUBY_VERSION})"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
begin
|
||||
# Standard libs
|
||||
require 'rubygems'
|
||||
|
||||
402
main.rb
Normal file
402
main.rb
Normal file
@@ -0,0 +1,402 @@
|
||||
#!/usr/bin/env ruby
|
||||
# encoding: UTF-8
|
||||
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#++
|
||||
|
||||
def output_vulnerabilities(vulns)
|
||||
vulns.each do |vulnerability|
|
||||
puts
|
||||
puts ' | ' + red("* Title: #{vulnerability.title}")
|
||||
vulnerability.references.each do |r|
|
||||
puts ' | ' + red("* Reference: #{r}")
|
||||
end
|
||||
vulnerability.metasploit_modules.each do |m|
|
||||
puts ' | ' + red("* Metasploit module: #{get_metasploit_url(m)}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def output_item_details(item)
|
||||
puts
|
||||
puts " | Name: #{item}" #this will also output the version number if detected
|
||||
puts " | Location: #{item.get_url_without_filename}"
|
||||
puts " | WordPress: #{item.wp_org_url}" if item.wp_org_item?
|
||||
puts ' | Directory listing enabled: Yes' if item.directory_listing?
|
||||
puts " | Readme: #{item.readme_url}" if item.has_readme?
|
||||
puts " | Changelog: #{item.changelog_url}" if item.has_changelog?
|
||||
|
||||
output_vulnerabilities(item.vulnerabilities)
|
||||
|
||||
if item.error_log?
|
||||
puts ' | ' + red('[!]') + " A WordPress error_log file has been found : #{item.error_log_url}"
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
# delete old logfile, check if it is a symlink first.
|
||||
File.delete(LOG_FILE) if File.exist?(LOG_FILE) and !File.symlink?(LOG_FILE)
|
||||
|
||||
banner()
|
||||
|
||||
begin
|
||||
wpscan_options = WpscanOptions.load_from_arguments
|
||||
|
||||
unless wpscan_options.has_options?
|
||||
raise "No argument supplied\n#{usage()}"
|
||||
end
|
||||
|
||||
if wpscan_options.help
|
||||
help()
|
||||
usage()
|
||||
exit
|
||||
end
|
||||
|
||||
# Check for updates
|
||||
if wpscan_options.update
|
||||
if !@updater.nil?
|
||||
if @updater.has_local_changes?
|
||||
puts "#{red('[!]')} Local file changes detected, an update will override local changes, do you want to continue updating? [y/n]"
|
||||
Readline.readline =~ /^y/i ? @updater.reset_head : raise('Update aborted')
|
||||
end
|
||||
puts @updater.update()
|
||||
else
|
||||
puts 'Svn / Git not installed, or wpscan has not been installed with one of them.'
|
||||
puts 'Update aborted'
|
||||
end
|
||||
exit(1)
|
||||
end
|
||||
|
||||
wp_target = WpTarget.new(wpscan_options.url, wpscan_options.to_h)
|
||||
|
||||
# Remote website up?
|
||||
unless wp_target.online?
|
||||
raise "The WordPress URL supplied '#{wp_target.uri}' seems to be down."
|
||||
end
|
||||
|
||||
if wpscan_options.proxy
|
||||
proxy_response = Browser.instance.get(wp_target.url)
|
||||
|
||||
unless WpTarget::valid_response_codes.include?(proxy_response.code)
|
||||
raise "Proxy Error :\r\n#{proxy_response.headers}"
|
||||
end
|
||||
end
|
||||
|
||||
redirection = wp_target.redirection
|
||||
if redirection
|
||||
if wpscan_options.follow_redirection
|
||||
puts "Following redirection #{redirection}"
|
||||
puts
|
||||
else
|
||||
puts "The remote host tried to redirect us to #{redirection}"
|
||||
puts 'Do you want follow the redirection ? [y/n]'
|
||||
end
|
||||
|
||||
if wpscan_options.follow_redirection or Readline.readline =~ /^y/i
|
||||
wpscan_options.url = redirection
|
||||
wp_target = WpTarget.new(redirection, wpscan_options.to_h)
|
||||
else
|
||||
puts 'Scan aborted'
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
if wp_target.has_basic_auth? && wpscan_options.basic_auth.nil?
|
||||
raise 'Basic authentication is required, please provide it with --basic-auth <login:password>'
|
||||
end
|
||||
|
||||
# Remote website is wordpress?
|
||||
unless wpscan_options.force
|
||||
unless wp_target.wordpress?
|
||||
raise 'The remote website is up, but does not seem to be running WordPress.'
|
||||
end
|
||||
end
|
||||
|
||||
unless wp_target.wp_content_dir
|
||||
raise 'The wp_content_dir has not been found, please supply it with --wp-content-dir'
|
||||
end
|
||||
|
||||
unless wp_target.wp_plugins_dir_exists?
|
||||
puts "The plugins directory '#{wp_target.wp_plugins_dir}' does not exist."
|
||||
puts 'You can specify one per command line option (don\'t forget to include the wp-content directory if needed)'
|
||||
puts 'Continue? [y/n]'
|
||||
unless Readline.readline =~ /^y/i
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
# Output runtime data
|
||||
start_time = Time.now
|
||||
puts "| URL: #{wp_target.url}"
|
||||
puts "| Started on #{start_time.asctime}"
|
||||
puts
|
||||
|
||||
if wp_target.has_robots?
|
||||
puts green('[+]') + " robots.txt available under '#{wp_target.robots_url}'"
|
||||
end
|
||||
|
||||
if wp_target.has_readme?
|
||||
puts red('[!]') + " The WordPress '#{wp_target.readme_url}' file exists"
|
||||
end
|
||||
|
||||
if wp_target.has_full_path_disclosure?
|
||||
puts red('[!]') + " Full Path Disclosure (FPD) in '#{wp_target.full_path_disclosure_url}'"
|
||||
end
|
||||
|
||||
if wp_target.has_debug_log?
|
||||
puts red('[!]') + " Debug log file found : #{wp_target.debug_log_url}"
|
||||
end
|
||||
|
||||
wp_target.config_backup.each do |file_url|
|
||||
puts red("[!] A wp-config.php backup file has been found '#{file_url}'")
|
||||
end
|
||||
|
||||
if wp_target.search_replace_db_2_exists?
|
||||
puts red("[!] searchreplacedb2.php has been found '#{wp_target.search_replace_db_2_url}'")
|
||||
end
|
||||
|
||||
if wp_target.is_multisite?
|
||||
puts green('[+]') + ' This site seems to be a multisite (http://codex.wordpress.org/Glossary#Multisite)'
|
||||
end
|
||||
|
||||
if wp_target.registration_enabled?
|
||||
puts green('[+]') + ' User registration is enabled'
|
||||
end
|
||||
|
||||
if wp_target.has_xml_rpc?
|
||||
puts green('[+]') + " XML-RPC Interface available under #{wp_target.xml_rpc_url}"
|
||||
end
|
||||
|
||||
if wp_target.has_malwares?
|
||||
malwares = wp_target.malwares
|
||||
puts red('[!]') + " #{malwares.size} malware(s) found :"
|
||||
|
||||
malwares.each do |malware_url|
|
||||
puts
|
||||
puts ' | ' + red("#{malware_url}")
|
||||
end
|
||||
puts
|
||||
end
|
||||
|
||||
wp_version = wp_target.version
|
||||
if wp_version
|
||||
puts green('[+]') + " WordPress version #{wp_version.number} identified from #{wp_version.discovery_method}"
|
||||
|
||||
version_vulnerabilities = wp_version.vulnerabilities
|
||||
|
||||
unless version_vulnerabilities.empty?
|
||||
puts
|
||||
puts red('[!]') + " We have identified #{version_vulnerabilities.size} vulnerabilities from the version number :"
|
||||
output_vulnerabilities(version_vulnerabilities)
|
||||
end
|
||||
end
|
||||
|
||||
wp_theme = wp_target.theme
|
||||
if wp_theme
|
||||
puts
|
||||
# Theme version is handled in wp_item.to_s
|
||||
puts green('[+]') + " The WordPress theme in use is #{wp_theme}"
|
||||
output_item_details(wp_theme)
|
||||
end
|
||||
|
||||
if wpscan_options.enumerate_plugins == nil and wpscan_options.enumerate_only_vulnerable_plugins == nil
|
||||
puts
|
||||
puts green('[+]') + ' Enumerating plugins from passive detection ... '
|
||||
|
||||
plugins = wp_target.plugins_from_passive_detection(base_url: wp_target.uri, wp_content_dir: wp_target.wp_content_dir)
|
||||
if !plugins.empty?
|
||||
puts "#{plugins.size} plugins found :"
|
||||
|
||||
plugins.each do |plugin|
|
||||
output_item_details(plugin)
|
||||
end
|
||||
else
|
||||
puts 'No plugins found :('
|
||||
end
|
||||
end
|
||||
|
||||
# Enumerate the installed plugins
|
||||
if wpscan_options.enumerate_plugins or wpscan_options.enumerate_only_vulnerable_plugins or wpscan_options.enumerate_all_plugins
|
||||
puts
|
||||
puts green('[+]') + " Enumerating installed plugins #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_plugins} ..."
|
||||
puts
|
||||
|
||||
options = {
|
||||
base_url: wp_target.uri,
|
||||
only_vulnerable_ones: wpscan_options.enumerate_only_vulnerable_plugins || false,
|
||||
show_progression: true,
|
||||
wp_content_dir: wp_target.wp_content_dir,
|
||||
error_404_hash: wp_target.error_404_hash,
|
||||
homepage_hash: wp_target.homepage_hash,
|
||||
wp_plugins_dir: wp_target.wp_plugins_dir,
|
||||
full: wpscan_options.enumerate_all_plugins,
|
||||
exclude_content_based: wpscan_options.exclude_content_based
|
||||
}
|
||||
|
||||
plugins = wp_target.plugins_from_aggressive_detection(options)
|
||||
if !plugins.empty?
|
||||
puts
|
||||
puts
|
||||
puts green('[+]') + " We found #{plugins.size.to_s} plugins:"
|
||||
|
||||
plugins.each do |plugin|
|
||||
output_item_details(plugin)
|
||||
end
|
||||
else
|
||||
puts
|
||||
puts 'No plugins found :('
|
||||
end
|
||||
end
|
||||
|
||||
# Enumerate installed themes
|
||||
if wpscan_options.enumerate_themes or wpscan_options.enumerate_only_vulnerable_themes or wpscan_options.enumerate_all_themes
|
||||
puts
|
||||
puts green('[+]') + " Enumerating installed themes #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_themes} ..."
|
||||
puts
|
||||
|
||||
options = {
|
||||
base_url: wp_target.uri,
|
||||
only_vulnerable_ones: wpscan_options.enumerate_only_vulnerable_themes || false,
|
||||
show_progression: true,
|
||||
wp_content_dir: wp_target.wp_content_dir,
|
||||
error_404_hash: wp_target.error_404_hash,
|
||||
homepage_hash: wp_target.homepage_hash,
|
||||
full: wpscan_options.enumerate_all_themes,
|
||||
exclude_content_based: wpscan_options.exclude_content_based
|
||||
}
|
||||
|
||||
themes = wp_target.themes_from_aggressive_detection(options)
|
||||
if !themes.empty?
|
||||
puts
|
||||
puts
|
||||
puts green('[+]') + " We found #{themes.size.to_s} themes:"
|
||||
|
||||
themes.each do |theme|
|
||||
output_item_details(theme)
|
||||
end
|
||||
else
|
||||
puts
|
||||
puts 'No themes found :('
|
||||
end
|
||||
end
|
||||
|
||||
if wpscan_options.enumerate_timthumbs
|
||||
puts
|
||||
puts green('[+]') + ' Enumerating timthumb files ...'
|
||||
puts
|
||||
|
||||
options = {
|
||||
base_url: wp_target.uri,
|
||||
show_progression: true,
|
||||
wp_content_dir: wp_target.wp_content_dir,
|
||||
error_404_hash: wp_target.error_404_hash,
|
||||
homepage_hash: wp_target.homepage_hash,
|
||||
exclude_content_based: wpscan_options.exclude_content_based
|
||||
}
|
||||
|
||||
theme_name = wp_theme ? wp_theme.name : nil
|
||||
if wp_target.has_timthumbs?(theme_name, options)
|
||||
timthumbs = wp_target.timthumbs
|
||||
|
||||
puts
|
||||
puts green('[+]') + " We found #{timthumbs.size.to_s} timthumb file/s :"
|
||||
puts
|
||||
|
||||
timthumbs.each do |t|
|
||||
puts ' | ' + red('[!]') + " #{t.get_full_url.to_s}"
|
||||
end
|
||||
puts
|
||||
puts red(' * Reference: http://www.exploit-db.com/exploits/17602/')
|
||||
else
|
||||
puts
|
||||
puts 'No timthumb files found :('
|
||||
end
|
||||
end
|
||||
|
||||
# If we haven't been supplied a username, enumerate them...
|
||||
if !wpscan_options.username and wpscan_options.wordlist or wpscan_options.enumerate_usernames
|
||||
puts
|
||||
puts green('[+]') + ' Enumerating usernames ...'
|
||||
|
||||
usernames = wp_target.usernames(range: wpscan_options.enumerate_usernames_range)
|
||||
|
||||
if usernames.empty?
|
||||
puts
|
||||
puts 'We did not enumerate any usernames :('
|
||||
puts 'Try supplying your own username with the --username option'
|
||||
puts
|
||||
exit(1)
|
||||
else
|
||||
puts
|
||||
puts green('[+]') + " We found the following #{usernames.length.to_s} username/s :"
|
||||
puts
|
||||
|
||||
max_id_length = usernames.sort { |a, b| a.id.to_s.length <=> b.id.to_s.length }.last.id.to_s.length
|
||||
max_name_length = usernames.sort { |a, b| a.name.length <=> b.name.length }.last.name.length
|
||||
max_nickname_length = usernames.sort { |a, b| a.nickname.length <=> b.nickname.length }.last.nickname.length
|
||||
|
||||
space = 1
|
||||
usernames.each do |u|
|
||||
id_string = "id: #{u.id.to_s.ljust(max_id_length + space)}"
|
||||
name_string = "name: #{u.name.ljust(max_name_length + space)}"
|
||||
nickname_string = "nickname: #{u.nickname.ljust(max_nickname_length + space)}"
|
||||
puts " | #{id_string}| #{name_string}| #{nickname_string}"
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
usernames = [WpUser.new(wpscan_options.username, -1, 'empty')]
|
||||
end
|
||||
|
||||
# Start the brute forcer
|
||||
bruteforce = true
|
||||
if wpscan_options.wordlist
|
||||
if wp_target.has_login_protection?
|
||||
|
||||
protection_plugin = wp_target.login_protection_plugin()
|
||||
|
||||
puts
|
||||
puts "The plugin #{protection_plugin.name} has been detected. It might record the IP and timestamp of every failed login. Not a good idea for brute forcing !"
|
||||
puts '[?] Do you want to start the brute force anyway ? [y/n]'
|
||||
|
||||
bruteforce = false if Readline.readline !~ /^y/i
|
||||
end
|
||||
|
||||
if bruteforce
|
||||
puts
|
||||
puts green('[+]') + ' Starting the password brute forcer'
|
||||
puts
|
||||
wp_target.brute_force(usernames, wpscan_options.wordlist, {show_progression: true})
|
||||
else
|
||||
puts
|
||||
puts 'Brute forcing aborted'
|
||||
end
|
||||
end
|
||||
|
||||
stop_time = Time.now
|
||||
puts
|
||||
puts green("[+] Finished at #{stop_time.asctime}")
|
||||
elapsed = stop_time - start_time
|
||||
puts green("[+] Elapsed time: #{Time.at(elapsed).utc.strftime('%H:%M:%S')}")
|
||||
exit() # must exit!
|
||||
rescue => e
|
||||
puts red("[ERROR] #{e.message}")
|
||||
puts red('Trace :')
|
||||
puts red(e.backtrace.join("\n"))
|
||||
end
|
||||
end
|
||||
382
wpscan.rb
382
wpscan.rb
@@ -19,385 +19,9 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#++
|
||||
|
||||
|
||||
$: << '.'
|
||||
require File.dirname(__FILE__) + '/lib/wpscan/wpscan_helper'
|
||||
require File.dirname(__FILE__) + '/main'
|
||||
|
||||
def output_vulnerabilities(vulns)
|
||||
vulns.each do |vulnerability|
|
||||
puts
|
||||
puts ' | ' + red("* Title: #{vulnerability.title}")
|
||||
vulnerability.references.each do |r|
|
||||
puts ' | ' + red("* Reference: #{r}")
|
||||
end
|
||||
vulnerability.metasploit_modules.each do |m|
|
||||
puts ' | ' + red("* Metasploit module: #{get_metasploit_url(m)}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def output_item_details(item)
|
||||
puts
|
||||
puts " | Name: #{item}" #this will also output the version number if detected
|
||||
puts " | Location: #{item.get_url_without_filename}"
|
||||
puts " | WordPress: #{item.wp_org_url}" if item.wp_org_item?
|
||||
puts ' | Directory listing enabled: Yes' if item.directory_listing?
|
||||
puts " | Readme: #{item.readme_url}" if item.has_readme?
|
||||
puts " | Changelog: #{item.changelog_url}" if item.has_changelog?
|
||||
|
||||
output_vulnerabilities(item.vulnerabilities)
|
||||
|
||||
if item.error_log?
|
||||
puts ' | ' + red('[!]') + " A WordPress error_log file has been found : #{item.error_log_url}"
|
||||
end
|
||||
end
|
||||
|
||||
# delete old logfile, check if it is a symlink first.
|
||||
File.delete(LOG_FILE) if File.exist?(LOG_FILE) and !File.symlink?(LOG_FILE)
|
||||
|
||||
banner()
|
||||
|
||||
begin
|
||||
wpscan_options = WpscanOptions.load_from_arguments
|
||||
|
||||
unless wpscan_options.has_options?
|
||||
raise "No argument supplied\n#{usage()}"
|
||||
end
|
||||
|
||||
if wpscan_options.help
|
||||
help()
|
||||
usage()
|
||||
exit
|
||||
end
|
||||
|
||||
# Check for updates
|
||||
if wpscan_options.update
|
||||
if !@updater.nil?
|
||||
if @updater.has_local_changes?
|
||||
puts "#{red('[!]')} Local file changes detected, an update will override local changes, do you want to continue updating? [y/n]"
|
||||
Readline.readline =~ /^y/i ? @updater.reset_head : raise('Update aborted')
|
||||
end
|
||||
puts @updater.update()
|
||||
else
|
||||
puts 'Svn / Git not installed, or wpscan has not been installed with one of them.'
|
||||
puts 'Update aborted'
|
||||
end
|
||||
exit(1)
|
||||
end
|
||||
|
||||
wp_target = WpTarget.new(wpscan_options.url, wpscan_options.to_h)
|
||||
|
||||
# Remote website up?
|
||||
unless wp_target.online?
|
||||
raise "The WordPress URL supplied '#{wp_target.uri}' seems to be down."
|
||||
end
|
||||
|
||||
if wpscan_options.proxy
|
||||
proxy_response = Browser.instance.get(wp_target.url)
|
||||
|
||||
unless WpTarget::valid_response_codes.include?(proxy_response.code)
|
||||
raise "Proxy Error :\r\n#{proxy_response.headers}"
|
||||
end
|
||||
end
|
||||
|
||||
redirection = wp_target.redirection
|
||||
if redirection
|
||||
if wpscan_options.follow_redirection
|
||||
puts "Following redirection #{redirection}"
|
||||
puts
|
||||
else
|
||||
puts "The remote host tried to redirect us to #{redirection}"
|
||||
puts 'Do you want follow the redirection ? [y/n]'
|
||||
end
|
||||
|
||||
if wpscan_options.follow_redirection or Readline.readline =~ /^y/i
|
||||
wpscan_options.url = redirection
|
||||
wp_target = WpTarget.new(redirection, wpscan_options.to_h)
|
||||
else
|
||||
puts 'Scan aborted'
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
if wp_target.has_basic_auth? && wpscan_options.basic_auth.nil?
|
||||
raise 'Basic authentication is required, please provide it with --basic-auth <login:password>'
|
||||
end
|
||||
|
||||
# Remote website is wordpress?
|
||||
unless wpscan_options.force
|
||||
unless wp_target.wordpress?
|
||||
raise 'The remote website is up, but does not seem to be running WordPress.'
|
||||
end
|
||||
end
|
||||
|
||||
unless wp_target.wp_content_dir
|
||||
raise 'The wp_content_dir has not been found, please supply it with --wp-content-dir'
|
||||
end
|
||||
|
||||
unless wp_target.wp_plugins_dir_exists?
|
||||
puts "The plugins directory '#{wp_target.wp_plugins_dir}' does not exist."
|
||||
puts 'You can specify one per command line option (don\'t forget to include the wp-content directory if needed)'
|
||||
puts 'Continue? [y/n]'
|
||||
unless Readline.readline =~ /^y/i
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
# Output runtime data
|
||||
start_time = Time.now
|
||||
puts "| URL: #{wp_target.url}"
|
||||
puts "| Started on #{start_time.asctime}"
|
||||
puts
|
||||
|
||||
if wp_target.has_robots?
|
||||
puts green('[+]') + " robots.txt available under '#{wp_target.robots_url}'"
|
||||
end
|
||||
|
||||
if wp_target.has_readme?
|
||||
puts red('[!]') + " The WordPress '#{wp_target.readme_url}' file exists"
|
||||
end
|
||||
|
||||
if wp_target.has_full_path_disclosure?
|
||||
puts red('[!]') + " Full Path Disclosure (FPD) in '#{wp_target.full_path_disclosure_url}'"
|
||||
end
|
||||
|
||||
if wp_target.has_debug_log?
|
||||
puts red('[!]') + " Debug log file found : #{wp_target.debug_log_url}"
|
||||
end
|
||||
|
||||
wp_target.config_backup.each do |file_url|
|
||||
puts red("[!] A wp-config.php backup file has been found '#{file_url}'")
|
||||
end
|
||||
|
||||
if wp_target.search_replace_db_2_exists?
|
||||
puts red("[!] searchreplacedb2.php has been found '#{wp_target.search_replace_db_2_url}'")
|
||||
end
|
||||
|
||||
if wp_target.is_multisite?
|
||||
puts green('[+]') + ' This site seems to be a multisite (http://codex.wordpress.org/Glossary#Multisite)'
|
||||
end
|
||||
|
||||
if wp_target.registration_enabled?
|
||||
puts green('[+]') + ' User registration is enabled'
|
||||
end
|
||||
|
||||
if wp_target.has_xml_rpc?
|
||||
puts green('[+]') + " XML-RPC Interface available under #{wp_target.xml_rpc_url}"
|
||||
end
|
||||
|
||||
if wp_target.has_malwares?
|
||||
malwares = wp_target.malwares
|
||||
puts red('[!]') + " #{malwares.size} malware(s) found :"
|
||||
|
||||
malwares.each do |malware_url|
|
||||
puts
|
||||
puts ' | ' + red("#{malware_url}")
|
||||
end
|
||||
puts
|
||||
end
|
||||
|
||||
wp_version = wp_target.version
|
||||
if wp_version
|
||||
puts green('[+]') + " WordPress version #{wp_version.number} identified from #{wp_version.discovery_method}"
|
||||
|
||||
version_vulnerabilities = wp_version.vulnerabilities
|
||||
|
||||
unless version_vulnerabilities.empty?
|
||||
puts
|
||||
puts red('[!]') + " We have identified #{version_vulnerabilities.size} vulnerabilities from the version number :"
|
||||
output_vulnerabilities(version_vulnerabilities)
|
||||
end
|
||||
end
|
||||
|
||||
wp_theme = wp_target.theme
|
||||
if wp_theme
|
||||
puts
|
||||
# Theme version is handled in wp_item.to_s
|
||||
puts green('[+]') + " The WordPress theme in use is #{wp_theme}"
|
||||
output_item_details(wp_theme)
|
||||
end
|
||||
|
||||
if wpscan_options.enumerate_plugins == nil and wpscan_options.enumerate_only_vulnerable_plugins == nil
|
||||
puts
|
||||
puts green('[+]') + ' Enumerating plugins from passive detection ... '
|
||||
|
||||
plugins = wp_target.plugins_from_passive_detection(base_url: wp_target.uri, wp_content_dir: wp_target.wp_content_dir)
|
||||
if !plugins.empty?
|
||||
puts "#{plugins.size} plugins found :"
|
||||
|
||||
plugins.each do |plugin|
|
||||
output_item_details(plugin)
|
||||
end
|
||||
else
|
||||
puts 'No plugins found :('
|
||||
end
|
||||
end
|
||||
|
||||
# Enumerate the installed plugins
|
||||
if wpscan_options.enumerate_plugins or wpscan_options.enumerate_only_vulnerable_plugins or wpscan_options.enumerate_all_plugins
|
||||
puts
|
||||
puts green('[+]') + " Enumerating installed plugins #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_plugins} ..."
|
||||
puts
|
||||
|
||||
options = {
|
||||
base_url: wp_target.uri,
|
||||
only_vulnerable_ones: wpscan_options.enumerate_only_vulnerable_plugins || false,
|
||||
show_progression: true,
|
||||
wp_content_dir: wp_target.wp_content_dir,
|
||||
error_404_hash: wp_target.error_404_hash,
|
||||
homepage_hash: wp_target.homepage_hash,
|
||||
wp_plugins_dir: wp_target.wp_plugins_dir,
|
||||
full: wpscan_options.enumerate_all_plugins,
|
||||
exclude_content_based: wpscan_options.exclude_content_based
|
||||
}
|
||||
|
||||
plugins = wp_target.plugins_from_aggressive_detection(options)
|
||||
if !plugins.empty?
|
||||
puts
|
||||
puts
|
||||
puts green('[+]') + " We found #{plugins.size.to_s} plugins:"
|
||||
|
||||
plugins.each do |plugin|
|
||||
output_item_details(plugin)
|
||||
end
|
||||
else
|
||||
puts
|
||||
puts 'No plugins found :('
|
||||
end
|
||||
end
|
||||
|
||||
# Enumerate installed themes
|
||||
if wpscan_options.enumerate_themes or wpscan_options.enumerate_only_vulnerable_themes or wpscan_options.enumerate_all_themes
|
||||
puts
|
||||
puts green('[+]') + " Enumerating installed themes #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_themes} ..."
|
||||
puts
|
||||
|
||||
options = {
|
||||
base_url: wp_target.uri,
|
||||
only_vulnerable_ones: wpscan_options.enumerate_only_vulnerable_themes || false,
|
||||
show_progression: true,
|
||||
wp_content_dir: wp_target.wp_content_dir,
|
||||
error_404_hash: wp_target.error_404_hash,
|
||||
homepage_hash: wp_target.homepage_hash,
|
||||
full: wpscan_options.enumerate_all_themes,
|
||||
exclude_content_based: wpscan_options.exclude_content_based
|
||||
}
|
||||
|
||||
themes = wp_target.themes_from_aggressive_detection(options)
|
||||
if !themes.empty?
|
||||
puts
|
||||
puts
|
||||
puts green('[+]') + " We found #{themes.size.to_s} themes:"
|
||||
|
||||
themes.each do |theme|
|
||||
output_item_details(theme)
|
||||
end
|
||||
else
|
||||
puts
|
||||
puts 'No themes found :('
|
||||
end
|
||||
end
|
||||
|
||||
if wpscan_options.enumerate_timthumbs
|
||||
puts
|
||||
puts green('[+]') + ' Enumerating timthumb files ...'
|
||||
puts
|
||||
|
||||
options = {
|
||||
base_url: wp_target.uri,
|
||||
show_progression: true,
|
||||
wp_content_dir: wp_target.wp_content_dir,
|
||||
error_404_hash: wp_target.error_404_hash,
|
||||
homepage_hash: wp_target.homepage_hash,
|
||||
exclude_content_based: wpscan_options.exclude_content_based
|
||||
}
|
||||
|
||||
theme_name = wp_theme ? wp_theme.name : nil
|
||||
if wp_target.has_timthumbs?(theme_name, options)
|
||||
timthumbs = wp_target.timthumbs
|
||||
|
||||
puts
|
||||
puts green('[+]') + " We found #{timthumbs.size.to_s} timthumb file/s :"
|
||||
puts
|
||||
|
||||
timthumbs.each do |t|
|
||||
puts ' | ' + red('[!]') + " #{t.get_full_url.to_s}"
|
||||
end
|
||||
puts
|
||||
puts red(' * Reference: http://www.exploit-db.com/exploits/17602/')
|
||||
else
|
||||
puts
|
||||
puts 'No timthumb files found :('
|
||||
end
|
||||
end
|
||||
|
||||
# If we haven't been supplied a username, enumerate them...
|
||||
if !wpscan_options.username and wpscan_options.wordlist or wpscan_options.enumerate_usernames
|
||||
puts
|
||||
puts green('[+]') + ' Enumerating usernames ...'
|
||||
|
||||
usernames = wp_target.usernames(range: wpscan_options.enumerate_usernames_range)
|
||||
|
||||
if usernames.empty?
|
||||
puts
|
||||
puts 'We did not enumerate any usernames :('
|
||||
puts 'Try supplying your own username with the --username option'
|
||||
puts
|
||||
exit(1)
|
||||
else
|
||||
puts
|
||||
puts green('[+]') + " We found the following #{usernames.length.to_s} username/s :"
|
||||
puts
|
||||
|
||||
max_id_length = usernames.sort { |a, b| a.id.to_s.length <=> b.id.to_s.length }.last.id.to_s.length
|
||||
max_name_length = usernames.sort { |a, b| a.name.length <=> b.name.length }.last.name.length
|
||||
max_nickname_length = usernames.sort { |a, b| a.nickname.length <=> b.nickname.length }.last.nickname.length
|
||||
|
||||
space = 1
|
||||
usernames.each do |u|
|
||||
id_string = "id: #{u.id.to_s.ljust(max_id_length + space)}"
|
||||
name_string = "name: #{u.name.ljust(max_name_length + space)}"
|
||||
nickname_string = "nickname: #{u.nickname.ljust(max_nickname_length + space)}"
|
||||
puts " | #{id_string}| #{name_string}| #{nickname_string}"
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
usernames = [WpUser.new(wpscan_options.username, -1, 'empty')]
|
||||
end
|
||||
|
||||
# Start the brute forcer
|
||||
bruteforce = true
|
||||
if wpscan_options.wordlist
|
||||
if wp_target.has_login_protection?
|
||||
|
||||
protection_plugin = wp_target.login_protection_plugin()
|
||||
|
||||
puts
|
||||
puts "The plugin #{protection_plugin.name} has been detected. It might record the IP and timestamp of every failed login. Not a good idea for brute forcing !"
|
||||
puts '[?] Do you want to start the brute force anyway ? [y/n]'
|
||||
|
||||
bruteforce = false if Readline.readline !~ /^y/i
|
||||
end
|
||||
|
||||
if bruteforce
|
||||
puts
|
||||
puts green('[+]') + ' Starting the password brute forcer'
|
||||
puts
|
||||
wp_target.brute_force(usernames, wpscan_options.wordlist, {show_progression: true})
|
||||
else
|
||||
puts
|
||||
puts 'Brute forcing aborted'
|
||||
end
|
||||
end
|
||||
|
||||
stop_time = Time.now
|
||||
puts
|
||||
puts green("[+] Finished at #{stop_time.asctime}")
|
||||
elapsed = stop_time - start_time
|
||||
puts green("[+] Elapsed time: #{Time.at(elapsed).utc.strftime('%H:%M:%S')}")
|
||||
exit() # must exit!
|
||||
rescue => e
|
||||
puts red("[ERROR] #{e.message}")
|
||||
puts red('Trace :')
|
||||
puts red(e.backtrace.join("\n"))
|
||||
end
|
||||
main()
|
||||
Reference in New Issue
Block a user