lib/wpscan rubocopied
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -34,10 +35,10 @@ module BruteForce
|
|||||||
request_count = 0
|
request_count = 0
|
||||||
password_found = false
|
password_found = false
|
||||||
|
|
||||||
File.open(wordlist_path, "r").each do |password|
|
File.open(wordlist_path, 'r').each do |password|
|
||||||
|
|
||||||
# ignore file comments, but will miss passwords if they start with a hash...
|
# ignore file comments, but will miss passwords if they start with a hash...
|
||||||
next if password[0,1] == "#"
|
next if password[0, 1] == '#'
|
||||||
|
|
||||||
# keep a count of the amount of requests to be sent
|
# keep a count of the amount of requests to be sent
|
||||||
request_count += 1
|
request_count += 1
|
||||||
@@ -50,9 +51,9 @@ module BruteForce
|
|||||||
# the request object
|
# the request object
|
||||||
request = Browser.instance.forge_request(login_url,
|
request = Browser.instance.forge_request(login_url,
|
||||||
{
|
{
|
||||||
:method => :post,
|
method: :post,
|
||||||
:params => {:log => username, :pwd => password},
|
params: {log: username, pwd: password},
|
||||||
:cache_timeout => 0
|
cache_timeout: 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -64,20 +65,20 @@ module BruteForce
|
|||||||
if response.body =~ /login_error/i
|
if response.body =~ /login_error/i
|
||||||
puts "\nIncorrect username and/or password." if @verbose
|
puts "\nIncorrect username and/or password." if @verbose
|
||||||
elsif response.code == 302
|
elsif response.code == 302
|
||||||
puts "\n " + green("[SUCCESS]") + " Username : #{username} Password : #{password}\n" if show_progression
|
puts "\n " + green('[SUCCESS]') + " Username : #{username} Password : #{password}\n" if show_progression
|
||||||
found << { :name => username, :password => password }
|
found << { name: username, password: password }
|
||||||
password_found = true
|
password_found = true
|
||||||
elsif response.timed_out?
|
elsif response.timed_out?
|
||||||
puts red("ERROR:") + " Request timed out." if show_progression
|
puts red('ERROR:') + ' Request timed out.' if show_progression
|
||||||
elsif response.code == 0
|
elsif response.code == 0
|
||||||
puts red("ERROR:") + " No response from remote server. WAF/IPS?" if show_progression
|
puts red('ERROR:') + ' No response from remote server. WAF/IPS?' if show_progression
|
||||||
# code is a fixnum, needs a string for regex
|
# code is a fixnum, needs a string for regex
|
||||||
elsif response.code.to_s =~ /^50/
|
elsif response.code.to_s =~ /^50/
|
||||||
puts red("ERROR:") + " Server error, try reducing the number of threads." if show_progression
|
puts red('ERROR:') + ' Server error, try reducing the number of threads.' if show_progression
|
||||||
else
|
else
|
||||||
puts "\n" + red("ERROR:") + " We recieved an unknown response for #{password}..." if show_progression
|
puts "\n" + red('ERROR:') + " We recieved an unknown response for #{password}..." if show_progression
|
||||||
|
|
||||||
# ugly method to get the coverage :/ (otherwise some output is present in the rspec)
|
# HACK to get the coverage :/ (otherwise some output is present in the rspec)
|
||||||
puts red("Code: #{response.code.to_s}") if @verbose
|
puts red("Code: #{response.code.to_s}") if @verbose
|
||||||
puts red("Body: #{response.body}") if @verbose
|
puts red("Body: #{response.body}") if @verbose
|
||||||
puts if @verbose
|
puts if @verbose
|
||||||
@@ -116,7 +117,7 @@ module BruteForce
|
|||||||
# wordlists, although bareable.
|
# wordlists, although bareable.
|
||||||
def self.lines_in_file(file_path)
|
def self.lines_in_file(file_path)
|
||||||
lines = 0
|
lines = 0
|
||||||
File.open(file_path, 'r').each { || lines += 1 }
|
File.open(file_path, 'r').each { |_| lines += 1 }
|
||||||
lines
|
lines
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -17,7 +18,10 @@
|
|||||||
#++
|
#++
|
||||||
|
|
||||||
module Malwares
|
module Malwares
|
||||||
# Used as cache : nil => malwares not checked, [] => no malwares, otherwise array of malwares url found
|
# Used as cache :
|
||||||
|
# nil => malwares not checked,
|
||||||
|
# [] => no malwares,
|
||||||
|
# otherwise array of malwares url found
|
||||||
@malwares = nil
|
@malwares = nil
|
||||||
|
|
||||||
def has_malwares?(malwares_file_path = nil)
|
def has_malwares?(malwares_file_path = nil)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -34,7 +35,7 @@ module WebSite
|
|||||||
|
|
||||||
response = Browser.instance.get(
|
response = Browser.instance.get(
|
||||||
login_url(),
|
login_url(),
|
||||||
{:follow_location => true, :max_redirects => 2}
|
{ follow_location: true, max_redirects: 2 }
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.body =~ %r{WordPress}i
|
if response.body =~ %r{WordPress}i
|
||||||
@@ -42,7 +43,7 @@ module WebSite
|
|||||||
else
|
else
|
||||||
response = Browser.instance.get(
|
response = Browser.instance.get(
|
||||||
xml_rpc_url,
|
xml_rpc_url,
|
||||||
{:follow_location => true, :max_redirects => 2}
|
{ follow_location: true, max_redirects: 2 }
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.body =~ %r{XML-RPC server accepts POST requests only}i
|
if response.body =~ %r{XML-RPC server accepts POST requests only}i
|
||||||
@@ -60,7 +61,7 @@ module WebSite
|
|||||||
def xml_rpc_url
|
def xml_rpc_url
|
||||||
unless @xmlrpc_url
|
unless @xmlrpc_url
|
||||||
headers = Browser.instance.get(@uri.to_s).headers_hash
|
headers = Browser.instance.get(@uri.to_s).headers_hash
|
||||||
value = headers["x-pingback"]
|
value = headers['x-pingback']
|
||||||
if value.nil? or value.empty?
|
if value.nil? or value.empty?
|
||||||
@xmlrpc_url = nil
|
@xmlrpc_url = nil
|
||||||
else
|
else
|
||||||
@@ -105,7 +106,7 @@ module WebSite
|
|||||||
# Return the MD5 hash of a 404 page
|
# Return the MD5 hash of a 404 page
|
||||||
def error_404_hash
|
def error_404_hash
|
||||||
unless @error_404_hash
|
unless @error_404_hash
|
||||||
non_existant_page = Digest::MD5.hexdigest(rand(9999999999).to_s) + ".html"
|
non_existant_page = Digest::MD5.hexdigest(rand(999_999_999).to_s) + '.html'
|
||||||
@error_404_hash = WebSite.page_hash(@uri.merge(non_existant_page).to_s)
|
@error_404_hash = WebSite.page_hash(@uri.merge(non_existant_page).to_s)
|
||||||
end
|
end
|
||||||
@error_404_hash
|
@error_404_hash
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -25,6 +26,6 @@ module WpFullPathDisclosure
|
|||||||
end
|
end
|
||||||
|
|
||||||
def full_path_disclosure_url
|
def full_path_disclosure_url
|
||||||
@uri.merge("wp-includes/rss-functions.php").to_s
|
@uri.merge('wp-includes/rss-functions.php').to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -37,10 +38,10 @@ module WpLoginProtection
|
|||||||
plugin_name = symbol_to_call[LOGIN_PROTECTION_METHOD_PATTERN, 1].gsub('_', '-')
|
plugin_name = symbol_to_call[LOGIN_PROTECTION_METHOD_PATTERN, 1].gsub('_', '-')
|
||||||
|
|
||||||
return @login_protection_plugin = WpPlugin.new(
|
return @login_protection_plugin = WpPlugin.new(
|
||||||
:name => plugin_name,
|
name: plugin_name,
|
||||||
:base_url => @uri,
|
base_url: @uri,
|
||||||
:path => "/plugins/#{plugin_name}/",
|
path: "/plugins/#{plugin_name}/",
|
||||||
:wp_content_dir => @wp_content_dir
|
wp_content_dir: @wp_content_dir
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -67,10 +68,10 @@ module WpLoginProtection
|
|||||||
end
|
end
|
||||||
|
|
||||||
def better_wp_security_url
|
def better_wp_security_url
|
||||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||||
:base_url => @uri,
|
base_url: @uri,
|
||||||
:path => "/plugins/better-wp-security/",
|
path: '/plugins/better-wp-security/',
|
||||||
:name => "better-wp-security"
|
name: 'better-wp-security'
|
||||||
).get_url_without_filename
|
).get_url_without_filename
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -80,10 +81,10 @@ module WpLoginProtection
|
|||||||
end
|
end
|
||||||
|
|
||||||
def simple_login_lockdown_url
|
def simple_login_lockdown_url
|
||||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||||
:base_url => @uri,
|
base_url: @uri,
|
||||||
:path => "/plugins/simple-login-lockdown/",
|
path: '/plugins/simple-login-lockdown/',
|
||||||
:name => "simple-login-lockdown"
|
name: 'simple-login-lockdown'
|
||||||
).get_url_without_filename
|
).get_url_without_filename
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -93,10 +94,10 @@ module WpLoginProtection
|
|||||||
end
|
end
|
||||||
|
|
||||||
def login_security_solution_url
|
def login_security_solution_url
|
||||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||||
:base_url => @uri,
|
base_url: @uri,
|
||||||
:path => "/plugins/login-security-solution/",
|
path: '/plugins/login-security-solution/',
|
||||||
:name => "login-security-solution"
|
name: 'login-security-solution'
|
||||||
).get_url_without_filename
|
).get_url_without_filename
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -106,10 +107,10 @@ module WpLoginProtection
|
|||||||
end
|
end
|
||||||
|
|
||||||
def limit_login_attempts_url
|
def limit_login_attempts_url
|
||||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||||
:base_url => @uri,
|
base_url: @uri,
|
||||||
:path => "/plugins/limit-login-attempts/",
|
path: '/plugins/limit-login-attempts/',
|
||||||
:name => "limit-login-attempts"
|
name: 'limit-login-attempts'
|
||||||
).get_url_without_filename
|
).get_url_without_filename
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -119,10 +120,10 @@ module WpLoginProtection
|
|||||||
end
|
end
|
||||||
|
|
||||||
def bluetrait_event_viewer_url
|
def bluetrait_event_viewer_url
|
||||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||||
:base_url => @uri,
|
base_url: @uri,
|
||||||
:path => "/plugins/bluetrait-event-viewer/",
|
path: '/plugins/bluetrait-event-viewer/',
|
||||||
:name => "bluetrait-event-viewer"
|
name: 'bluetrait-event-viewer'
|
||||||
).get_url_without_filename
|
).get_url_without_filename
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -22,22 +23,24 @@ module WpPlugins
|
|||||||
#
|
#
|
||||||
# return array of WpPlugin
|
# return array of WpPlugin
|
||||||
def plugins_from_aggressive_detection(options)
|
def plugins_from_aggressive_detection(options)
|
||||||
|
if options[:vulns_file].nil? or options[:vulns_file] == ''
|
||||||
|
options[:vulns_file] = PLUGINS_VULNS_FILE
|
||||||
|
end
|
||||||
|
|
||||||
options[:file] = options[:file] || (options[:full] ? PLUGINS_FULL_FILE : PLUGINS_FILE)
|
options[:file] = options[:file] || (options[:full] ? PLUGINS_FULL_FILE : PLUGINS_FILE)
|
||||||
options[:vulns_file] = (options[:vulns_file] != nil and options[:vulns_file] != "") ?
|
|
||||||
options[:vulns_file] : PLUGINS_VULNS_FILE
|
|
||||||
options[:vulns_xpath] = "//plugin[@name='#{@name}']/vulnerability"
|
options[:vulns_xpath] = "//plugin[@name='#{@name}']/vulnerability"
|
||||||
options[:vulns_xpath_2] = "//plugin"
|
options[:vulns_xpath_2] = '//plugin'
|
||||||
options[:type] = "plugins"
|
options[:type] = 'plugins'
|
||||||
result = WpDetector.aggressive_detection(options)
|
result = WpDetector.aggressive_detection(options)
|
||||||
plugins = []
|
plugins = []
|
||||||
result.each do |r|
|
result.each do |r|
|
||||||
plugins << WpPlugin.new(
|
plugins << WpPlugin.new(
|
||||||
:base_url => r.base_url,
|
base_url: r.base_url,
|
||||||
:path => r.path,
|
path: r.path,
|
||||||
:wp_content_dir => r.wp_content_dir,
|
wp_content_dir: r.wp_content_dir,
|
||||||
:name => r.name,
|
name: r.name,
|
||||||
:type => "plugins",
|
type: 'plugins',
|
||||||
:wp_plugins_dir => r.wp_plugins_dir
|
wp_plugins_dir: r.wp_plugins_dir
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
plugins.sort_by { |p| p.name }
|
plugins.sort_by { |p| p.name }
|
||||||
@@ -51,16 +54,16 @@ module WpPlugins
|
|||||||
# return array of WpPlugin
|
# return array of WpPlugin
|
||||||
def plugins_from_passive_detection(options)
|
def plugins_from_passive_detection(options)
|
||||||
plugins = []
|
plugins = []
|
||||||
temp = WpDetector.passive_detection(options[:base_url], "plugins", options[:wp_content_dir])
|
temp = WpDetector.passive_detection(options[:base_url], 'plugins', options[:wp_content_dir])
|
||||||
|
|
||||||
temp.each do |item|
|
temp.each do |item|
|
||||||
plugins << WpPlugin.new(
|
plugins << WpPlugin.new(
|
||||||
:base_url => item.base_url,
|
base_url: item.base_url,
|
||||||
:name => item.name,
|
name: item.name,
|
||||||
:path => item.path,
|
path: item.path,
|
||||||
:wp_content_dir => options[:wp_content_dir],
|
wp_content_dir: options[:wp_content_dir],
|
||||||
:type => "plugins",
|
type: 'plugins',
|
||||||
:wp_plugins_dir => options[:wp_plugins_dir]
|
wp_plugins_dir: options[:wp_plugins_dir]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
plugins.sort_by { |p| p.name }
|
plugins.sort_by { |p| p.name }
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -31,6 +32,6 @@ module WpReadme
|
|||||||
end
|
end
|
||||||
|
|
||||||
def readme_url
|
def readme_url
|
||||||
@uri.merge("readme.html").to_s
|
@uri.merge('readme.html').to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -19,20 +20,22 @@
|
|||||||
module WpThemes
|
module WpThemes
|
||||||
|
|
||||||
def themes_from_aggressive_detection(options)
|
def themes_from_aggressive_detection(options)
|
||||||
|
if options[:vulns_file].nil? or options[:vulns_file] == ''
|
||||||
|
options[:vulns_file] = THEMES_VULNS_FILE
|
||||||
|
end
|
||||||
|
|
||||||
options[:file] = options[:file] || (options[:full] ? THEMES_FULL_FILE : THEMES_FILE)
|
options[:file] = options[:file] || (options[:full] ? THEMES_FULL_FILE : THEMES_FILE)
|
||||||
options[:vulns_file] = (options[:vulns_file] != nil and options[:vulns_file] != "") ?
|
|
||||||
options[:vulns_file] : THEMES_VULNS_FILE
|
|
||||||
options[:vulns_xpath] = "//theme[@name='#{@name}']/vulnerability"
|
options[:vulns_xpath] = "//theme[@name='#{@name}']/vulnerability"
|
||||||
options[:vulns_xpath_2] = "//theme"
|
options[:vulns_xpath_2] = '//theme'
|
||||||
options[:type] = "themes"
|
options[:type] = 'themes'
|
||||||
result = WpDetector.aggressive_detection(options)
|
result = WpDetector.aggressive_detection(options)
|
||||||
themes = []
|
themes = []
|
||||||
result.each do |r|
|
result.each do |r|
|
||||||
themes << WpTheme.new(
|
themes << WpTheme.new(
|
||||||
:base_url => r.base_url,
|
base_url: r.base_url,
|
||||||
:path => r.path,
|
path: r.path,
|
||||||
:wp_content_dir => r.wp_content_dir,
|
wp_content_dir: r.wp_content_dir,
|
||||||
:name => r.name
|
name: r.name
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
themes.sort_by { |t| t.name }
|
themes.sort_by { |t| t.name }
|
||||||
@@ -40,14 +43,14 @@ module WpThemes
|
|||||||
|
|
||||||
def themes_from_passive_detection(options)
|
def themes_from_passive_detection(options)
|
||||||
themes = []
|
themes = []
|
||||||
temp = WpDetector.passive_detection(options[:base_url], "themes", options[:wp_content_dir])
|
temp = WpDetector.passive_detection(options[:base_url], 'themes', options[:wp_content_dir])
|
||||||
|
|
||||||
temp.each do |item|
|
temp.each do |item|
|
||||||
themes << WpTheme.new(
|
themes << WpTheme.new(
|
||||||
:base_url => item.base_url,
|
base_url: item.base_url,
|
||||||
:name => item.name,
|
name: item.name,
|
||||||
:path => item.path,
|
path: item.path,
|
||||||
:wp_content_dir => options[:wp_content_dir]
|
wp_content_dir: options[:wp_content_dir]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
themes.sort_by { |t| t.name }
|
themes.sort_by { |t| t.name }
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -18,7 +19,10 @@
|
|||||||
|
|
||||||
module WpTimthumbs
|
module WpTimthumbs
|
||||||
|
|
||||||
# Used as cache : nil => timthumbs not checked, [] => no timthumbs, otherwise array of timthumbs url found
|
# Used as cache :
|
||||||
|
# nil => timthumbs not checked,
|
||||||
|
# [] => no timthumbs,
|
||||||
|
# otherwise array of timthumbs url found
|
||||||
@wp_timthumbs = nil
|
@wp_timthumbs = nil
|
||||||
|
|
||||||
def has_timthumbs?(theme_name, options = {})
|
def has_timthumbs?(theme_name, options = {})
|
||||||
@@ -27,12 +31,12 @@ module WpTimthumbs
|
|||||||
|
|
||||||
def timthumbs(theme_name = nil, options = {})
|
def timthumbs(theme_name = nil, options = {})
|
||||||
if @wp_timthumbs.nil?
|
if @wp_timthumbs.nil?
|
||||||
options[:type] = "timthumbs"
|
options[:type] = 'timthumbs'
|
||||||
options[:only_vulnerable_ones] = false
|
options[:only_vulnerable_ones] = false
|
||||||
options[:file] = options[:file] || DATA_DIR + "/timthumbs.txt"
|
options[:file] = options[:file] || DATA_DIR + '/timthumbs.txt'
|
||||||
options[:vulns_file] = "xxx"
|
options[:vulns_file] = 'xxx'
|
||||||
options[:vulns_xpath] = "xxx"
|
options[:vulns_xpath] = 'xxx'
|
||||||
options[:vulns_xpath_2] = "xxx"
|
options[:vulns_xpath_2] = 'xxx'
|
||||||
|
|
||||||
WpOptions.check_options(options)
|
WpOptions.check_options(options)
|
||||||
if theme_name == nil
|
if theme_name == nil
|
||||||
@@ -55,13 +59,13 @@ module WpTimthumbs
|
|||||||
scripts/timthumb.php tools/timthumb.php functions/timthumb.php
|
scripts/timthumb.php tools/timthumb.php functions/timthumb.php
|
||||||
}.each do |file|
|
}.each do |file|
|
||||||
targets << WpItem.new(
|
targets << WpItem.new(
|
||||||
:base_url => options[:base_url],
|
base_url: options[:base_url],
|
||||||
:path => "themes/#{theme_name}/#{file}",
|
path: "themes/#{theme_name}/#{file}",
|
||||||
:wp_content_dir => options[:wp_content_dir],
|
wp_content_dir: options[:wp_content_dir],
|
||||||
:name => theme_name,
|
name: theme_name,
|
||||||
:vulns_file => "XX",
|
vulns_file: 'XX',
|
||||||
:type => "timthumbs",
|
type: 'timthumbs',
|
||||||
:wp_plugins_dir => options[:wp_plugins_dir]
|
wp_plugins_dir: options[:wp_plugins_dir]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
targets
|
targets
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -58,7 +59,7 @@ module WpUsernames
|
|||||||
end
|
end
|
||||||
|
|
||||||
def get_nickname_from_url(url)
|
def get_nickname_from_url(url)
|
||||||
resp = Browser.instance.get(url, {:follow_location => true, :max_redirects => 2})
|
resp = Browser.instance.get(url, { follow_location: true, max_redirects: 2 })
|
||||||
nickname = nil
|
nickname = nil
|
||||||
if resp.code == 200
|
if resp.code == 200
|
||||||
nickname = extract_nickname_from_body(resp.body)
|
nickname = extract_nickname_from_body(resp.body)
|
||||||
@@ -80,21 +81,21 @@ module WpUsernames
|
|||||||
|
|
||||||
def remove_junk_from_nickname(usernames)
|
def remove_junk_from_nickname(usernames)
|
||||||
unless usernames.kind_of? Array
|
unless usernames.kind_of? Array
|
||||||
raise("Need an array as input")
|
raise('Need an array as input')
|
||||||
end
|
end
|
||||||
nicknames = []
|
nicknames = []
|
||||||
usernames.each do |u|
|
usernames.each do |u|
|
||||||
unless u.kind_of? WpUser
|
unless u.kind_of? WpUser
|
||||||
raise("Items must be of type WpUser")
|
raise('Items must be of type WpUser')
|
||||||
end
|
end
|
||||||
nickname = u.nickname
|
nickname = u.nickname
|
||||||
unless nickname == "empty"
|
unless nickname == 'empty'
|
||||||
nicknames << nickname
|
nicknames << nickname
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
junk = get_equal_string_end(nicknames)
|
junk = get_equal_string_end(nicknames)
|
||||||
usernames.each do |u|
|
usernames.each do |u|
|
||||||
u.nickname = u.nickname.sub(/#{Regexp.escape(junk)}$/, "")
|
u.nickname = u.nickname.sub(/#{Regexp.escape(junk)}$/, '')
|
||||||
end
|
end
|
||||||
usernames
|
usernames
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -30,10 +31,10 @@ class Vulnerable
|
|||||||
|
|
||||||
xml.xpath(@vulns_xpath).each do |node|
|
xml.xpath(@vulns_xpath).each do |node|
|
||||||
vulnerabilities << WpVulnerability.new(
|
vulnerabilities << WpVulnerability.new(
|
||||||
node.search("title").text,
|
node.search('title').text,
|
||||||
node.search("reference").map(&:text),
|
node.search('reference').map(&:text),
|
||||||
node.search("type").text,
|
node.search('type').text,
|
||||||
node.search("metasploit").map(&:text)
|
node.search('metasploit').map(&:text)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
vulnerabilities
|
vulnerabilities
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -63,12 +64,12 @@ class WpDetector
|
|||||||
|
|
||||||
names.each do |item|
|
names.each do |item|
|
||||||
items << WpItem.new(
|
items << WpItem.new(
|
||||||
:base_url => url,
|
base_url: url,
|
||||||
:name => item,
|
name: item,
|
||||||
:type => type,
|
type: type,
|
||||||
:path => "#{item}/",
|
path: "#{item}/",
|
||||||
:wp_content_dir => wp_content_dir,
|
wp_content_dir: wp_content_dir,
|
||||||
:vulns_file => ""
|
vulns_file: ''
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
items
|
items
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -54,7 +55,7 @@ class WpEnumerator
|
|||||||
targets.each do |target|
|
targets.each do |target|
|
||||||
url = target.get_full_url
|
url = target.get_full_url
|
||||||
|
|
||||||
request = enum_browser.forge_request(url, { :cache_timeout => 0, :follow_location => true })
|
request = enum_browser.forge_request(url, { cache_timeout: 0, follow_location: true })
|
||||||
request_count += 1
|
request_count += 1
|
||||||
|
|
||||||
request.on_complete do |response|
|
request.on_complete do |response|
|
||||||
@@ -102,17 +103,17 @@ class WpEnumerator
|
|||||||
|
|
||||||
unless only_vulnerable
|
unless only_vulnerable
|
||||||
# Open and parse the 'most popular' plugin list...
|
# Open and parse the 'most popular' plugin list...
|
||||||
File.open(file, "r") do |f|
|
File.open(file, 'r') do |f|
|
||||||
f.readlines.collect do |line|
|
f.readlines.collect do |line|
|
||||||
l = line.strip
|
l = line.strip
|
||||||
targets_url << WpItem.new(
|
targets_url << WpItem.new(
|
||||||
:base_url => url,
|
base_url: url,
|
||||||
:path => l,
|
path: l,
|
||||||
:wp_content_dir => wp_content_dir,
|
wp_content_dir: wp_content_dir,
|
||||||
:name => l =~ /.+\/.+/ ? File.dirname(l) : l.sub(/\/$/, ""),
|
name: l =~ /.+\/.+/ ? File.dirname(l) : l.sub(/\/$/, ''),
|
||||||
:vulns_file => vulns_file,
|
vulns_file: vulns_file,
|
||||||
:type => type,
|
type: type,
|
||||||
:wp_plugins_dir => plugins_dir
|
wp_plugins_dir: plugins_dir
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -126,15 +127,15 @@ class WpEnumerator
|
|||||||
|
|
||||||
# We check if the plugin name from the plugin_vulns_file is already in targets, otherwise we add it
|
# We check if the plugin name from the plugin_vulns_file is already in targets, otherwise we add it
|
||||||
xml.xpath(options[:vulns_xpath_2]).each do |node|
|
xml.xpath(options[:vulns_xpath_2]).each do |node|
|
||||||
name = node.attribute("name").text
|
name = node.attribute('name').text
|
||||||
targets_url << WpItem.new(
|
targets_url << WpItem.new(
|
||||||
:base_url => url,
|
base_url: url,
|
||||||
:path => name,
|
path: name,
|
||||||
:wp_content_dir => wp_content_dir,
|
wp_content_dir: wp_content_dir,
|
||||||
:name => name,
|
name: name,
|
||||||
:vulns_file => vulns_file,
|
vulns_file: vulns_file,
|
||||||
:type => type,
|
type: type,
|
||||||
:wp_plugins_dir => plugins_dir
|
wp_plugins_dir: plugins_dir
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -24,7 +25,7 @@ class WpItem < Vulnerable
|
|||||||
|
|
||||||
def initialize(options)
|
def initialize(options)
|
||||||
@type = options[:type]
|
@type = options[:type]
|
||||||
@wp_content_dir = options[:wp_content_dir] ? options[:wp_content_dir].sub(/^\//, "").sub(/\/$/, "") : "wp-content"
|
@wp_content_dir = options[:wp_content_dir] ? options[:wp_content_dir].sub(/^\//, '').sub(/\/$/, '') : 'wp-content'
|
||||||
@wp_plugins_dir = options[:wp_plugins_dir] || "#@wp_content_dir/plugins"
|
@wp_plugins_dir = options[:wp_plugins_dir] || "#@wp_content_dir/plugins"
|
||||||
@base_url = options[:base_url]
|
@base_url = options[:base_url]
|
||||||
@path = options[:path]
|
@path = options[:path]
|
||||||
@@ -32,36 +33,36 @@ class WpItem < Vulnerable
|
|||||||
@vulns_file = options[:vulns_file]
|
@vulns_file = options[:vulns_file]
|
||||||
@vulns_xpath = options[:vulns_xpath].sub(/\$name\$/, @name) unless options[:vulns_xpath] == nil
|
@vulns_xpath = options[:vulns_xpath].sub(/\$name\$/, @name) unless options[:vulns_xpath] == nil
|
||||||
|
|
||||||
raise("base_url not set") unless @base_url
|
raise('base_url not set') unless @base_url
|
||||||
raise("path not set") unless @path
|
raise('path not set') unless @path
|
||||||
raise("wp_content_dir not set") unless @wp_content_dir
|
raise('wp_content_dir not set') unless @wp_content_dir
|
||||||
raise("name not set") unless @name
|
raise('name not set') unless @name
|
||||||
raise("vulns_file not set") unless @vulns_file
|
raise('vulns_file not set') unless @vulns_file
|
||||||
raise("type not set") unless @type
|
raise('type not set') unless @type
|
||||||
end
|
end
|
||||||
|
|
||||||
# The wordpress.org plugins directory URL
|
# The wordpress.org plugins directory URL
|
||||||
# See: https://github.com/wpscanteam/wpscan/issues/100
|
# See: https://github.com/wpscanteam/wpscan/issues/100
|
||||||
def wp_org_url
|
def wp_org_url
|
||||||
case @type
|
case @type
|
||||||
when "themes"
|
when 'themes'
|
||||||
return URI("http://wordpress.org/extend/themes/").merge("#@name/")
|
return URI('http://wordpress.org/extend/themes/').merge("#@name/")
|
||||||
when "plugins"
|
when 'plugins'
|
||||||
return URI("http://wordpress.org/extend/plugins/").merge("#@name/")
|
return URI('http://wordpress.org/extend/plugins/').merge("#@name/")
|
||||||
else
|
else
|
||||||
raise("No Wordpress URL for #@type")
|
raise("No Wordpress URL for #@type")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# returns true if this theme or plugin is hosted on wordpress.org
|
# returns true if this theme or plugin is hosted on wordpress.org
|
||||||
def wp_org_item?
|
def wp_org_item?
|
||||||
case @type
|
case @type
|
||||||
when "themes"
|
when 'themes'
|
||||||
file = THEMES_FULL_FILE
|
file = THEMES_FULL_FILE
|
||||||
when "plugins"
|
when 'plugins'
|
||||||
file = PLUGINS_FULL_FILE
|
file = PLUGINS_FULL_FILE
|
||||||
else
|
else
|
||||||
raise("Unknown type #@type")
|
raise("Unknown type #@type")
|
||||||
end
|
end
|
||||||
f = File.readlines(file).grep(/^#{Regexp.escape(@name)}$/i)
|
f = File.readlines(file).grep(/^#{Regexp.escape(@name)}$/i)
|
||||||
f.empty? ? false : true
|
f.empty? ? false : true
|
||||||
@@ -69,28 +70,28 @@ class WpItem < Vulnerable
|
|||||||
|
|
||||||
def get_sub_folder
|
def get_sub_folder
|
||||||
case @type
|
case @type
|
||||||
when "themes"
|
when 'themes'
|
||||||
folder = "themes"
|
folder = 'themes'
|
||||||
when "timthumbs"
|
when 'timthumbs'
|
||||||
# not needed
|
# not needed
|
||||||
folder = nil
|
folder = nil
|
||||||
else
|
else
|
||||||
raise("unknown type #@type")
|
raise("unknown type #@type")
|
||||||
end
|
end
|
||||||
folder
|
folder
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the full url for this item
|
# Get the full url for this item
|
||||||
def get_full_url
|
def get_full_url
|
||||||
url = @base_url.to_s.end_with?("/") ? @base_url.to_s : "#@base_url/"
|
url = @base_url.to_s.end_with?('/') ? @base_url.to_s : "#@base_url/"
|
||||||
# remove first and last /
|
# remove first and last /
|
||||||
wp_content_dir = @wp_content_dir.sub(/^\//, "").sub(/\/$/, "")
|
wp_content_dir = @wp_content_dir.sub(/^\//, "").sub(/\/$/, '')
|
||||||
# remove first /
|
# remove first /
|
||||||
path = @path.sub(/^\//, "")
|
path = @path.sub(/^\//, '')
|
||||||
if type =="plugins"
|
if type == 'plugins'
|
||||||
# plugins can be outside of wp-content. wp_content_dir included in wp_plugins_dir
|
# plugins can be outside of wp-content. wp_content_dir included in wp_plugins_dir
|
||||||
ret = URI.parse(URI.encode("#{url}#@wp_plugins_dir/#{path}"))
|
ret = URI.parse(URI.encode("#{url}#@wp_plugins_dir/#{path}"))
|
||||||
elsif type == "timthumbs"
|
elsif type == 'timthumbs'
|
||||||
# timthumbs have folder in path variable
|
# timthumbs have folder in path variable
|
||||||
ret = URI.parse(URI.encode("#{url}#{wp_content_dir}/#{path}"))
|
ret = URI.parse(URI.encode("#{url}#{wp_content_dir}/#{path}"))
|
||||||
else
|
else
|
||||||
@@ -112,7 +113,7 @@ class WpItem < Vulnerable
|
|||||||
# Returns version number from readme.txt if it exists
|
# Returns version number from readme.txt if it exists
|
||||||
def version
|
def version
|
||||||
unless @version
|
unless @version
|
||||||
response = Browser.instance.get(get_full_url.merge("readme.txt").to_s)
|
response = Browser.instance.get(get_full_url.merge('readme.txt').to_s)
|
||||||
@version = response.body[%r{stable tag: #{WpVersion.version_pattern}}i, 1]
|
@version = response.body[%r{stable tag: #{WpVersion.version_pattern}}i, 1]
|
||||||
end
|
end
|
||||||
@version
|
@version
|
||||||
@@ -152,12 +153,12 @@ class WpItem < Vulnerable
|
|||||||
|
|
||||||
# Url for readme.txt
|
# Url for readme.txt
|
||||||
def readme_url
|
def readme_url
|
||||||
get_url_without_filename.merge("readme.txt")
|
get_url_without_filename.merge('readme.txt')
|
||||||
end
|
end
|
||||||
|
|
||||||
# Url for changelog.txt
|
# Url for changelog.txt
|
||||||
def changelog_url
|
def changelog_url
|
||||||
get_url_without_filename.merge("changelog.txt")
|
get_url_without_filename.merge('changelog.txt')
|
||||||
end
|
end
|
||||||
|
|
||||||
# readme.txt present?
|
# readme.txt present?
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -32,16 +33,16 @@
|
|||||||
# * +type+ - Type: plugins, themes
|
# * +type+ - Type: plugins, themes
|
||||||
class WpOptions
|
class WpOptions
|
||||||
def self.check_options(options)
|
def self.check_options(options)
|
||||||
raise("base_url must be set") unless options[:base_url] != nil and options[:base_url].to_s.length > 0
|
raise('base_url must be set') unless options[:base_url] != nil and options[:base_url].to_s.length > 0
|
||||||
raise("only_vulnerable_ones must be set") unless options[:only_vulnerable_ones] != nil
|
raise('only_vulnerable_ones must be set') unless options[:only_vulnerable_ones] != nil
|
||||||
raise("file must be set") unless options[:file] != nil and options[:file].length > 0
|
raise('file must be set') unless options[:file] != nil and options[:file].length > 0
|
||||||
raise("vulns_file must be set") unless options[:vulns_file] != nil and options[:vulns_file].length > 0
|
raise('vulns_file must be set') unless options[:vulns_file] != nil and options[:vulns_file].length > 0
|
||||||
raise("vulns_xpath must be set") unless options[:vulns_xpath] != nil and options[:vulns_xpath].length > 0
|
raise('vulns_xpath must be set') unless options[:vulns_xpath] != nil and options[:vulns_xpath].length > 0
|
||||||
raise("vulns_xpath_2 must be set") unless options[:vulns_xpath_2] != nil and options[:vulns_xpath_2].length > 0
|
raise('vulns_xpath_2 must be set') unless options[:vulns_xpath_2] != nil and options[:vulns_xpath_2].length > 0
|
||||||
raise("wp_content_dir must be set") unless options[:wp_content_dir] != nil and options[:wp_content_dir].length > 0
|
raise('wp_content_dir must be set') unless options[:wp_content_dir] != nil and options[:wp_content_dir].length > 0
|
||||||
raise("show_progression must be set") unless options[:show_progression] != nil
|
raise('show_progression must be set') unless options[:show_progression] != nil
|
||||||
raise("error_404_hash must be set") unless options[:error_404_hash] != nil and options[:error_404_hash].length > 0
|
raise('error_404_hash must be set') unless options[:error_404_hash] != nil and options[:error_404_hash].length > 0
|
||||||
raise("type must be set") unless options[:type] != nil and options[:type].length > 0
|
raise('type must be set') unless options[:type] != nil and options[:type].length > 0
|
||||||
|
|
||||||
unless options[:type] =~ /plugins/i or options[:type] =~ /themes/i or options[:type] =~ /timthumbs/i
|
unless options[:type] =~ /plugins/i or options[:type] =~ /themes/i or options[:type] =~ /timthumbs/i
|
||||||
raise("Unknown type #{options[:type]}")
|
raise("Unknown type #{options[:type]}")
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -18,11 +19,14 @@
|
|||||||
|
|
||||||
class WpPlugin < WpItem
|
class WpPlugin < WpItem
|
||||||
def initialize(options = {})
|
def initialize(options = {})
|
||||||
options[:vulns_file] = (options[:vulns_file] != nil and options[:vulns_file] != "") ?
|
if options[:vulns_file].nil? or options[:vulns_file] == ''
|
||||||
options[:vulns_file] : PLUGINS_VULNS_FILE
|
options[:vulns_file] = PLUGINS_VULNS_FILE
|
||||||
|
end
|
||||||
|
|
||||||
options[:vulns_xpath] = "//plugin[@name='$name$']/vulnerability"
|
options[:vulns_xpath] = "//plugin[@name='$name$']/vulnerability"
|
||||||
options[:vulns_xpath_2] = "//plugin"
|
options[:vulns_xpath_2] = '//plugin'
|
||||||
options[:type] = "plugins"
|
options[:type] = 'plugins'
|
||||||
|
|
||||||
super(options)
|
super(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -32,11 +36,11 @@ class WpPlugin < WpItem
|
|||||||
# however can also be found in their specific plugin dir.
|
# however can also be found in their specific plugin dir.
|
||||||
# http://www.exploit-db.com/ghdb/3714/
|
# http://www.exploit-db.com/ghdb/3714/
|
||||||
def error_log?
|
def error_log?
|
||||||
response_body = Browser.instance.get(error_log_url(), :headers => {"range" => "bytes=0-700"}).body
|
response_body = Browser.instance.get(error_log_url(), headers: {'range' => 'bytes=0-700'}).body
|
||||||
response_body[%r{PHP Fatal error}i] ? true : false
|
response_body[%r{PHP Fatal error}i] ? true : false
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_log_url
|
def error_log_url
|
||||||
get_full_url.merge("error_log").to_s
|
get_full_url.merge('error_log').to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -47,7 +48,7 @@ class WpTarget
|
|||||||
end
|
end
|
||||||
|
|
||||||
def login_url
|
def login_url
|
||||||
url = @uri.merge("wp-login.php").to_s
|
url = @uri.merge('wp-login.php').to_s
|
||||||
|
|
||||||
# Let's check if the login url is redirected (to https url for example)
|
# Let's check if the login url is redirected (to https url for example)
|
||||||
redirection = redirection(url)
|
redirection = redirection(url)
|
||||||
@@ -80,9 +81,9 @@ class WpTarget
|
|||||||
uri_path = @uri.path
|
uri_path = @uri.path
|
||||||
|
|
||||||
if index_body[/\/wp-content\/(?:themes|plugins)\//i]
|
if index_body[/\/wp-content\/(?:themes|plugins)\//i]
|
||||||
@wp_content_dir = "wp-content"
|
@wp_content_dir = 'wp-content'
|
||||||
else
|
else
|
||||||
domains_excluded = "(?:www\.)?(facebook|twitter)\.com"
|
domains_excluded = '(?:www\.)?(facebook|twitter)\.com'
|
||||||
@wp_content_dir = index_body[/(?:href|src)\s*=\s*(?:"|').+#{Regexp.escape(uri_path)}((?!#{domains_excluded})[^"']+)\/(?:themes|plugins)\/.*(?:"|')/i, 1]
|
@wp_content_dir = index_body[/(?:href|src)\s*=\s*(?:"|').+#{Regexp.escape(uri_path)}((?!#{domains_excluded})[^"']+)\/(?:themes|plugins)\/.*(?:"|')/i, 1]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -102,7 +103,7 @@ class WpTarget
|
|||||||
|
|
||||||
def has_debug_log?
|
def has_debug_log?
|
||||||
# We only get the first 700 bytes of the file to avoid loading huge file (like 2Go)
|
# We only get the first 700 bytes of the file to avoid loading huge file (like 2Go)
|
||||||
response_body = Browser.instance.get(debug_log_url(), :headers => {"range" => "bytes=0-700"}).body
|
response_body = Browser.instance.get(debug_log_url(), headers: {'range' => 'bytes=0-700'}).body
|
||||||
response_body[%r{\[[^\]]+\] PHP (?:Warning|Error|Notice):}] ? true : false
|
response_body[%r{\[[^\]]+\] PHP (?:Warning|Error|Notice):}] ? true : false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ class WpTarget
|
|||||||
# reveals databse credentials after hitting submit
|
# reveals databse credentials after hitting submit
|
||||||
# http://interconnectit.com/124/search-and-replace-for-wordpress-databases/
|
# http://interconnectit.com/124/search-and-replace-for-wordpress-databases/
|
||||||
def search_replace_db_2_url
|
def search_replace_db_2_url
|
||||||
@uri.merge("searchreplacedb2.php").to_s
|
@uri.merge('searchreplacedb2.php').to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def search_replace_db_2_exists?
|
def search_replace_db_2_exists?
|
||||||
@@ -126,7 +127,7 @@ class WpTarget
|
|||||||
def registration_enabled?
|
def registration_enabled?
|
||||||
resp = Browser.instance.get(registration_url)
|
resp = Browser.instance.get(registration_url)
|
||||||
# redirect only on non multi sites
|
# redirect only on non multi sites
|
||||||
if resp.code == 302 and resp.headers_hash["location"] =~ /wp-login\.php\?registration=disabled/i
|
if resp.code == 302 and resp.headers_hash['location'] =~ /wp-login\.php\?registration=disabled/i
|
||||||
enabled = false
|
enabled = false
|
||||||
# multi site registration form
|
# multi site registration form
|
||||||
elsif resp.code == 200 and resp.body =~ /<form id="setupform" method="post" action="[^"]*wp-signup\.php[^"]*">/i
|
elsif resp.code == 200 and resp.body =~ /<form id="setupform" method="post" action="[^"]*wp-signup\.php[^"]*">/i
|
||||||
@@ -142,18 +143,18 @@ class WpTarget
|
|||||||
end
|
end
|
||||||
|
|
||||||
def registration_url
|
def registration_url
|
||||||
is_multisite? ? @uri.merge("wp-signup.php") : @uri.merge("wp-login.php?action=register")
|
is_multisite? ? @uri.merge('wp-signup.php') : @uri.merge('wp-login.php?action=register')
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_multisite?
|
def is_multisite?
|
||||||
unless @multisite
|
unless @multisite
|
||||||
# when multi site, there is no redirection or a redirect to the site itself
|
# when multi site, there is no redirection or a redirect to the site itself
|
||||||
# otherwise redirect to wp-login.php
|
# otherwise redirect to wp-login.php
|
||||||
url = @uri.merge("wp-signup.php")
|
url = @uri.merge('wp-signup.php')
|
||||||
resp = Browser.instance.get(url)
|
resp = Browser.instance.get(url)
|
||||||
if resp.code == 302 and resp.headers_hash["location"] =~ /wp-login\.php\?action=register/
|
if resp.code == 302 and resp.headers_hash['location'] =~ /wp-login\.php\?action=register/
|
||||||
@multisite = false
|
@multisite = false
|
||||||
elsif resp.code == 302 and resp.headers_hash["location"] =~ /wp-signup\.php/
|
elsif resp.code == 302 and resp.headers_hash['location'] =~ /wp-signup\.php/
|
||||||
@multisite = true
|
@multisite = true
|
||||||
elsif resp.code == 200
|
elsif resp.code == 200
|
||||||
@multisite = true
|
@multisite = true
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -23,12 +24,15 @@ class WpTheme < WpItem
|
|||||||
attr_reader :style_url, :version
|
attr_reader :style_url, :version
|
||||||
|
|
||||||
def initialize(options = {})
|
def initialize(options = {})
|
||||||
options[:vulns_file] = (options[:vulns_file] != nil and options[:vulns_file] != "") ?
|
if options[:vulns_file].nil? or options[:vulns_file] == ''
|
||||||
options[:vulns_file] : THEMES_VULNS_FILE
|
options[:vulns_file] = THEMES_VULNS_FILE
|
||||||
|
end
|
||||||
|
|
||||||
options[:vulns_xpath] = "//theme[@name='$name$']/vulnerability"
|
options[:vulns_xpath] = "//theme[@name='$name$']/vulnerability"
|
||||||
options[:type] = "themes"
|
options[:type] = 'themes'
|
||||||
@version = options[:version]
|
@version = options[:version]
|
||||||
@style_url = options[:style_url]
|
@style_url = options[:style_url]
|
||||||
|
|
||||||
super(options)
|
super(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -58,7 +62,7 @@ class WpTheme < WpItem
|
|||||||
|
|
||||||
# Discover the wordpress theme name by parsing the css link rel
|
# Discover the wordpress theme name by parsing the css link rel
|
||||||
def self.find_from_css_link(target_uri)
|
def self.find_from_css_link(target_uri)
|
||||||
response = Browser.instance.get(target_uri.to_s, {:follow_location => true, :max_redirects => 2})
|
response = Browser.instance.get(target_uri.to_s, { follow_location: true, max_redirects: 2 })
|
||||||
|
|
||||||
matches = %r{https?://[^"']+/([^/]+)/themes/([^"']+)/style.css}i.match(response.body)
|
matches = %r{https?://[^"']+/([^/]+)/themes/([^"']+)/style.css}i.match(response.body)
|
||||||
if matches
|
if matches
|
||||||
@@ -66,11 +70,12 @@ class WpTheme < WpItem
|
|||||||
wp_content_dir = matches[1]
|
wp_content_dir = matches[1]
|
||||||
theme_name = matches[2]
|
theme_name = matches[2]
|
||||||
|
|
||||||
return new(:name => theme_name,
|
return new(
|
||||||
:style_url => style_url,
|
name: theme_name,
|
||||||
:base_url => target_uri,
|
style_url: style_url,
|
||||||
:path => theme_name,
|
base_url: target_uri,
|
||||||
:wp_content_dir => wp_content_dir
|
path: theme_name,
|
||||||
|
wp_content_dir: wp_content_dir
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -86,11 +91,12 @@ class WpTheme < WpItem
|
|||||||
woo_theme_version = matches[2]
|
woo_theme_version = matches[2]
|
||||||
woo_framework_version = matches[3] # Not used at this time
|
woo_framework_version = matches[3] # Not used at this time
|
||||||
|
|
||||||
return new(:name => woo_theme_name,
|
return new(
|
||||||
:version => woo_theme_version,
|
name: woo_theme_name,
|
||||||
:base_url => matches[0],
|
version: woo_theme_version,
|
||||||
:path => "",
|
base_url: matches[0],
|
||||||
:wp_content_dir => ""
|
path: '',
|
||||||
|
wp_content_dir: ''
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -20,7 +21,7 @@ class WpUser
|
|||||||
|
|
||||||
def name
|
def name
|
||||||
if @name.nil? or @name.to_s.strip.empty?
|
if @name.nil? or @name.to_s.strip.empty?
|
||||||
return "empty"
|
return 'empty'
|
||||||
end
|
end
|
||||||
@name
|
@name
|
||||||
end
|
end
|
||||||
@@ -31,7 +32,7 @@ class WpUser
|
|||||||
|
|
||||||
def id
|
def id
|
||||||
if @id.nil? or @id.to_s.strip.empty?
|
if @id.nil? or @id.to_s.strip.empty?
|
||||||
return "empty"
|
return 'empty'
|
||||||
end
|
end
|
||||||
@id
|
@id
|
||||||
end
|
end
|
||||||
@@ -42,7 +43,7 @@ class WpUser
|
|||||||
|
|
||||||
def nickname
|
def nickname
|
||||||
if @nickname.nil? or @nickname.to_s.strip.empty?
|
if @nickname.nil? or @nickname.to_s.strip.empty?
|
||||||
return "empty"
|
return 'empty'
|
||||||
end
|
end
|
||||||
@nickname
|
@nickname
|
||||||
end
|
end
|
||||||
@@ -57,15 +58,15 @@ class WpUser
|
|||||||
self.nickname = nickname
|
self.nickname = nickname
|
||||||
end
|
end
|
||||||
|
|
||||||
def <=>(item)
|
def <=>(other)
|
||||||
item.name <=> self.name
|
other.name <=> self.name
|
||||||
end
|
end
|
||||||
|
|
||||||
def ===(item)
|
def ===(other)
|
||||||
item.name === self.name and item.id === self.id and item.nickname === self.nickname
|
other.name === self.name and other.id === self.id and other.nickname === self.nickname
|
||||||
end
|
end
|
||||||
|
|
||||||
def eql?(item)
|
def eql?(other)
|
||||||
item.name === self.name and item.id === self.id and item.nickname === self.nickname
|
other.name === self.name and other.id === self.id and other.nickname === self.nickname
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -38,14 +39,14 @@ class WpVersion < Vulnerable
|
|||||||
# (find_from_meta_generator, find_from_rss_generator etc)
|
# (find_from_meta_generator, find_from_rss_generator etc)
|
||||||
def self.find(target_uri, wp_content_dir)
|
def self.find(target_uri, wp_content_dir)
|
||||||
options = {
|
options = {
|
||||||
:base_url => target_uri,
|
base_url: target_uri,
|
||||||
:wp_content_dir => wp_content_dir
|
wp_content_dir: wp_content_dir
|
||||||
}
|
}
|
||||||
self.methods.grep(/find_from_/).each do |method_to_call|
|
self.methods.grep(/find_from_/).each do |method_to_call|
|
||||||
version = self.send(method_to_call, options)
|
version = self.send(method_to_call, options)
|
||||||
|
|
||||||
if version
|
if version
|
||||||
return new(version, :discovery_method => method_to_call[%r{find_from_(.*)}, 1].gsub('_', ' '))
|
return new(version, discovery_method: method_to_call[%r{find_from_(.*)}, 1].gsub('_', ' '))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
nil
|
nil
|
||||||
@@ -60,7 +61,7 @@ class WpVersion < Vulnerable
|
|||||||
# that it is reinstated on upgrade.
|
# that it is reinstated on upgrade.
|
||||||
def self.find_from_meta_generator(options)
|
def self.find_from_meta_generator(options)
|
||||||
target_uri = options[:base_url]
|
target_uri = options[:base_url]
|
||||||
response = Browser.instance.get(target_uri.to_s, {:follow_location => true, :max_redirects => 2})
|
response = Browser.instance.get(target_uri.to_s, { follow_location: true, max_redirects: 2 })
|
||||||
|
|
||||||
response.body[%r{name="generator" content="wordpress #{WpVersion.version_pattern}"}i, 1]
|
response.body[%r{name="generator" content="wordpress #{WpVersion.version_pattern}"}i, 1]
|
||||||
end
|
end
|
||||||
@@ -69,7 +70,7 @@ class WpVersion < Vulnerable
|
|||||||
# the generator tag in the RSS feed source.
|
# the generator tag in the RSS feed source.
|
||||||
def self.find_from_rss_generator(options)
|
def self.find_from_rss_generator(options)
|
||||||
target_uri = options[:base_url]
|
target_uri = options[:base_url]
|
||||||
response = Browser.instance.get(target_uri.merge("feed/").to_s, {:follow_location => true, :max_redirects => 2})
|
response = Browser.instance.get(target_uri.merge('feed/').to_s, { follow_location: true, max_redirects: 2 })
|
||||||
|
|
||||||
response.body[%r{<generator>http://wordpress.org/\?v=#{WpVersion.version_pattern}</generator>}i, 1]
|
response.body[%r{<generator>http://wordpress.org/\?v=#{WpVersion.version_pattern}</generator>}i, 1]
|
||||||
end
|
end
|
||||||
@@ -78,7 +79,7 @@ class WpVersion < Vulnerable
|
|||||||
# the generator tag in the RDF feed source.
|
# the generator tag in the RDF feed source.
|
||||||
def self.find_from_rdf_generator(options)
|
def self.find_from_rdf_generator(options)
|
||||||
target_uri = options[:base_url]
|
target_uri = options[:base_url]
|
||||||
response = Browser.instance.get(target_uri.merge("feed/rdf/").to_s, {:follow_location => true, :max_redirects => 2})
|
response = Browser.instance.get(target_uri.merge('feed/rdf/').to_s, { follow_location: true, max_redirects: 2 })
|
||||||
|
|
||||||
response.body[%r{<admin:generatorAgent rdf:resource="http://wordpress.org/\?v=#{WpVersion.version_pattern}" />}i, 1]
|
response.body[%r{<admin:generatorAgent rdf:resource="http://wordpress.org/\?v=#{WpVersion.version_pattern}" />}i, 1]
|
||||||
end
|
end
|
||||||
@@ -89,7 +90,7 @@ class WpVersion < Vulnerable
|
|||||||
# Have not been able to find an example of this - Ryan
|
# Have not been able to find an example of this - Ryan
|
||||||
#def self.find_from_rss2_generator(options)
|
#def self.find_from_rss2_generator(options)
|
||||||
# target_uri = options[:base_url]
|
# target_uri = options[:base_url]
|
||||||
# response = Browser.instance.get(target_uri.merge("feed/rss/").to_s, {:follow_location => true, :max_redirects => 2})
|
# response = Browser.instance.get(target_uri.merge('feed/rss/').to_s, {:follow_location => true, :max_redirects => 2})
|
||||||
#
|
#
|
||||||
# response.body[%r{<generator>http://wordpress.org/?v=(#{WpVersion.version_pattern})</generator>}i, 1]
|
# response.body[%r{<generator>http://wordpress.org/?v=(#{WpVersion.version_pattern})</generator>}i, 1]
|
||||||
#end
|
#end
|
||||||
@@ -98,7 +99,7 @@ class WpVersion < Vulnerable
|
|||||||
# the generator tag in the Atom source.
|
# the generator tag in the Atom source.
|
||||||
def self.find_from_atom_generator(options)
|
def self.find_from_atom_generator(options)
|
||||||
target_uri = options[:base_url]
|
target_uri = options[:base_url]
|
||||||
response = Browser.instance.get(target_uri.merge("feed/atom/").to_s, {:follow_location => true, :max_redirects => 2})
|
response = Browser.instance.get(target_uri.merge('feed/atom/').to_s, { follow_location: true, max_redirects: 2 })
|
||||||
|
|
||||||
response.body[%r{<generator uri="http://wordpress.org/" version="#{WpVersion.version_pattern}">WordPress</generator>}i, 1]
|
response.body[%r{<generator uri="http://wordpress.org/" version="#{WpVersion.version_pattern}">WordPress</generator>}i, 1]
|
||||||
end
|
end
|
||||||
@@ -109,7 +110,7 @@ class WpVersion < Vulnerable
|
|||||||
# Have not been able to find an example of this - Ryan
|
# Have not been able to find an example of this - Ryan
|
||||||
#def self.find_from_comments_rss_generator(options)
|
#def self.find_from_comments_rss_generator(options)
|
||||||
# target_uri = options[:base_url]
|
# target_uri = options[:base_url]
|
||||||
# response = Browser.instance.get(target_uri.merge("comments/feed/").to_s, {:follow_location => true, :max_redirects => 2})
|
# response = Browser.instance.get(target_uri.merge('comments/feed/').to_s, {:follow_location => true, :max_redirects => 2})
|
||||||
#
|
#
|
||||||
# response.body[%r{<!-- generator="WordPress/#{WpVersion.version_pattern}" -->}i, 1]
|
# response.body[%r{<!-- generator="WordPress/#{WpVersion.version_pattern}" -->}i, 1]
|
||||||
#end
|
#end
|
||||||
@@ -129,7 +130,7 @@ class WpVersion < Vulnerable
|
|||||||
config.noblanks
|
config.noblanks
|
||||||
end
|
end
|
||||||
|
|
||||||
xml.xpath("//file").each do |node|
|
xml.xpath('//file').each do |node|
|
||||||
wp_content = options[:wp_content_dir]
|
wp_content = options[:wp_content_dir]
|
||||||
wp_plugins = "#{wp_content}/plugins"
|
wp_plugins = "#{wp_content}/plugins"
|
||||||
file_url = target_uri.merge(node.attribute('src').text).to_s
|
file_url = target_uri.merge(node.attribute('src').text).to_s
|
||||||
@@ -149,7 +150,7 @@ class WpVersion < Vulnerable
|
|||||||
# Attempts to find the WordPress version from the readme.html file.
|
# Attempts to find the WordPress version from the readme.html file.
|
||||||
def self.find_from_readme(options)
|
def self.find_from_readme(options)
|
||||||
target_uri = options[:base_url]
|
target_uri = options[:base_url]
|
||||||
Browser.instance.get(target_uri.merge("readme.html").to_s).body[%r{<br />\sversion #{WpVersion.version_pattern}}i, 1]
|
Browser.instance.get(target_uri.merge('readme.html').to_s).body[%r{<br />\sversion #{WpVersion.version_pattern}}i, 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Attempts to find the WordPress version from the sitemap.xml file.
|
# Attempts to find the WordPress version from the sitemap.xml file.
|
||||||
@@ -157,13 +158,13 @@ class WpVersion < Vulnerable
|
|||||||
# See: http://code.google.com/p/wpscan/issues/detail?id=109
|
# See: http://code.google.com/p/wpscan/issues/detail?id=109
|
||||||
def self.find_from_sitemap_generator(options)
|
def self.find_from_sitemap_generator(options)
|
||||||
target_uri = options[:base_url]
|
target_uri = options[:base_url]
|
||||||
Browser.instance.get(target_uri.merge("sitemap.xml").to_s).body[%r{generator="wordpress/#{WpVersion.version_pattern}"}i, 1]
|
Browser.instance.get(target_uri.merge('sitemap.xml').to_s).body[%r{generator="wordpress/#{WpVersion.version_pattern}"}i, 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Attempts to find the WordPress version from the p-links-opml.php file.
|
# Attempts to find the WordPress version from the p-links-opml.php file.
|
||||||
def self.find_from_links_opml(options)
|
def self.find_from_links_opml(options)
|
||||||
target_uri = options[:base_url]
|
target_uri = options[:base_url]
|
||||||
Browser.instance.get(target_uri.merge("wp-links-opml.php").to_s).body[%r{generator="wordpress/#{WpVersion.version_pattern}"}i, 1]
|
Browser.instance.get(target_uri.merge('wp-links-opml.php').to_s).body[%r{generator="wordpress/#{WpVersion.version_pattern}"}i, 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Used to check if the version is correct: must contain at least one dot.
|
# Used to check if the version is correct: must contain at least one dot.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -18,95 +19,95 @@
|
|||||||
|
|
||||||
require File.expand_path(File.dirname(__FILE__) + '/../common_helper')
|
require File.expand_path(File.dirname(__FILE__) + '/../common_helper')
|
||||||
|
|
||||||
require_files_from_directory(WPSCAN_LIB_DIR, "**/*.rb")
|
require_files_from_directory(WPSCAN_LIB_DIR, '**/*.rb')
|
||||||
|
|
||||||
# wpscan usage
|
# wpscan usage
|
||||||
def usage()
|
def usage
|
||||||
script_name = $0
|
script_name = $0
|
||||||
puts
|
puts
|
||||||
puts "Examples :"
|
puts 'Examples :'
|
||||||
puts
|
puts
|
||||||
puts "-Further help ..."
|
puts '-Further help ...'
|
||||||
puts "ruby #{script_name} --help"
|
puts "ruby #{script_name} --help"
|
||||||
puts
|
puts
|
||||||
puts "-Do 'non-intrusive' checks ..."
|
puts "-Do 'non-intrusive' checks ..."
|
||||||
puts "ruby #{script_name} --url www.example.com"
|
puts "ruby #{script_name} --url www.example.com"
|
||||||
puts
|
puts
|
||||||
puts "-Do wordlist password brute force on enumerated users using 50 threads ..."
|
puts '-Do wordlist password brute force on enumerated users using 50 threads ...'
|
||||||
puts "ruby #{script_name} --url www.example.com --wordlist darkc0de.lst --threads 50"
|
puts "ruby #{script_name} --url www.example.com --wordlist darkc0de.lst --threads 50"
|
||||||
puts
|
puts
|
||||||
puts "-Do wordlist password brute force on the 'admin' username only ..."
|
puts "-Do wordlist password brute force on the 'admin' username only ..."
|
||||||
puts "ruby #{script_name} --url www.example.com --wordlist darkc0de.lst --username admin"
|
puts "ruby #{script_name} --url www.example.com --wordlist darkc0de.lst --username admin"
|
||||||
puts
|
puts
|
||||||
puts "-Enumerate installed plugins ..."
|
puts '-Enumerate installed plugins ...'
|
||||||
puts "ruby #{script_name} --url www.example.com --enumerate p"
|
puts "ruby #{script_name} --url www.example.com --enumerate p"
|
||||||
puts
|
puts
|
||||||
puts "-Enumerate installed themes ..."
|
puts '-Enumerate installed themes ...'
|
||||||
puts "ruby #{script_name} --url www.example.com --enumerate t"
|
puts "ruby #{script_name} --url www.example.com --enumerate t"
|
||||||
puts
|
puts
|
||||||
puts "-Enumerate users ..."
|
puts '-Enumerate users ...'
|
||||||
puts "ruby #{script_name} --url www.example.com --enumerate u"
|
puts "ruby #{script_name} --url www.example.com --enumerate u"
|
||||||
puts
|
puts
|
||||||
puts "-Enumerate installed timthumbs ..."
|
puts '-Enumerate installed timthumbs ...'
|
||||||
puts "ruby #{script_name} --url www.example.com --enumerate tt"
|
puts "ruby #{script_name} --url www.example.com --enumerate tt"
|
||||||
puts
|
puts
|
||||||
puts "-Use a HTTP proxy ..."
|
puts '-Use a HTTP proxy ...'
|
||||||
puts "ruby #{script_name} --url www.example.com --proxy 127.0.0.1:8118"
|
puts "ruby #{script_name} --url www.example.com --proxy 127.0.0.1:8118"
|
||||||
puts
|
puts
|
||||||
puts "-Use a SOCKS5 proxy ... (cURL >= v7.21.7 needed)"
|
puts '-Use a SOCKS5 proxy ... (cURL >= v7.21.7 needed)'
|
||||||
puts "ruby #{script_name} --url www.example.com --proxy socks5://127.0.0.1:9000"
|
puts "ruby #{script_name} --url www.example.com --proxy socks5://127.0.0.1:9000"
|
||||||
puts
|
puts
|
||||||
puts "-Use custom content directory ..."
|
puts '-Use custom content directory ...'
|
||||||
puts "ruby #{script_name} -u www.example.com --wp-content-dir custom-content"
|
puts "ruby #{script_name} -u www.example.com --wp-content-dir custom-content"
|
||||||
puts
|
puts
|
||||||
puts "-Use custom plugins directory ..."
|
puts '-Use custom plugins directory ...'
|
||||||
puts "ruby #{script_name} -u www.example.com --wp-plugins-dir wp-content/custom-plugins"
|
puts "ruby #{script_name} -u www.example.com --wp-plugins-dir wp-content/custom-plugins"
|
||||||
puts
|
puts
|
||||||
puts "-Update ..."
|
puts '-Update ...'
|
||||||
puts "ruby #{script_name} --update"
|
puts "ruby #{script_name} --update"
|
||||||
puts
|
puts
|
||||||
puts "See README for further information."
|
puts 'See README for further information.'
|
||||||
puts
|
puts
|
||||||
end
|
end
|
||||||
|
|
||||||
# command help
|
# command help
|
||||||
def help()
|
def help
|
||||||
puts "Help :"
|
puts 'Help :'
|
||||||
puts
|
puts
|
||||||
puts "Some values are settable in conf/browser.conf.json :"
|
puts 'Some values are settable in conf/browser.conf.json :'
|
||||||
puts " user-agent, proxy, proxy-auth, threads, cache timeout and request timeout"
|
puts ' user-agent, proxy, proxy-auth, threads, cache timeout and request timeout'
|
||||||
puts
|
puts
|
||||||
puts "--update Update to the latest revision"
|
puts '--update Update to the latest revision'
|
||||||
puts "--url | -u <target url> The WordPress URL/domain to scan."
|
puts '--url | -u <target url> The WordPress URL/domain to scan.'
|
||||||
puts "--force | -f Forces WPScan to not check if the remote site is running WordPress."
|
puts '--force | -f Forces WPScan to not check if the remote site is running WordPress.'
|
||||||
puts "--enumerate | -e [option(s)] Enumeration."
|
puts '--enumerate | -e [option(s)] Enumeration.'
|
||||||
puts " option :"
|
puts ' option :'
|
||||||
puts " u usernames from id 1 to 10"
|
puts ' u usernames from id 1 to 10'
|
||||||
puts " u[10-20] usernames from id 10 to 20 (you must write [] chars)"
|
puts ' u[10-20] usernames from id 10 to 20 (you must write [] chars)'
|
||||||
puts " p plugins"
|
puts ' p plugins'
|
||||||
puts " vp only vulnerable plugins"
|
puts ' vp only vulnerable plugins'
|
||||||
puts " ap all plugins (can take a long time)"
|
puts ' ap all plugins (can take a long time)'
|
||||||
puts " tt timthumbs"
|
puts ' tt timthumbs'
|
||||||
puts " t themes"
|
puts ' t themes'
|
||||||
puts " vt only vulnerable themes"
|
puts ' vt only vulnerable themes'
|
||||||
puts " at all themes (can take a long time)"
|
puts ' at all themes (can take a long time)'
|
||||||
puts " Multiple values are allowed : '-e t,p' will enumerate timthumbs and plugins"
|
puts ' Multiple values are allowed : "-e t,p" will enumerate timthumbs and plugins'
|
||||||
puts " If no option is supplied, the default is 'vt,tt,u,vp'"
|
puts ' If no option is supplied, the default is "vt,tt,u,vp"'
|
||||||
puts
|
puts
|
||||||
puts "--exclude-content-based '<regexp or string>' Used with the enumeration option, will exclude all occurence based on the regexp or string supplied"
|
puts '--exclude-content-based "<regexp or string>" Used with the enumeration option, will exclude all occurence based on the regexp or string supplied'
|
||||||
puts " You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)"
|
puts ' You do not need to provide the regexp delimiters, but you must write the quotes (simple or double)'
|
||||||
puts "--config-file | -c <config file> Use the specified config file"
|
puts '--config-file | -c <config file> Use the specified config file'
|
||||||
puts "--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not"
|
puts '--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not'
|
||||||
puts "--wp-content-dir <wp content dir> WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specified it. Subdirectories are allowed"
|
puts '--wp-content-dir <wp content dir> WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specified it. Subdirectories are allowed'
|
||||||
puts "--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory. If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed"
|
puts '--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory. If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed'
|
||||||
puts "--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json)."
|
puts '--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).'
|
||||||
puts " HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used"
|
puts ' HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used'
|
||||||
puts "--proxy-auth <username:password> Supply the proxy login credentials (will override the one from conf/browser.conf.json)."
|
puts '--proxy-auth <username:password> Supply the proxy login credentials (will override the one from conf/browser.conf.json).'
|
||||||
puts "--basic-auth <username:password> Set the HTTP Basic authentification"
|
puts '--basic-auth <username:password> Set the HTTP Basic authentification'
|
||||||
puts "--wordlist | -w <wordlist> Supply a wordlist for the password bruter and do the brute."
|
puts '--wordlist | -w <wordlist> Supply a wordlist for the password bruter and do the brute.'
|
||||||
puts "--threads | -t <number of threads> The number of threads to use when multi-threading requests. (will override the value from conf/browser.conf.json)"
|
puts '--threads | -t <number of threads> The number of threads to use when multi-threading requests. (will override the value from conf/browser.conf.json)'
|
||||||
puts "--username | -U <username> Only brute force the supplied username."
|
puts '--username | -U <username> Only brute force the supplied username.'
|
||||||
puts "--help | -h This help screen."
|
puts '--help | -h This help screen.'
|
||||||
puts "--verbose | -v Verbose output."
|
puts '--verbose | -v Verbose output.'
|
||||||
puts
|
puts
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
#--
|
#--
|
||||||
# WPScan - WordPress Security Scanner
|
# WPScan - WordPress Security Scanner
|
||||||
# Copyright (C) 2012-2013
|
# Copyright (C) 2012-2013
|
||||||
@@ -56,7 +57,7 @@ class WpscanOptions
|
|||||||
end
|
end
|
||||||
|
|
||||||
def url=(url)
|
def url=(url)
|
||||||
raise "Empty URL given" if !url
|
raise 'Empty URL given' if !url
|
||||||
|
|
||||||
@url = URI.parse(add_http_protocol(url)).to_s
|
@url = URI.parse(add_http_protocol(url)).to_s
|
||||||
end
|
end
|
||||||
@@ -75,7 +76,7 @@ class WpscanOptions
|
|||||||
|
|
||||||
def proxy=(proxy)
|
def proxy=(proxy)
|
||||||
if proxy.index(':') == nil
|
if proxy.index(':') == nil
|
||||||
raise "Invalid proxy format. Should be host:port."
|
raise 'Invalid proxy format. Should be host:port.'
|
||||||
else
|
else
|
||||||
@proxy = proxy
|
@proxy = proxy
|
||||||
end
|
end
|
||||||
@@ -83,7 +84,7 @@ class WpscanOptions
|
|||||||
|
|
||||||
def proxy_auth=(auth)
|
def proxy_auth=(auth)
|
||||||
if auth.index(':') == nil
|
if auth.index(':') == nil
|
||||||
raise "Invalid proxy auth format, username:password expected"
|
raise 'Invalid proxy auth format, username:password expected'
|
||||||
else
|
else
|
||||||
@proxy_auth = auth
|
@proxy_auth = auth
|
||||||
end
|
end
|
||||||
@@ -91,7 +92,7 @@ class WpscanOptions
|
|||||||
|
|
||||||
def enumerate_plugins=(enumerate_plugins)
|
def enumerate_plugins=(enumerate_plugins)
|
||||||
if enumerate_plugins === true and (@enumerate_all_plugins === true or @enumerate_only_vulnerable_plugins === true)
|
if enumerate_plugins === true and (@enumerate_all_plugins === true or @enumerate_only_vulnerable_plugins === true)
|
||||||
raise "Please choose only one plugin enumeration option"
|
raise 'Please choose only one plugin enumeration option'
|
||||||
else
|
else
|
||||||
@enumerate_plugins = enumerate_plugins
|
@enumerate_plugins = enumerate_plugins
|
||||||
end
|
end
|
||||||
@@ -99,7 +100,7 @@ class WpscanOptions
|
|||||||
|
|
||||||
def enumerate_only_vulnerable_plugins=(enumerate_only_vulnerable_plugins)
|
def enumerate_only_vulnerable_plugins=(enumerate_only_vulnerable_plugins)
|
||||||
if enumerate_only_vulnerable_plugins === true and (@enumerate_all_plugins === true or @enumerate_plugins === true)
|
if enumerate_only_vulnerable_plugins === true and (@enumerate_all_plugins === true or @enumerate_plugins === true)
|
||||||
raise "Please choose only one plugin enumeration option"
|
raise 'Please choose only one plugin enumeration option'
|
||||||
else
|
else
|
||||||
@enumerate_only_vulnerable_plugins = enumerate_only_vulnerable_plugins
|
@enumerate_only_vulnerable_plugins = enumerate_only_vulnerable_plugins
|
||||||
end
|
end
|
||||||
@@ -107,7 +108,7 @@ class WpscanOptions
|
|||||||
|
|
||||||
def enumerate_all_plugins=(enumerate_all_plugins)
|
def enumerate_all_plugins=(enumerate_all_plugins)
|
||||||
if enumerate_all_plugins === true and (@enumerate_plugins === true or @enumerate_only_vulnerable_plugins === true)
|
if enumerate_all_plugins === true and (@enumerate_plugins === true or @enumerate_only_vulnerable_plugins === true)
|
||||||
raise "Please choose only one plugin enumeration option"
|
raise 'Please choose only one plugin enumeration option'
|
||||||
else
|
else
|
||||||
@enumerate_all_plugins = enumerate_all_plugins
|
@enumerate_all_plugins = enumerate_all_plugins
|
||||||
end
|
end
|
||||||
@@ -115,7 +116,7 @@ class WpscanOptions
|
|||||||
|
|
||||||
def enumerate_themes=(enumerate_themes)
|
def enumerate_themes=(enumerate_themes)
|
||||||
if enumerate_themes === true and (@enumerate_all_themes === true or @enumerate_only_vulnerable_themes === true)
|
if enumerate_themes === true and (@enumerate_all_themes === true or @enumerate_only_vulnerable_themes === true)
|
||||||
raise "Please choose only one theme enumeration option"
|
raise 'Please choose only one theme enumeration option'
|
||||||
else
|
else
|
||||||
@enumerate_themes = enumerate_themes
|
@enumerate_themes = enumerate_themes
|
||||||
end
|
end
|
||||||
@@ -123,7 +124,7 @@ class WpscanOptions
|
|||||||
|
|
||||||
def enumerate_only_vulnerable_themes=(enumerate_only_vulnerable_themes)
|
def enumerate_only_vulnerable_themes=(enumerate_only_vulnerable_themes)
|
||||||
if enumerate_only_vulnerable_themes === true and (@enumerate_all_themes === true or @enumerate_themes === true)
|
if enumerate_only_vulnerable_themes === true and (@enumerate_all_themes === true or @enumerate_themes === true)
|
||||||
raise "Please choose only one theme enumeration option"
|
raise 'Please choose only one theme enumeration option'
|
||||||
else
|
else
|
||||||
@enumerate_only_vulnerable_themes = enumerate_only_vulnerable_themes
|
@enumerate_only_vulnerable_themes = enumerate_only_vulnerable_themes
|
||||||
end
|
end
|
||||||
@@ -131,14 +132,14 @@ class WpscanOptions
|
|||||||
|
|
||||||
def enumerate_all_themes=(enumerate_all_themes)
|
def enumerate_all_themes=(enumerate_all_themes)
|
||||||
if enumerate_all_themes === true and (@enumerate_themes === true or @enumerate_only_vulnerable_themes === true)
|
if enumerate_all_themes === true and (@enumerate_themes === true or @enumerate_only_vulnerable_themes === true)
|
||||||
raise "Please choose only one theme enumeration option"
|
raise 'Please choose only one theme enumeration option'
|
||||||
else
|
else
|
||||||
@enumerate_all_themes = enumerate_all_themes
|
@enumerate_all_themes = enumerate_all_themes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def basic_auth=(basic_auth)
|
def basic_auth=(basic_auth)
|
||||||
raise "Invalid basic authentication format, login:password expected" if basic_auth.index(':').nil?
|
raise 'Invalid basic authentication format, login:password expected' if basic_auth.index(':').nil?
|
||||||
@basic_auth = "Basic #{Base64.encode64(basic_auth).chomp}"
|
@basic_auth = "Basic #{Base64.encode64(basic_auth).chomp}"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -183,9 +184,9 @@ class WpscanOptions
|
|||||||
WpscanOptions.option_to_instance_variable_setter(cli_option),
|
WpscanOptions.option_to_instance_variable_setter(cli_option),
|
||||||
cli_value
|
cli_value
|
||||||
)
|
)
|
||||||
elsif cli_option === "--enumerate" # Special cases
|
elsif cli_option === '--enumerate' # Special cases
|
||||||
# Default value if no argument is given
|
# Default value if no argument is given
|
||||||
cli_value = "vt,tt,u,vp" if cli_value.length == 0
|
cli_value = 'vt,tt,u,vp' if cli_value.length == 0
|
||||||
|
|
||||||
enumerate_options_from_string(cli_value)
|
enumerate_options_from_string(cli_value)
|
||||||
else
|
else
|
||||||
@@ -200,7 +201,7 @@ class WpscanOptions
|
|||||||
def enumerate_options_from_string(value)
|
def enumerate_options_from_string(value)
|
||||||
# Usage of self is mandatory because there are overridden setters
|
# Usage of self is mandatory because there are overridden setters
|
||||||
|
|
||||||
value = value.split(',').map{ |c| c.downcase }
|
value = value.split(',').map { |c| c.downcase }
|
||||||
|
|
||||||
self.enumerate_only_vulnerable_plugins = true if value.include?('vp')
|
self.enumerate_only_vulnerable_plugins = true if value.include?('vp')
|
||||||
|
|
||||||
@@ -231,23 +232,23 @@ class WpscanOptions
|
|||||||
# Even if a short option is given (IE : -u), the long one will be returned (IE : --url)
|
# Even if a short option is given (IE : -u), the long one will be returned (IE : --url)
|
||||||
def self.get_opt_long
|
def self.get_opt_long
|
||||||
GetoptLong.new(
|
GetoptLong.new(
|
||||||
["--url", "-u", GetoptLong::REQUIRED_ARGUMENT],
|
['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--enumerate", "-e", GetoptLong::OPTIONAL_ARGUMENT],
|
['--enumerate', '-e', GetoptLong::OPTIONAL_ARGUMENT],
|
||||||
["--username", "-U", GetoptLong::REQUIRED_ARGUMENT],
|
['--username', '-U', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--wordlist", "-w", GetoptLong::REQUIRED_ARGUMENT],
|
['--wordlist', '-w', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--threads", "-t", GetoptLong::REQUIRED_ARGUMENT],
|
['--threads', '-t', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--force", "-f", GetoptLong::NO_ARGUMENT],
|
['--force', '-f', GetoptLong::NO_ARGUMENT],
|
||||||
["--help", "-h", GetoptLong::NO_ARGUMENT],
|
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
||||||
["--verbose", "-v", GetoptLong::NO_ARGUMENT],
|
['--verbose', '-v', GetoptLong::NO_ARGUMENT],
|
||||||
["--proxy", GetoptLong::REQUIRED_ARGUMENT],
|
['--proxy', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--proxy-auth", GetoptLong::REQUIRED_ARGUMENT],
|
['--proxy-auth', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--update", GetoptLong::NO_ARGUMENT],
|
['--update', GetoptLong::NO_ARGUMENT],
|
||||||
["--follow-redirection", GetoptLong::NO_ARGUMENT],
|
['--follow-redirection', GetoptLong::NO_ARGUMENT],
|
||||||
["--wp-content-dir", GetoptLong::REQUIRED_ARGUMENT],
|
['--wp-content-dir', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--wp-plugins-dir", GetoptLong::REQUIRED_ARGUMENT],
|
['--wp-plugins-dir', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--config-file", "-c", GetoptLong::REQUIRED_ARGUMENT],
|
['--config-file', '-c', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--exclude-content-based", GetoptLong::REQUIRED_ARGUMENT],
|
['--exclude-content-based', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
["--basic-auth", GetoptLong::REQUIRED_ARGUMENT]
|
['--basic-auth', GetoptLong::REQUIRED_ARGUMENT]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user