-) custom plugins directory (outside of wp-content)

-) feedback from ewanlr
-) Regex fix for version detection from readme.txt due to false positives (tag-cloud-widget plugin)
This commit is contained in:
Christian Mehlmauer
2012-09-23 21:50:41 +02:00
parent 9b6a2805d7
commit a15028793e
46 changed files with 560 additions and 415 deletions

View File

@@ -38,7 +38,7 @@ module WpLoginProtection
return @login_protection_plugin = WpPlugin.new(
:name => plugin_name,
:url => @uri,
:base_url => @uri,
:path => "/plugins/#{plugin_name}/",
:wp_content_dir => @wp_content_dir
)
@@ -68,7 +68,7 @@ module WpLoginProtection
def better_wp_security_url
WpPlugin.new(:wp_content_dir => @wp_content_dir,
:url => @uri,
:base_url => @uri,
:path => "/plugins/better-wp-security/",
:name => "better-wp-security"
).get_url_without_filename
@@ -81,7 +81,7 @@ module WpLoginProtection
def simple_login_lockdown_url
WpPlugin.new(:wp_content_dir => @wp_content_dir,
:url => @uri,
:base_url => @uri,
:path => "/plugins/simple-login-lockdown/",
:name => "simple-login-lockdown"
).get_url_without_filename
@@ -94,7 +94,7 @@ module WpLoginProtection
def login_security_solution_url
WpPlugin.new(:wp_content_dir => @wp_content_dir,
:url => @uri,
:base_url => @uri,
:path => "/plugins/login-security-solution/",
:name => "login-security-solution"
).get_url_without_filename
@@ -107,7 +107,7 @@ module WpLoginProtection
def limit_login_attempts_url
WpPlugin.new(:wp_content_dir => @wp_content_dir,
:url => @uri,
:base_url => @uri,
:path => "/plugins/limit-login-attempts/",
:name => "limit-login-attempts"
).get_url_without_filename
@@ -120,7 +120,7 @@ module WpLoginProtection
def bluetrait_event_viewer_url
WpPlugin.new(:wp_content_dir => @wp_content_dir,
:url => @uri,
:base_url => @uri,
:path => "/plugins/bluetrait-event-viewer/",
:name => "bluetrait-event-viewer"
).get_url_without_filename

View File

@@ -31,12 +31,12 @@ module WpPlugins
plugins = []
result.each do |r|
plugins << WpPlugin.new(
:url => r.url,
: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_plugin_dir
:wp_plugins_dir => r.wp_plugins_dir
)
end
plugins.sort_by { |p| p.name }
@@ -50,16 +50,16 @@ module WpPlugins
# return array of WpPlugin
def plugins_from_passive_detection(options)
plugins = []
temp = WpDetector.passive_detection(options[: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(
:url => item.url,
: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_plugin_dir]
:wp_plugins_dir => options[:wp_plugins_dir]
)
end
plugins.sort_by { |p| p.name }

View File

@@ -29,7 +29,7 @@ module WpThemes
themes = []
result.each do |r|
themes << WpTheme.new(
:url => r.url,
:base_url => r.base_url,
:path => r.path,
:wp_content_dir => r.wp_content_dir,
:name => r.name
@@ -40,11 +40,11 @@ module WpThemes
def themes_from_passive_detection(options)
themes = []
temp = WpDetector.passive_detection(options[: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(
:url => item.url,
:base_url => item.base_url,
:name => item.name,
:path => item.path,
:wp_content_dir => options[:wp_content_dir]

View File

@@ -55,7 +55,7 @@ module WpTimthumbs
scripts/timthumb.php tools/timthumb.php functions/timthumb.php
}.each do |file|
targets << WpItem.new(
:url => options[:url],
:base_url => options[:base_url],
:path => "themes/#{theme_name}/#{file}",
:wp_content_dir => options[:wp_content_dir],
:name => theme_name,

View File

@@ -23,7 +23,7 @@ class WpDetector
result = items
if items == nil or items.length == 0
result = passive_detection(options[:url], options[:type], options[:wp_content_dir])
result = passive_detection(options[:base_url], options[:type], options[:wp_content_dir])
end
enum_results = WpEnumerator.enumerate(options)
@@ -61,7 +61,7 @@ class WpDetector
names.each do |item|
items << WpItem.new(
:url => url,
:base_url => url,
:name => item,
:type => type,
:path => "#{item}/",

View File

@@ -24,7 +24,7 @@ class WpEnumerator
# ==== Attributes
#
# * +targets+ - targets to enumerate
# * * +:url+ - Base URL
# * * +:base_url+ - Base URL
# * * +:wp_content+ - wp-content directory
# * * +:path+ - Path to plugin
# * +type+ - "plugins" or "themes", item to enumerate
@@ -50,7 +50,7 @@ class WpEnumerator
enumerate_size = targets.size
targets.each do |target|
url = target.get_url
url = target.get_full_url
request = enum_browser.forge_request(url, { :cache_timeout => 0, :follow_location => true })
request_count += 1
@@ -86,7 +86,7 @@ class WpEnumerator
file = options[:file]
vulns_file = options[:vulns_file]
wp_content_dir = options[:wp_content_dir]
url = options[:url]
url = options[:base_url]
type = options[:type]
plugins_dir = options[:wp_plugins_dir]
targets_url = []
@@ -96,7 +96,7 @@ class WpEnumerator
File.open(file, "r") do |f|
f.readlines.collect do |line|
targets_url << WpItem.new(
:url => url,
:base_url => url,
:path => line.strip,
:wp_content_dir => wp_content_dir,
:name => File.dirname(line.strip),
@@ -118,7 +118,7 @@ class WpEnumerator
xml.xpath(options[:vulns_xpath_2]).each do |node|
name = node.attribute("name").text
targets_url << WpItem.new(
:url => url,
:base_url => url,
:path => name,
:wp_content_dir => wp_content_dir,
:name => name,

View File

@@ -19,20 +19,20 @@
require "#{WPSCAN_LIB_DIR}/vulnerable"
class WpItem < Vulnerable
attr_accessor :path, :url, :wp_content_dir, :name, :vulns_file, :vulns_xpath, :wp_plugin_dir, :type
attr_reader :base_url, :path, :wp_content_dir, :name, :vulns_file, :vulns_xpath, :wp_plugins_dir, :type
@version = nil
def initialize(options)
@type = options[:type]
@wp_content_dir = options[:wp_content_dir] || "wp-content"
@wp_plugin_dir = options[:wp_plugins_dir] || "plugins"
@url = options[:url]
@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]
@name = options[:name] || extract_name_from_url
@vulns_file = options[:vulns_file]
@vulns_xpath = options[:vulns_xpath].sub(/\$name\$/, @name) unless options[:vulns_xpath] == nil
raise("url not set") unless @url
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
@@ -42,8 +42,6 @@ class WpItem < Vulnerable
def get_sub_folder
case @type
when "plugins"
folder = @wp_plugin_dir
when "themes"
folder = "themes"
when "timthumbs"
@@ -56,13 +54,16 @@ class WpItem < Vulnerable
end
# Get the full url for this item
def get_url
url = @url.to_s.end_with?("/") ? @url.to_s : "#@url/"
def get_full_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(/\/$/, "")
# remove first /
path = @path.sub(/^\//, "")
if type == "timthumbs"
if type =="plugins"
# plugins can be outside of wp-content. wp_content_dir included in wp_plugins_dir
ret = URI.parse("#{url}#@wp_plugins_dir/#{path}")
elsif type == "timthumbs"
# timthumbs have folder in path variable
ret = URI.parse("#{url}#{wp_content_dir}/#{path}")
else
@@ -73,7 +74,7 @@ class WpItem < Vulnerable
# Gets the full url for this item without filenames
def get_url_without_filename
location_url = get_url.to_s
location_url = get_full_url.to_s
valid_location_url = location_url[%r{^(https?://.*/)[^.]+\.[^/]+$}, 1]
unless valid_location_url
valid_location_url = add_trailing_slash(location_url)
@@ -84,7 +85,7 @@ class WpItem < Vulnerable
# Returns version number from readme.txt if it exists
def version
unless @version
response = Browser.instance.get(get_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
@@ -98,7 +99,7 @@ class WpItem < Vulnerable
# Extract item name from a url
def extract_name_from_url
get_url.to_s[%r{^(https?://.*/([^/]+)/)}i, 2]
get_full_url.to_s[%r{^(https?://.*/([^/]+)/)}i, 2]
end
# To string. Adds a version number if detected

View File

@@ -32,7 +32,7 @@
# * +type+ - Type: plugins, themes
class WpOptions
def self.check_options(options)
raise("url must be set") unless options[:url] != nil and options[: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("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

View File

@@ -37,6 +37,6 @@ class WpPlugin < WpItem
end
def error_log_url
get_url.merge("error_log").to_s
get_full_url.merge("error_log").to_s
end
end

View File

@@ -104,7 +104,7 @@ class WpTarget
def wp_plugins_dir
unless @wp_plugins_dir
@wp_plugins_dir = "plugins"
@wp_plugins_dir = "#{wp_content_dir}/plugins"
end
@wp_plugins_dir
end

View File

@@ -20,7 +20,7 @@ require "#{WPSCAN_LIB_DIR}/vulnerable"
class WpTheme < WpItem
attr_reader :name, :style_url, :version
attr_reader :style_url, :version
def initialize(options = {})
options[:vulns_file] = (options[:vulns_file] != nil and options[:vulns_file] != "") ?
@@ -67,7 +67,7 @@ class WpTheme < WpItem
return new(:name => theme_name,
:style_url => style_url,
:url => style_url,
:base_url => style_url,
:path => "",
:wp_content_dir => ""
)
@@ -87,7 +87,7 @@ class WpTheme < WpItem
return new(:name => woo_theme_name,
:version => woo_theme_version,
:url => matches[0],
:base_url => matches[0],
:path => "",
:wp_content_dir => ""
)

View File

@@ -38,7 +38,7 @@ class WpVersion < Vulnerable
# (find_from_meta_generator, find_from_rss_generator etc)
def self.find(target_uri, wp_content_dir)
options = {
:url => target_uri,
:base_url => target_uri,
:wp_content_dir => wp_content_dir
}
self.methods.grep(/find_from_/).each do |method_to_call|
@@ -59,14 +59,14 @@ class WpVersion < Vulnerable
# The meta tag can be removed however it seems,
# that it is reinstated on upgrade.
def self.find_from_meta_generator(options)
target_uri = options[:url]
target_uri = options[:base_url]
response = Browser.instance.get(target_uri.to_s, {:follow_location => true, :max_redirects => 2})
response.body[%r{name="generator" content="wordpress ([^"]+)"}i, 1]
end
def self.find_from_rss_generator(options)
target_uri = options[:url]
target_uri = options[:base_url]
response = Browser.instance.get(target_uri.merge("feed/").to_s, {:follow_location => true, :max_redirects => 2})
response.body[%r{<generator>http://wordpress.org/\?v=([^<]+)</generator>}i, 1]
@@ -92,7 +92,7 @@ class WpVersion < Vulnerable
# /!\ Warning : this method might return false positive if the file used for fingerprinting is part of a theme (they can be updated)
#
def self.find_from_advanced_fingerprinting(options)
target_uri = options[:url]
target_uri = options[:base_url]
# needed for rpsec tests
version_xml = options[:version_xml] || DATA_DIR + "/wp_versions.xml"
xml = Nokogiri::XML(File.open(version_xml)) do |config|
@@ -117,18 +117,18 @@ class WpVersion < Vulnerable
end
def self.find_from_readme(options)
target_uri = options[: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]
end
# http://code.google.com/p/wpscan/issues/detail?id=109
def self.find_from_sitemap_generator(options)
target_uri = options[:url]
target_uri = options[:base_url]
Browser.instance.get(target_uri.merge("sitemap.xml").to_s).body[%r{generator="wordpress/#{WpVersion.version_pattern}"}, 1]
end
# Used to check if the version is correct : should be numeric with at least one '.'
# Used to check if the version is correct : must contain at least one .
def self.version_pattern
'(.*(?=.)(?=.*\d)(?=.*[.]).*)'
'([^\r\n]+[\.][^\r\n]+)'
end
end