lib/wpscan rubocopied
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -34,10 +35,10 @@ module BruteForce
|
||||
request_count = 0
|
||||
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...
|
||||
next if password[0,1] == "#"
|
||||
next if password[0, 1] == '#'
|
||||
|
||||
# keep a count of the amount of requests to be sent
|
||||
request_count += 1
|
||||
@@ -50,9 +51,9 @@ module BruteForce
|
||||
# the request object
|
||||
request = Browser.instance.forge_request(login_url,
|
||||
{
|
||||
:method => :post,
|
||||
:params => {:log => username, :pwd => password},
|
||||
:cache_timeout => 0
|
||||
method: :post,
|
||||
params: {log: username, pwd: password},
|
||||
cache_timeout: 0
|
||||
}
|
||||
)
|
||||
|
||||
@@ -64,20 +65,20 @@ module BruteForce
|
||||
if response.body =~ /login_error/i
|
||||
puts "\nIncorrect username and/or password." if @verbose
|
||||
elsif response.code == 302
|
||||
puts "\n " + green("[SUCCESS]") + " Username : #{username} Password : #{password}\n" if show_progression
|
||||
found << { :name => username, :password => password }
|
||||
puts "\n " + green('[SUCCESS]') + " Username : #{username} Password : #{password}\n" if show_progression
|
||||
found << { name: username, password: password }
|
||||
password_found = true
|
||||
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
|
||||
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
|
||||
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
|
||||
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("Body: #{response.body}") if @verbose
|
||||
puts if @verbose
|
||||
@@ -116,7 +117,7 @@ module BruteForce
|
||||
# wordlists, although bareable.
|
||||
def self.lines_in_file(file_path)
|
||||
lines = 0
|
||||
File.open(file_path, 'r').each { || lines += 1 }
|
||||
File.open(file_path, 'r').each { |_| lines += 1 }
|
||||
lines
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -17,7 +18,10 @@
|
||||
#++
|
||||
|
||||
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
|
||||
|
||||
def has_malwares?(malwares_file_path = nil)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -34,7 +35,7 @@ module WebSite
|
||||
|
||||
response = Browser.instance.get(
|
||||
login_url(),
|
||||
{:follow_location => true, :max_redirects => 2}
|
||||
{ follow_location: true, max_redirects: 2 }
|
||||
)
|
||||
|
||||
if response.body =~ %r{WordPress}i
|
||||
@@ -42,7 +43,7 @@ module WebSite
|
||||
else
|
||||
response = Browser.instance.get(
|
||||
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
|
||||
@@ -60,7 +61,7 @@ module WebSite
|
||||
def xml_rpc_url
|
||||
unless @xmlrpc_url
|
||||
headers = Browser.instance.get(@uri.to_s).headers_hash
|
||||
value = headers["x-pingback"]
|
||||
value = headers['x-pingback']
|
||||
if value.nil? or value.empty?
|
||||
@xmlrpc_url = nil
|
||||
else
|
||||
@@ -105,7 +106,7 @@ module WebSite
|
||||
# Return the MD5 hash of a 404 page
|
||||
def 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)
|
||||
end
|
||||
@error_404_hash
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -25,6 +26,6 @@ module WpFullPathDisclosure
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -37,10 +38,10 @@ module WpLoginProtection
|
||||
plugin_name = symbol_to_call[LOGIN_PROTECTION_METHOD_PATTERN, 1].gsub('_', '-')
|
||||
|
||||
return @login_protection_plugin = WpPlugin.new(
|
||||
:name => plugin_name,
|
||||
:base_url => @uri,
|
||||
:path => "/plugins/#{plugin_name}/",
|
||||
:wp_content_dir => @wp_content_dir
|
||||
name: plugin_name,
|
||||
base_url: @uri,
|
||||
path: "/plugins/#{plugin_name}/",
|
||||
wp_content_dir: @wp_content_dir
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -67,10 +68,10 @@ module WpLoginProtection
|
||||
end
|
||||
|
||||
def better_wp_security_url
|
||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
||||
:base_url => @uri,
|
||||
:path => "/plugins/better-wp-security/",
|
||||
:name => "better-wp-security"
|
||||
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||
base_url: @uri,
|
||||
path: '/plugins/better-wp-security/',
|
||||
name: 'better-wp-security'
|
||||
).get_url_without_filename
|
||||
end
|
||||
|
||||
@@ -80,10 +81,10 @@ module WpLoginProtection
|
||||
end
|
||||
|
||||
def simple_login_lockdown_url
|
||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
||||
:base_url => @uri,
|
||||
:path => "/plugins/simple-login-lockdown/",
|
||||
:name => "simple-login-lockdown"
|
||||
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||
base_url: @uri,
|
||||
path: '/plugins/simple-login-lockdown/',
|
||||
name: 'simple-login-lockdown'
|
||||
).get_url_without_filename
|
||||
end
|
||||
|
||||
@@ -93,10 +94,10 @@ module WpLoginProtection
|
||||
end
|
||||
|
||||
def login_security_solution_url
|
||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
||||
:base_url => @uri,
|
||||
:path => "/plugins/login-security-solution/",
|
||||
:name => "login-security-solution"
|
||||
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||
base_url: @uri,
|
||||
path: '/plugins/login-security-solution/',
|
||||
name: 'login-security-solution'
|
||||
).get_url_without_filename
|
||||
end
|
||||
|
||||
@@ -106,10 +107,10 @@ module WpLoginProtection
|
||||
end
|
||||
|
||||
def limit_login_attempts_url
|
||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
||||
:base_url => @uri,
|
||||
:path => "/plugins/limit-login-attempts/",
|
||||
:name => "limit-login-attempts"
|
||||
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||
base_url: @uri,
|
||||
path: '/plugins/limit-login-attempts/',
|
||||
name: 'limit-login-attempts'
|
||||
).get_url_without_filename
|
||||
end
|
||||
|
||||
@@ -119,10 +120,10 @@ module WpLoginProtection
|
||||
end
|
||||
|
||||
def bluetrait_event_viewer_url
|
||||
WpPlugin.new(:wp_content_dir => @wp_content_dir,
|
||||
:base_url => @uri,
|
||||
:path => "/plugins/bluetrait-event-viewer/",
|
||||
:name => "bluetrait-event-viewer"
|
||||
WpPlugin.new(wp_content_dir: @wp_content_dir,
|
||||
base_url: @uri,
|
||||
path: '/plugins/bluetrait-event-viewer/',
|
||||
name: 'bluetrait-event-viewer'
|
||||
).get_url_without_filename
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -22,22 +23,24 @@ module WpPlugins
|
||||
#
|
||||
# return array of WpPlugin
|
||||
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[: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_2] = "//plugin"
|
||||
options[:type] = "plugins"
|
||||
options[:vulns_xpath_2] = '//plugin'
|
||||
options[:type] = 'plugins'
|
||||
result = WpDetector.aggressive_detection(options)
|
||||
plugins = []
|
||||
result.each do |r|
|
||||
plugins << WpPlugin.new(
|
||||
:base_url => r.base_url,
|
||||
:path => r.path,
|
||||
:wp_content_dir => r.wp_content_dir,
|
||||
:name => r.name,
|
||||
:type => "plugins",
|
||||
:wp_plugins_dir => r.wp_plugins_dir
|
||||
base_url: r.base_url,
|
||||
path: r.path,
|
||||
wp_content_dir: r.wp_content_dir,
|
||||
name: r.name,
|
||||
type: 'plugins',
|
||||
wp_plugins_dir: r.wp_plugins_dir
|
||||
)
|
||||
end
|
||||
plugins.sort_by { |p| p.name }
|
||||
@@ -51,16 +54,16 @@ module WpPlugins
|
||||
# return array of WpPlugin
|
||||
def plugins_from_passive_detection(options)
|
||||
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|
|
||||
plugins << WpPlugin.new(
|
||||
:base_url => item.base_url,
|
||||
:name => item.name,
|
||||
:path => item.path,
|
||||
:wp_content_dir => options[:wp_content_dir],
|
||||
:type => "plugins",
|
||||
:wp_plugins_dir => options[:wp_plugins_dir]
|
||||
base_url: item.base_url,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
wp_content_dir: options[:wp_content_dir],
|
||||
type: 'plugins',
|
||||
wp_plugins_dir: options[:wp_plugins_dir]
|
||||
)
|
||||
end
|
||||
plugins.sort_by { |p| p.name }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -31,6 +32,6 @@ module WpReadme
|
||||
end
|
||||
|
||||
def readme_url
|
||||
@uri.merge("readme.html").to_s
|
||||
@uri.merge('readme.html').to_s
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -19,20 +20,22 @@
|
||||
module WpThemes
|
||||
|
||||
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[: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_2] = "//theme"
|
||||
options[:type] = "themes"
|
||||
options[:vulns_xpath_2] = '//theme'
|
||||
options[:type] = 'themes'
|
||||
result = WpDetector.aggressive_detection(options)
|
||||
themes = []
|
||||
result.each do |r|
|
||||
themes << WpTheme.new(
|
||||
:base_url => r.base_url,
|
||||
:path => r.path,
|
||||
:wp_content_dir => r.wp_content_dir,
|
||||
:name => r.name
|
||||
base_url: r.base_url,
|
||||
path: r.path,
|
||||
wp_content_dir: r.wp_content_dir,
|
||||
name: r.name
|
||||
)
|
||||
end
|
||||
themes.sort_by { |t| t.name }
|
||||
@@ -40,14 +43,14 @@ module WpThemes
|
||||
|
||||
def themes_from_passive_detection(options)
|
||||
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|
|
||||
themes << WpTheme.new(
|
||||
:base_url => item.base_url,
|
||||
:name => item.name,
|
||||
:path => item.path,
|
||||
:wp_content_dir => options[:wp_content_dir]
|
||||
base_url: item.base_url,
|
||||
name: item.name,
|
||||
path: item.path,
|
||||
wp_content_dir: options[:wp_content_dir]
|
||||
)
|
||||
end
|
||||
themes.sort_by { |t| t.name }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -18,7 +19,10 @@
|
||||
|
||||
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
|
||||
|
||||
def has_timthumbs?(theme_name, options = {})
|
||||
@@ -27,12 +31,12 @@ module WpTimthumbs
|
||||
|
||||
def timthumbs(theme_name = nil, options = {})
|
||||
if @wp_timthumbs.nil?
|
||||
options[:type] = "timthumbs"
|
||||
options[:type] = 'timthumbs'
|
||||
options[:only_vulnerable_ones] = false
|
||||
options[:file] = options[:file] || DATA_DIR + "/timthumbs.txt"
|
||||
options[:vulns_file] = "xxx"
|
||||
options[:vulns_xpath] = "xxx"
|
||||
options[:vulns_xpath_2] = "xxx"
|
||||
options[:file] = options[:file] || DATA_DIR + '/timthumbs.txt'
|
||||
options[:vulns_file] = 'xxx'
|
||||
options[:vulns_xpath] = 'xxx'
|
||||
options[:vulns_xpath_2] = 'xxx'
|
||||
|
||||
WpOptions.check_options(options)
|
||||
if theme_name == nil
|
||||
@@ -55,13 +59,13 @@ module WpTimthumbs
|
||||
scripts/timthumb.php tools/timthumb.php functions/timthumb.php
|
||||
}.each do |file|
|
||||
targets << WpItem.new(
|
||||
:base_url => options[:base_url],
|
||||
:path => "themes/#{theme_name}/#{file}",
|
||||
:wp_content_dir => options[:wp_content_dir],
|
||||
:name => theme_name,
|
||||
:vulns_file => "XX",
|
||||
:type => "timthumbs",
|
||||
:wp_plugins_dir => options[:wp_plugins_dir]
|
||||
base_url: options[:base_url],
|
||||
path: "themes/#{theme_name}/#{file}",
|
||||
wp_content_dir: options[:wp_content_dir],
|
||||
name: theme_name,
|
||||
vulns_file: 'XX',
|
||||
type: 'timthumbs',
|
||||
wp_plugins_dir: options[:wp_plugins_dir]
|
||||
)
|
||||
end
|
||||
targets
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -58,7 +59,7 @@ module WpUsernames
|
||||
end
|
||||
|
||||
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
|
||||
if resp.code == 200
|
||||
nickname = extract_nickname_from_body(resp.body)
|
||||
@@ -80,21 +81,21 @@ module WpUsernames
|
||||
|
||||
def remove_junk_from_nickname(usernames)
|
||||
unless usernames.kind_of? Array
|
||||
raise("Need an array as input")
|
||||
raise('Need an array as input')
|
||||
end
|
||||
nicknames = []
|
||||
usernames.each do |u|
|
||||
unless u.kind_of? WpUser
|
||||
raise("Items must be of type WpUser")
|
||||
raise('Items must be of type WpUser')
|
||||
end
|
||||
nickname = u.nickname
|
||||
unless nickname == "empty"
|
||||
unless nickname == 'empty'
|
||||
nicknames << nickname
|
||||
end
|
||||
end
|
||||
junk = get_equal_string_end(nicknames)
|
||||
usernames.each do |u|
|
||||
u.nickname = u.nickname.sub(/#{Regexp.escape(junk)}$/, "")
|
||||
u.nickname = u.nickname.sub(/#{Regexp.escape(junk)}$/, '')
|
||||
end
|
||||
usernames
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -30,10 +31,10 @@ class Vulnerable
|
||||
|
||||
xml.xpath(@vulns_xpath).each do |node|
|
||||
vulnerabilities << WpVulnerability.new(
|
||||
node.search("title").text,
|
||||
node.search("reference").map(&:text),
|
||||
node.search("type").text,
|
||||
node.search("metasploit").map(&:text)
|
||||
node.search('title').text,
|
||||
node.search('reference').map(&:text),
|
||||
node.search('type').text,
|
||||
node.search('metasploit').map(&:text)
|
||||
)
|
||||
end
|
||||
vulnerabilities
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -63,12 +64,12 @@ class WpDetector
|
||||
|
||||
names.each do |item|
|
||||
items << WpItem.new(
|
||||
:base_url => url,
|
||||
:name => item,
|
||||
:type => type,
|
||||
:path => "#{item}/",
|
||||
:wp_content_dir => wp_content_dir,
|
||||
:vulns_file => ""
|
||||
base_url: url,
|
||||
name: item,
|
||||
type: type,
|
||||
path: "#{item}/",
|
||||
wp_content_dir: wp_content_dir,
|
||||
vulns_file: ''
|
||||
)
|
||||
end
|
||||
items
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -54,7 +55,7 @@ class WpEnumerator
|
||||
targets.each do |target|
|
||||
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.on_complete do |response|
|
||||
@@ -102,17 +103,17 @@ class WpEnumerator
|
||||
|
||||
unless only_vulnerable
|
||||
# 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|
|
||||
l = line.strip
|
||||
targets_url << WpItem.new(
|
||||
:base_url => url,
|
||||
:path => l,
|
||||
:wp_content_dir => wp_content_dir,
|
||||
:name => l =~ /.+\/.+/ ? File.dirname(l) : l.sub(/\/$/, ""),
|
||||
:vulns_file => vulns_file,
|
||||
:type => type,
|
||||
:wp_plugins_dir => plugins_dir
|
||||
base_url: url,
|
||||
path: l,
|
||||
wp_content_dir: wp_content_dir,
|
||||
name: l =~ /.+\/.+/ ? File.dirname(l) : l.sub(/\/$/, ''),
|
||||
vulns_file: vulns_file,
|
||||
type: type,
|
||||
wp_plugins_dir: plugins_dir
|
||||
)
|
||||
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
|
||||
xml.xpath(options[:vulns_xpath_2]).each do |node|
|
||||
name = node.attribute("name").text
|
||||
name = node.attribute('name').text
|
||||
targets_url << WpItem.new(
|
||||
:base_url => url,
|
||||
:path => name,
|
||||
:wp_content_dir => wp_content_dir,
|
||||
:name => name,
|
||||
:vulns_file => vulns_file,
|
||||
:type => type,
|
||||
:wp_plugins_dir => plugins_dir
|
||||
base_url: url,
|
||||
path: name,
|
||||
wp_content_dir: wp_content_dir,
|
||||
name: name,
|
||||
vulns_file: vulns_file,
|
||||
type: type,
|
||||
wp_plugins_dir: plugins_dir
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -24,7 +25,7 @@ class WpItem < Vulnerable
|
||||
|
||||
def initialize(options)
|
||||
@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"
|
||||
@base_url = options[:base_url]
|
||||
@path = options[:path]
|
||||
@@ -32,22 +33,22 @@ class WpItem < Vulnerable
|
||||
@vulns_file = options[:vulns_file]
|
||||
@vulns_xpath = options[:vulns_xpath].sub(/\$name\$/, @name) unless options[:vulns_xpath] == nil
|
||||
|
||||
raise("base_url not set") unless @base_url
|
||||
raise("path not set") unless @path
|
||||
raise("wp_content_dir not set") unless @wp_content_dir
|
||||
raise("name not set") unless @name
|
||||
raise("vulns_file not set") unless @vulns_file
|
||||
raise("type not set") unless @type
|
||||
raise('base_url not set') unless @base_url
|
||||
raise('path not set') unless @path
|
||||
raise('wp_content_dir not set') unless @wp_content_dir
|
||||
raise('name not set') unless @name
|
||||
raise('vulns_file not set') unless @vulns_file
|
||||
raise('type not set') unless @type
|
||||
end
|
||||
|
||||
# The wordpress.org plugins directory URL
|
||||
# See: https://github.com/wpscanteam/wpscan/issues/100
|
||||
def wp_org_url
|
||||
case @type
|
||||
when "themes"
|
||||
return URI("http://wordpress.org/extend/themes/").merge("#@name/")
|
||||
when "plugins"
|
||||
return URI("http://wordpress.org/extend/plugins/").merge("#@name/")
|
||||
when 'themes'
|
||||
return URI('http://wordpress.org/extend/themes/').merge("#@name/")
|
||||
when 'plugins'
|
||||
return URI('http://wordpress.org/extend/plugins/').merge("#@name/")
|
||||
else
|
||||
raise("No Wordpress URL for #@type")
|
||||
end
|
||||
@@ -56,9 +57,9 @@ class WpItem < Vulnerable
|
||||
# returns true if this theme or plugin is hosted on wordpress.org
|
||||
def wp_org_item?
|
||||
case @type
|
||||
when "themes"
|
||||
when 'themes'
|
||||
file = THEMES_FULL_FILE
|
||||
when "plugins"
|
||||
when 'plugins'
|
||||
file = PLUGINS_FULL_FILE
|
||||
else
|
||||
raise("Unknown type #@type")
|
||||
@@ -69,9 +70,9 @@ class WpItem < Vulnerable
|
||||
|
||||
def get_sub_folder
|
||||
case @type
|
||||
when "themes"
|
||||
folder = "themes"
|
||||
when "timthumbs"
|
||||
when 'themes'
|
||||
folder = 'themes'
|
||||
when 'timthumbs'
|
||||
# not needed
|
||||
folder = nil
|
||||
else
|
||||
@@ -82,15 +83,15 @@ class WpItem < Vulnerable
|
||||
|
||||
# Get the full url for this item
|
||||
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 /
|
||||
wp_content_dir = @wp_content_dir.sub(/^\//, "").sub(/\/$/, "")
|
||||
wp_content_dir = @wp_content_dir.sub(/^\//, "").sub(/\/$/, '')
|
||||
# remove first /
|
||||
path = @path.sub(/^\//, "")
|
||||
if type =="plugins"
|
||||
path = @path.sub(/^\//, '')
|
||||
if type == 'plugins'
|
||||
# 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}"))
|
||||
elsif type == "timthumbs"
|
||||
elsif type == 'timthumbs'
|
||||
# timthumbs have folder in path variable
|
||||
ret = URI.parse(URI.encode("#{url}#{wp_content_dir}/#{path}"))
|
||||
else
|
||||
@@ -112,7 +113,7 @@ class WpItem < Vulnerable
|
||||
# Returns version number from readme.txt if it exists
|
||||
def 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]
|
||||
end
|
||||
@version
|
||||
@@ -152,12 +153,12 @@ class WpItem < Vulnerable
|
||||
|
||||
# Url for readme.txt
|
||||
def readme_url
|
||||
get_url_without_filename.merge("readme.txt")
|
||||
get_url_without_filename.merge('readme.txt')
|
||||
end
|
||||
|
||||
# Url for changelog.txt
|
||||
def changelog_url
|
||||
get_url_without_filename.merge("changelog.txt")
|
||||
get_url_without_filename.merge('changelog.txt')
|
||||
end
|
||||
|
||||
# readme.txt present?
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -32,16 +33,16 @@
|
||||
# * +type+ - Type: plugins, themes
|
||||
class WpOptions
|
||||
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("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("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_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("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("type must be set") unless options[:type] != nil and options[:type].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('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_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('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('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
|
||||
|
||||
unless options[:type] =~ /plugins/i or options[:type] =~ /themes/i or options[:type] =~ /timthumbs/i
|
||||
raise("Unknown type #{options[:type]}")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -18,11 +19,14 @@
|
||||
|
||||
class WpPlugin < WpItem
|
||||
def initialize(options = {})
|
||||
options[:vulns_file] = (options[:vulns_file] != nil and options[:vulns_file] != "") ?
|
||||
options[:vulns_file] : PLUGINS_VULNS_FILE
|
||||
if options[:vulns_file].nil? or options[:vulns_file] == ''
|
||||
options[:vulns_file] = PLUGINS_VULNS_FILE
|
||||
end
|
||||
|
||||
options[:vulns_xpath] = "//plugin[@name='$name$']/vulnerability"
|
||||
options[:vulns_xpath_2] = "//plugin"
|
||||
options[:type] = "plugins"
|
||||
options[:vulns_xpath_2] = '//plugin'
|
||||
options[:type] = 'plugins'
|
||||
|
||||
super(options)
|
||||
end
|
||||
|
||||
@@ -32,11 +36,11 @@ class WpPlugin < WpItem
|
||||
# however can also be found in their specific plugin dir.
|
||||
# http://www.exploit-db.com/ghdb/3714/
|
||||
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
|
||||
end
|
||||
|
||||
def error_log_url
|
||||
get_full_url.merge("error_log").to_s
|
||||
get_full_url.merge('error_log').to_s
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -47,7 +48,7 @@ class WpTarget
|
||||
end
|
||||
|
||||
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)
|
||||
redirection = redirection(url)
|
||||
@@ -80,9 +81,9 @@ class WpTarget
|
||||
uri_path = @uri.path
|
||||
|
||||
if index_body[/\/wp-content\/(?:themes|plugins)\//i]
|
||||
@wp_content_dir = "wp-content"
|
||||
@wp_content_dir = 'wp-content'
|
||||
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]
|
||||
end
|
||||
end
|
||||
@@ -102,7 +103,7 @@ class WpTarget
|
||||
|
||||
def has_debug_log?
|
||||
# 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
|
||||
end
|
||||
|
||||
@@ -114,7 +115,7 @@ class WpTarget
|
||||
# reveals databse credentials after hitting submit
|
||||
# http://interconnectit.com/124/search-and-replace-for-wordpress-databases/
|
||||
def search_replace_db_2_url
|
||||
@uri.merge("searchreplacedb2.php").to_s
|
||||
@uri.merge('searchreplacedb2.php').to_s
|
||||
end
|
||||
|
||||
def search_replace_db_2_exists?
|
||||
@@ -126,7 +127,7 @@ class WpTarget
|
||||
def registration_enabled?
|
||||
resp = Browser.instance.get(registration_url)
|
||||
# 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
|
||||
# multi site registration form
|
||||
elsif resp.code == 200 and resp.body =~ /<form id="setupform" method="post" action="[^"]*wp-signup\.php[^"]*">/i
|
||||
@@ -142,18 +143,18 @@ class WpTarget
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def is_multisite?
|
||||
unless @multisite
|
||||
# when multi site, there is no redirection or a redirect to the site itself
|
||||
# otherwise redirect to wp-login.php
|
||||
url = @uri.merge("wp-signup.php")
|
||||
url = @uri.merge('wp-signup.php')
|
||||
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
|
||||
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
|
||||
elsif resp.code == 200
|
||||
@multisite = true
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -23,12 +24,15 @@ class WpTheme < WpItem
|
||||
attr_reader :style_url, :version
|
||||
|
||||
def initialize(options = {})
|
||||
options[:vulns_file] = (options[:vulns_file] != nil and options[:vulns_file] != "") ?
|
||||
options[:vulns_file] : THEMES_VULNS_FILE
|
||||
if options[:vulns_file].nil? or options[:vulns_file] == ''
|
||||
options[:vulns_file] = THEMES_VULNS_FILE
|
||||
end
|
||||
|
||||
options[:vulns_xpath] = "//theme[@name='$name$']/vulnerability"
|
||||
options[:type] = "themes"
|
||||
options[:type] = 'themes'
|
||||
@version = options[:version]
|
||||
@style_url = options[:style_url]
|
||||
|
||||
super(options)
|
||||
end
|
||||
|
||||
@@ -58,7 +62,7 @@ class WpTheme < WpItem
|
||||
|
||||
# Discover the wordpress theme name by parsing the css link rel
|
||||
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)
|
||||
if matches
|
||||
@@ -66,11 +70,12 @@ class WpTheme < WpItem
|
||||
wp_content_dir = matches[1]
|
||||
theme_name = matches[2]
|
||||
|
||||
return new(:name => theme_name,
|
||||
:style_url => style_url,
|
||||
:base_url => target_uri,
|
||||
:path => theme_name,
|
||||
:wp_content_dir => wp_content_dir
|
||||
return new(
|
||||
name: theme_name,
|
||||
style_url: style_url,
|
||||
base_url: target_uri,
|
||||
path: theme_name,
|
||||
wp_content_dir: wp_content_dir
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -86,11 +91,12 @@ class WpTheme < WpItem
|
||||
woo_theme_version = matches[2]
|
||||
woo_framework_version = matches[3] # Not used at this time
|
||||
|
||||
return new(:name => woo_theme_name,
|
||||
:version => woo_theme_version,
|
||||
:base_url => matches[0],
|
||||
:path => "",
|
||||
:wp_content_dir => ""
|
||||
return new(
|
||||
name: woo_theme_name,
|
||||
version: woo_theme_version,
|
||||
base_url: matches[0],
|
||||
path: '',
|
||||
wp_content_dir: ''
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -20,7 +21,7 @@ class WpUser
|
||||
|
||||
def name
|
||||
if @name.nil? or @name.to_s.strip.empty?
|
||||
return "empty"
|
||||
return 'empty'
|
||||
end
|
||||
@name
|
||||
end
|
||||
@@ -31,7 +32,7 @@ class WpUser
|
||||
|
||||
def id
|
||||
if @id.nil? or @id.to_s.strip.empty?
|
||||
return "empty"
|
||||
return 'empty'
|
||||
end
|
||||
@id
|
||||
end
|
||||
@@ -42,7 +43,7 @@ class WpUser
|
||||
|
||||
def nickname
|
||||
if @nickname.nil? or @nickname.to_s.strip.empty?
|
||||
return "empty"
|
||||
return 'empty'
|
||||
end
|
||||
@nickname
|
||||
end
|
||||
@@ -57,15 +58,15 @@ class WpUser
|
||||
self.nickname = nickname
|
||||
end
|
||||
|
||||
def <=>(item)
|
||||
item.name <=> self.name
|
||||
def <=>(other)
|
||||
other.name <=> self.name
|
||||
end
|
||||
|
||||
def ===(item)
|
||||
item.name === self.name and item.id === self.id and item.nickname === self.nickname
|
||||
def ===(other)
|
||||
other.name === self.name and other.id === self.id and other.nickname === self.nickname
|
||||
end
|
||||
|
||||
def eql?(item)
|
||||
item.name === self.name and item.id === self.id and item.nickname === self.nickname
|
||||
def eql?(other)
|
||||
other.name === self.name and other.id === self.id and other.nickname === self.nickname
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -38,14 +39,14 @@ class WpVersion < Vulnerable
|
||||
# (find_from_meta_generator, find_from_rss_generator etc)
|
||||
def self.find(target_uri, wp_content_dir)
|
||||
options = {
|
||||
:base_url => target_uri,
|
||||
:wp_content_dir => wp_content_dir
|
||||
base_url: target_uri,
|
||||
wp_content_dir: wp_content_dir
|
||||
}
|
||||
self.methods.grep(/find_from_/).each do |method_to_call|
|
||||
version = self.send(method_to_call, options)
|
||||
|
||||
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
|
||||
nil
|
||||
@@ -60,7 +61,7 @@ class WpVersion < Vulnerable
|
||||
# that it is reinstated on upgrade.
|
||||
def self.find_from_meta_generator(options)
|
||||
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]
|
||||
end
|
||||
@@ -69,7 +70,7 @@ class WpVersion < Vulnerable
|
||||
# the generator tag in the RSS feed source.
|
||||
def self.find_from_rss_generator(options)
|
||||
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]
|
||||
end
|
||||
@@ -78,7 +79,7 @@ class WpVersion < Vulnerable
|
||||
# the generator tag in the RDF feed source.
|
||||
def self.find_from_rdf_generator(options)
|
||||
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]
|
||||
end
|
||||
@@ -89,7 +90,7 @@ class WpVersion < Vulnerable
|
||||
# Have not been able to find an example of this - Ryan
|
||||
#def self.find_from_rss2_generator(options)
|
||||
# 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]
|
||||
#end
|
||||
@@ -98,7 +99,7 @@ class WpVersion < Vulnerable
|
||||
# the generator tag in the Atom source.
|
||||
def self.find_from_atom_generator(options)
|
||||
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]
|
||||
end
|
||||
@@ -109,7 +110,7 @@ class WpVersion < Vulnerable
|
||||
# Have not been able to find an example of this - Ryan
|
||||
#def self.find_from_comments_rss_generator(options)
|
||||
# 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]
|
||||
#end
|
||||
@@ -129,7 +130,7 @@ class WpVersion < Vulnerable
|
||||
config.noblanks
|
||||
end
|
||||
|
||||
xml.xpath("//file").each do |node|
|
||||
xml.xpath('//file').each do |node|
|
||||
wp_content = options[:wp_content_dir]
|
||||
wp_plugins = "#{wp_content}/plugins"
|
||||
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.
|
||||
def self.find_from_readme(options)
|
||||
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
|
||||
|
||||
# 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
|
||||
def self.find_from_sitemap_generator(options)
|
||||
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
|
||||
|
||||
# Attempts to find the WordPress version from the p-links-opml.php file.
|
||||
def self.find_from_links_opml(options)
|
||||
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
|
||||
|
||||
# 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
|
||||
# Copyright (C) 2012-2013
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -18,95 +19,95 @@
|
||||
|
||||
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
|
||||
def usage()
|
||||
def usage
|
||||
script_name = $0
|
||||
puts
|
||||
puts "Examples :"
|
||||
puts 'Examples :'
|
||||
puts
|
||||
puts "-Further help ..."
|
||||
puts '-Further help ...'
|
||||
puts "ruby #{script_name} --help"
|
||||
puts
|
||||
puts "-Do 'non-intrusive' checks ..."
|
||||
puts "ruby #{script_name} --url www.example.com"
|
||||
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
|
||||
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
|
||||
puts "-Enumerate installed plugins ..."
|
||||
puts '-Enumerate installed plugins ...'
|
||||
puts "ruby #{script_name} --url www.example.com --enumerate p"
|
||||
puts
|
||||
puts "-Enumerate installed themes ..."
|
||||
puts '-Enumerate installed themes ...'
|
||||
puts "ruby #{script_name} --url www.example.com --enumerate t"
|
||||
puts
|
||||
puts "-Enumerate users ..."
|
||||
puts '-Enumerate users ...'
|
||||
puts "ruby #{script_name} --url www.example.com --enumerate u"
|
||||
puts
|
||||
puts "-Enumerate installed timthumbs ..."
|
||||
puts '-Enumerate installed timthumbs ...'
|
||||
puts "ruby #{script_name} --url www.example.com --enumerate tt"
|
||||
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
|
||||
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
|
||||
puts "-Use custom content directory ..."
|
||||
puts '-Use custom content directory ...'
|
||||
puts "ruby #{script_name} -u www.example.com --wp-content-dir custom-content"
|
||||
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
|
||||
puts "-Update ..."
|
||||
puts '-Update ...'
|
||||
puts "ruby #{script_name} --update"
|
||||
puts
|
||||
puts "See README for further information."
|
||||
puts 'See README for further information.'
|
||||
puts
|
||||
end
|
||||
|
||||
# command help
|
||||
def help()
|
||||
puts "Help :"
|
||||
def help
|
||||
puts 'Help :'
|
||||
puts
|
||||
puts "Some values are settable in conf/browser.conf.json :"
|
||||
puts " user-agent, proxy, proxy-auth, threads, cache timeout and request timeout"
|
||||
puts 'Some values are settable in conf/browser.conf.json :'
|
||||
puts ' user-agent, proxy, proxy-auth, threads, cache timeout and request timeout'
|
||||
puts
|
||||
puts "--update Update to the latest revision"
|
||||
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 "--enumerate | -e [option(s)] Enumeration."
|
||||
puts " option :"
|
||||
puts " u usernames from id 1 to 10"
|
||||
puts " u[10-20] usernames from id 10 to 20 (you must write [] chars)"
|
||||
puts " p plugins"
|
||||
puts " vp only vulnerable plugins"
|
||||
puts " ap all plugins (can take a long time)"
|
||||
puts " tt timthumbs"
|
||||
puts " t themes"
|
||||
puts " vt only vulnerable themes"
|
||||
puts " at all themes (can take a long time)"
|
||||
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 '--update Update to the latest revision'
|
||||
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 '--enumerate | -e [option(s)] Enumeration.'
|
||||
puts ' option :'
|
||||
puts ' u usernames from id 1 to 10'
|
||||
puts ' u[10-20] usernames from id 10 to 20 (you must write [] chars)'
|
||||
puts ' p plugins'
|
||||
puts ' vp only vulnerable plugins'
|
||||
puts ' ap all plugins (can take a long time)'
|
||||
puts ' tt timthumbs'
|
||||
puts ' t themes'
|
||||
puts ' vt only vulnerable themes'
|
||||
puts ' at all themes (can take a long time)'
|
||||
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
|
||||
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 "--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 "--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 "--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 "--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 "--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 "--username | -U <username> Only brute force the supplied username."
|
||||
puts "--help | -h This help screen."
|
||||
puts "--verbose | -v Verbose output."
|
||||
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 '--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 '--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 '--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 '--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 '--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 '--username | -U <username> Only brute force the supplied username.'
|
||||
puts '--help | -h This help screen.'
|
||||
puts '--verbose | -v Verbose output.'
|
||||
puts
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# encoding: UTF-8
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012-2013
|
||||
@@ -56,7 +57,7 @@ class WpscanOptions
|
||||
end
|
||||
|
||||
def url=(url)
|
||||
raise "Empty URL given" if !url
|
||||
raise 'Empty URL given' if !url
|
||||
|
||||
@url = URI.parse(add_http_protocol(url)).to_s
|
||||
end
|
||||
@@ -75,7 +76,7 @@ class WpscanOptions
|
||||
|
||||
def proxy=(proxy)
|
||||
if proxy.index(':') == nil
|
||||
raise "Invalid proxy format. Should be host:port."
|
||||
raise 'Invalid proxy format. Should be host:port.'
|
||||
else
|
||||
@proxy = proxy
|
||||
end
|
||||
@@ -83,7 +84,7 @@ class WpscanOptions
|
||||
|
||||
def proxy_auth=(auth)
|
||||
if auth.index(':') == nil
|
||||
raise "Invalid proxy auth format, username:password expected"
|
||||
raise 'Invalid proxy auth format, username:password expected'
|
||||
else
|
||||
@proxy_auth = auth
|
||||
end
|
||||
@@ -91,7 +92,7 @@ class WpscanOptions
|
||||
|
||||
def enumerate_plugins=(enumerate_plugins)
|
||||
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
|
||||
@enumerate_plugins = enumerate_plugins
|
||||
end
|
||||
@@ -99,7 +100,7 @@ class WpscanOptions
|
||||
|
||||
def enumerate_only_vulnerable_plugins=(enumerate_only_vulnerable_plugins)
|
||||
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
|
||||
@enumerate_only_vulnerable_plugins = enumerate_only_vulnerable_plugins
|
||||
end
|
||||
@@ -107,7 +108,7 @@ class WpscanOptions
|
||||
|
||||
def enumerate_all_plugins=(enumerate_all_plugins)
|
||||
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
|
||||
@enumerate_all_plugins = enumerate_all_plugins
|
||||
end
|
||||
@@ -115,7 +116,7 @@ class WpscanOptions
|
||||
|
||||
def enumerate_themes=(enumerate_themes)
|
||||
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
|
||||
@enumerate_themes = enumerate_themes
|
||||
end
|
||||
@@ -123,7 +124,7 @@ class WpscanOptions
|
||||
|
||||
def enumerate_only_vulnerable_themes=(enumerate_only_vulnerable_themes)
|
||||
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
|
||||
@enumerate_only_vulnerable_themes = enumerate_only_vulnerable_themes
|
||||
end
|
||||
@@ -131,14 +132,14 @@ class WpscanOptions
|
||||
|
||||
def enumerate_all_themes=(enumerate_all_themes)
|
||||
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
|
||||
@enumerate_all_themes = enumerate_all_themes
|
||||
end
|
||||
end
|
||||
|
||||
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}"
|
||||
end
|
||||
|
||||
@@ -183,9 +184,9 @@ class WpscanOptions
|
||||
WpscanOptions.option_to_instance_variable_setter(cli_option),
|
||||
cli_value
|
||||
)
|
||||
elsif cli_option === "--enumerate" # Special cases
|
||||
elsif cli_option === '--enumerate' # Special cases
|
||||
# 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)
|
||||
else
|
||||
@@ -231,23 +232,23 @@ class WpscanOptions
|
||||
# Even if a short option is given (IE : -u), the long one will be returned (IE : --url)
|
||||
def self.get_opt_long
|
||||
GetoptLong.new(
|
||||
["--url", "-u", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--enumerate", "-e", GetoptLong::OPTIONAL_ARGUMENT],
|
||||
["--username", "-U", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--wordlist", "-w", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--threads", "-t", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--force", "-f", GetoptLong::NO_ARGUMENT],
|
||||
["--help", "-h", GetoptLong::NO_ARGUMENT],
|
||||
["--verbose", "-v", GetoptLong::NO_ARGUMENT],
|
||||
["--proxy", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--proxy-auth", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--update", GetoptLong::NO_ARGUMENT],
|
||||
["--follow-redirection", GetoptLong::NO_ARGUMENT],
|
||||
["--wp-content-dir", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--wp-plugins-dir", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--config-file", "-c", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--exclude-content-based", GetoptLong::REQUIRED_ARGUMENT],
|
||||
["--basic-auth", GetoptLong::REQUIRED_ARGUMENT]
|
||||
['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--enumerate', '-e', GetoptLong::OPTIONAL_ARGUMENT],
|
||||
['--username', '-U', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--wordlist', '-w', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--threads', '-t', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--force', '-f', GetoptLong::NO_ARGUMENT],
|
||||
['--help', '-h', GetoptLong::NO_ARGUMENT],
|
||||
['--verbose', '-v', GetoptLong::NO_ARGUMENT],
|
||||
['--proxy', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--proxy-auth', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--update', GetoptLong::NO_ARGUMENT],
|
||||
['--follow-redirection', GetoptLong::NO_ARGUMENT],
|
||||
['--wp-content-dir', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--wp-plugins-dir', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--config-file', '-c', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--exclude-content-based', GetoptLong::REQUIRED_ARGUMENT],
|
||||
['--basic-auth', GetoptLong::REQUIRED_ARGUMENT]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user