Files
wpscan/wpscan.rb

310 lines
9.0 KiB
Ruby
Executable File

#!/usr/bin/env ruby
#
# WPScan - WordPress Security Scanner
# Copyright (C) 2011 Ryan Dewhurst AKA ethicalhack3r
#
# 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/>.
#
# ryandewhurst at gmail
#
$: << '.'
require File.dirname(__FILE__) +'/lib/wpscan/wpscan_helper'
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()
exit
end
# Check for updates
if wpscan_options.update
unless @updater.nil?
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.is_online?
raise "The WordPress URL supplied '#{wp_target.uri}' seems to be down."
end
if redirection = wp_target.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
# Remote website is wordpress?
unless wpscan_options.force
unless wp_target.is_wordpress?
raise "The remote website is up, but does not seem to be running WordPress."
end
end
if wp_content_dir = wp_target.wp_content_dir()
Browser.instance.variables_to_replace_in_url = {"$wp-content$" => wp_content_dir, "$wp-plugins$" => wp_target.wp_plugins_dir()}
else
raise "The wp_content_dir has not been found, please supply it with --wp-content-dir"
end
# Output runtime data
puts "| URL: #{wp_target.url}"
puts "| Started on #{Time.now.asctime}"
puts
# Can we identify the theme name?
if wp_theme = wp_target.theme
theme_version = wp_theme.version
puts "[!] The WordPress theme in use is #{wp_theme}"
theme_vulnerabilities = wp_theme.vulnerabilities
unless theme_vulnerabilities.empty?
puts "[+] We have identified #{theme_vulnerabilities.size} vulnerabilities for this theme :"
theme_vulnerabilities.each do |vulnerability|
puts
puts " | * Title: " + vulnerability.title
puts " | * Reference: " + vulnerability.reference
end
puts
end
end
# Is the readme.html file there?
if wp_target.has_readme?
puts "[!] The WordPress '#{wp_target.readme_url}' file exists"
end
# Full Path Disclosure (FPD)?
if wp_target.has_full_path_disclosure?
puts "[!] Full Path Disclosure (FPD) in '#{wp_target.full_path_disclosure_url}'"
end
# Is the wp-config.php file backed up?
wp_target.config_backup.each do |file_url|
puts "[!] A wp-config.php backup file has been found '#{file_url}'"
end
# Checking for malwares
if wp_target.has_malwares?
malwares = wp_target.malwares
puts "[!] #{malwares.size} malware(s) found :"
malwares.each do |malware_url|
puts
puts " | " + malware_url
end
puts
end
# Checking the version...
if wp_version = wp_target.version
puts "[!] WordPress version #{wp_version.number} identified from #{wp_version.discovery_method}"
# Are there any vulnerabilities associated with this version?
version_vulnerabilities = wp_version.vulnerabilities
unless version_vulnerabilities.empty?
puts
puts "[+] We have identified #{version_vulnerabilities.size} vulnerabilities from the version number :"
version_vulnerabilities.each do |vulnerability|
puts
puts " | * Title: " + vulnerability.title
puts " | * Reference: " + vulnerability.reference
end
end
end
# Plugins from passive detection
puts
print "[+] Enumerating plugins from passive detection ... "
plugins = wp_target.plugins_from_passive_detection
unless plugins.empty?
print "#{plugins.size} found :\n"
plugins.each do |plugin|
puts
puts " | Name: " + plugin.name
puts " | Location: " + plugin.location_url.gsub("$wp-plugins$", wp_target.wp_plugins_dir()) #Hotfix
plugin.vulnerabilities.each do |vulnerability|
puts " |"
puts " | [!] " + vulnerability.title
puts " | * Reference: " + vulnerability.reference
end
end
else
print "No plugins found :(\n"
end
# Enumerate the installed plugins
if wpscan_options.enumerate_plugins or wpscan_options.enumerate_only_vulnerable_plugins
puts
puts "[+] Enumerating installed plugins #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_plugins} ..."
puts
plugins = wp_target.plugins_from_aggressive_detection(
:only_vulnerable_ones => wpscan_options.enumerate_only_vulnerable_plugins,
:show_progress_bar => true
)
unless plugins.empty?
puts
puts
puts "[+] We found " + plugins.size.to_s + " plugins:"
plugins.each do |plugin|
puts
puts " | Name: #{plugin}" #this will also output the version number if detected
puts " | Location: " + plugin.location_url
puts " | Directory listing enabled? #{plugin.directory_listing? ? "Yes." : "No."}"
plugin.vulnerabilities.each do |vulnerability|
#vulnerability['vulnerability'][0]['uri'] == nil ? "" : uri = vulnerability['vulnerability'][0]['uri'] # uri
#vulnerability['vulnerability'][0]['postdata'] == nil ? "" : postdata = CGI.unescapeHTML(vulnerability['vulnerability'][0]['postdata']) # postdata
puts " |"
puts " | [!] " + vulnerability.title
puts " | * Reference: " + vulnerability.reference
# This has been commented out as MSF are moving from
# XML-RPC to MessagePack.
# I need to get to grips with the new way of communicating
# with MSF and implement new code.
# check if vuln is exploitable
#Exploit.new(url, type, uri, postdata.to_s, use_proxy, proxy_addr, proxy_port)
end
if plugin.error_log?
puts " | [!] A WordPress error_log file has been found : " + plugin.error_log_url
end
end
else
puts
puts "No plugins found :("
end
end
# try to find timthumb files
if wpscan_options.enumerate_timthumbs
puts
puts "[+] Enumerating timthumb files ..."
puts
if wp_target.has_timthumbs?(:theme_name => wp_theme ? wp_theme.name : nil, :show_progress_bar => true)
timthumbs = wp_target.timthumbs
puts
puts "[+] We found " + timthumbs.size.to_s + " timthumb file/s :"
puts
timthumbs.each do |file_url|
puts " | [!] " + file_url
end
puts
puts " * 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 "[+] 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 "We found the following " + usernames.length.to_s + " username/s :"
puts
usernames.each {|username| puts " " + username}
end
else
usernames = [wpscan_options.username]
end
# Start the brute forcer
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]"
if Readline.readline !~ /^y/i
bruteforce = false
end
end
if bruteforce === false
puts
puts "Brute forcing aborted"
else
puts
puts "[+] Starting the password brute forcer"
puts
wp_target.brute_force(usernames, wpscan_options.wordlist)
end
end
puts
puts '[+] Finished at ' + Time.now.asctime
exit() # must exit!
rescue => e
puts "[ERROR] #{e}"
puts "Trace : #{e.backtrace}"
end