diff --git a/Gemfile b/Gemfile
index 26d9a044..b27aafd4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,7 @@
source "https://rubygems.org"
gem "typhoeus", ">=0.6.2"
+gem "ethon", :git => "https://github.com/typhoeus/ethon.git"
gem "nokogiri"
gem "json"
diff --git a/lib/common/browser.rb b/lib/common/browser.rb
index eb5e8349..767aa492 100644
--- a/lib/common/browser.rb
+++ b/lib/common/browser.rb
@@ -1,8 +1,11 @@
# encoding: UTF-8
require 'common/typhoeus_cache'
+require 'common/browser/actions'
class Browser
+ extend Browser::Actions
+
@@instance = nil
USER_AGENT_MODES = %w{ static semi-static random }
@@ -122,26 +125,6 @@ class Browser
end
end
- def get(url, params = {})
- run_request(
- forge_request(url, params.merge(method: :get))
- )
- end
-
- def post(url, params = {})
- run_request(
- forge_request(url, params.merge(method: :post))
- )
- end
-
- def get_and_follow_location(url, params = {})
- params[:maxredirs] ||= 2
-
- run_request(
- forge_request(url, params.merge(method: :get, followlocation: true))
- )
- end
-
def forge_request(url, params = {})
Typhoeus::Request.new(
url.to_s,
@@ -181,12 +164,19 @@ class Browser
params.merge!(ssl_verifypeer: false)
params.merge!(ssl_verifyhost: 0)
- params.merge!(cookie_jar: @cache_dir + '/cookie-jar')
- params.merge!(cookie_file: @cache_dir + '/cookie-jar')
+ params.merge!(cookiejar: @cache_dir + '/cookie-jar')
+ params.merge!(cookiefile: @cache_dir + '/cookie-jar')
params
end
+ # return the response
+ def run_request(request)
+ @hydra.queue request
+ @hydra.run
+ request.response
+ end
+
private
# return Array
@@ -199,13 +189,6 @@ class Browser
params
end
- # return the response
- def run_request(request)
- @hydra.queue request
- @hydra.run
- request.response
- end
-
# Override with the options if they are set
def override_config_with_options(options)
options.each do |option, value|
@@ -214,4 +197,5 @@ class Browser
end
end
end
+
end
diff --git a/lib/common/browser/actions.rb b/lib/common/browser/actions.rb
new file mode 100644
index 00000000..bb3ddde2
--- /dev/null
+++ b/lib/common/browser/actions.rb
@@ -0,0 +1,49 @@
+# encoding: UTF-8
+
+class Browser
+ module Actions
+
+ # @param [ String ] url
+ # @param [ Hash ] params
+ #
+ # @return [ Typhoeus::Response ]
+ def get(url, params = {})
+ #Typhoeus.get(url, Browser.instance.merge_request_params(params))
+ process(url, params.merge(method: :get))
+ end
+
+ # @param [ String ] url
+ # @param [ Hash ] params
+ #
+ # @return [ Typhoeus::Response ]
+ def post(url, params = {})
+ #Typhoeus.post(url, Browser.instance.merge_request_params(params))
+ process(url, params.merge(method: :post))
+ end
+
+ # @param [ String ] url
+ # @param [ Hash ] params
+ #
+ # @return [ Typhoeus::Response ]
+ def get_and_follow_location(url, params = {})
+ params[:maxredirs] ||= 2
+
+ get(url, params.merge(followlocation: true))
+ end
+
+ protected
+
+ # @param [ String ] url
+ # @param [ Hash ] params
+ #
+ # @return [ Typhoeus::Response ]
+ def process(url, params)
+ browser = Browser.instance
+
+ browser.run_request(
+ browser.forge_request(url, params)
+ )
+ end
+
+ end
+end
diff --git a/lib/common/browser/options.rb b/lib/common/browser/options.rb
new file mode 100644
index 00000000..a663802a
--- /dev/null
+++ b/lib/common/browser/options.rb
@@ -0,0 +1,7 @@
+# encoding: UTF-8
+
+class Browser
+ module Options
+
+ end
+end
diff --git a/lib/common/collections/wp_items/detectable.rb b/lib/common/collections/wp_items/detectable.rb
index 4a56bf9a..f53353c6 100755
--- a/lib/common/collections/wp_items/detectable.rb
+++ b/lib/common/collections/wp_items/detectable.rb
@@ -67,7 +67,7 @@ class WpItems < Array
results = new
item_class = self.item_class
type = self.to_s.gsub(/Wp/, '').downcase
- response = Browser.instance.get(wp_target.url)
+ response = Browser.get(wp_target.url)
item_options = {
wp_content_dir: wp_target.wp_content_dir,
wp_plugins_dir: wp_target.wp_plugins_dir,
diff --git a/lib/common/hacks.rb b/lib/common/hacks.rb
index 98dcfbbb..c4717d90 100644
--- a/lib/common/hacks.rb
+++ b/lib/common/hacks.rb
@@ -47,20 +47,6 @@ module Typhoeus
end
end
-module Ethon
- class Easy
- module Options
- def cookie_jar=(value)
- Curl.set_option(:cookiejar, value_for(value, :string), handle)
- end
-
- def cookie_file=(value)
- Curl.set_option(:cookiefile, value_for(value, :string), handle)
- end
- end
- end
-end
-
# Override for puts to enable logging
def puts(o = '')
# remove color for logging
diff --git a/lib/common/models/wp_item/existable.rb b/lib/common/models/wp_item/existable.rb
index 0daa45d0..372607a2 100755
--- a/lib/common/models/wp_item/existable.rb
+++ b/lib/common/models/wp_item/existable.rb
@@ -13,7 +13,7 @@ class WpItem
# @return [ Boolean ]
def exists?(options = {}, response = nil)
unless response
- response = Browser.instance.get(url)
+ response = Browser.get(url)
end
exists_from_response?(response, options)
end
diff --git a/lib/common/models/wp_item/infos.rb b/lib/common/models/wp_item/infos.rb
index e4c1f819..dce401a3 100644
--- a/lib/common/models/wp_item/infos.rb
+++ b/lib/common/models/wp_item/infos.rb
@@ -7,7 +7,7 @@ class WpItem
# @return [ Boolean ]
def has_readme?
- Browser.instance.get(readme_url).code == 200 ? true : false
+ Browser.get(readme_url).code == 200 ? true : false
end
# @return [ String ] The url to the readme file
@@ -17,7 +17,7 @@ class WpItem
# @return [ Boolean ]
def has_changelog?
- Browser.instance.get(changelog_url).code == 200 ? true : false
+ Browser.get(changelog_url).code == 200 ? true : false
end
# @return [ String ] The url to the changelog file
@@ -27,7 +27,7 @@ class WpItem
# @return [ Boolean ]
def has_directory_listing?
- Browser.instance.get(@uri.to_s).body[%r{
Index of}] ? true : false
+ Browser.get(@uri.to_s).body[%r{Index of}] ? true : false
end
# Discover any error_log files created by WordPress
@@ -41,7 +41,7 @@ class WpItem
#
# @return [ Boolean ]
def has_error_log?
- response_body = Browser.instance.get(error_log_url, headers: {'range' => 'bytes=0-700'}).body
+ response_body = Browser.get(error_log_url, headers: {'range' => 'bytes=0-700'}).body
response_body[%r{PHP Fatal error}i] ? true : false
end
diff --git a/lib/common/models/wp_item/versionable.rb b/lib/common/models/wp_item/versionable.rb
index d1b70595..6370e550 100755
--- a/lib/common/models/wp_item/versionable.rb
+++ b/lib/common/models/wp_item/versionable.rb
@@ -10,7 +10,7 @@ class WpItem
# @return [ String ] The version number
def version
unless @version
- response = Browser.instance.get(readme_url)
+ response = Browser.get(readme_url)
@version = response.body[%r{stable tag: #{WpVersion.version_pattern}}i, 1]
end
@version
diff --git a/lib/common/models/wp_theme/findable.rb b/lib/common/models/wp_theme/findable.rb
index 1ef6c32e..8ebc6056 100755
--- a/lib/common/models/wp_theme/findable.rb
+++ b/lib/common/models/wp_theme/findable.rb
@@ -27,7 +27,7 @@ class WpTheme < WpItem
#
# @return [ WpTheme ]
def find_from_css_link(target_uri)
- response = Browser.instance.get_and_follow_location(target_uri.to_s)
+ response = Browser.get_and_follow_location(target_uri.to_s)
# https + domain is optional because of relative links
matches = %r{(?:https?://[^"']+)?/([^/]+)/themes/([^"']+)/style.css}i.match(response.body)
@@ -49,7 +49,7 @@ class WpTheme < WpItem
#
# @return [ WpTheme ]
def find_from_wooframework(target_uri)
- body = Browser.instance.get(target_uri.to_s).body
+ body = Browser.get(target_uri.to_s).body
regexp = %r{\s+}
diff --git a/lib/common/models/wp_theme/versionable.rb b/lib/common/models/wp_theme/versionable.rb
index 5c548747..d0d07d25 100755
--- a/lib/common/models/wp_theme/versionable.rb
+++ b/lib/common/models/wp_theme/versionable.rb
@@ -5,7 +5,7 @@ class WpTheme < WpItem
def version
unless @version
- @version = Browser.instance.get(style_url).body[%r{Version:\s([^\s]+)}i, 1]
+ @version = Browser.get(style_url).body[%r{Version:\s([^\s]+)}i, 1]
# Get Version from readme.txt
@version ||= super
diff --git a/lib/common/models/wp_timthumb/versionable.rb b/lib/common/models/wp_timthumb/versionable.rb
index 95f5a9b4..d570966f 100755
--- a/lib/common/models/wp_timthumb/versionable.rb
+++ b/lib/common/models/wp_timthumb/versionable.rb
@@ -9,7 +9,7 @@ class WpTimthumb < WpItem
# @return [ String ] The version
def version
unless @version
- response = Browser.instance.get(url)
+ response = Browser.get(url)
@version = response.body[%r{TimThumb version\s*: ([^<]+)} , 1]
end
@version
diff --git a/lib/common/models/wp_user/existable.rb b/lib/common/models/wp_user/existable.rb
index 924f3981..9fafcdd4 100755
--- a/lib/common/models/wp_user/existable.rb
+++ b/lib/common/models/wp_user/existable.rb
@@ -24,7 +24,7 @@ class WpUser < WpItem
@login = Existable.login_from_author_pattern(location)
@display_name = Existable.display_name_from_body(
- Browser.instance.get(location).body
+ Browser.get(location).body
)
elsif response.code == 200 # login in body?
@login = Existable.login_from_body(response.body)
diff --git a/lib/common/models/wp_version/findable.rb b/lib/common/models/wp_version/findable.rb
index df71f65c..87c29c36 100755
--- a/lib/common/models/wp_version/findable.rb
+++ b/lib/common/models/wp_version/findable.rb
@@ -45,7 +45,7 @@ class WpVersion < WpItem
# @return [ String ]
def scan_url(target_uri, pattern, path = nil)
url = path ? target_uri.merge(path).to_s : target_uri.to_s
- response = Browser.instance.get_and_follow_location(url)
+ response = Browser.get_and_follow_location(url)
response.body[pattern, 1]
end
@@ -163,7 +163,7 @@ class WpVersion < WpItem
xml.xpath('//file').each do |node|
wp_item.path = node.attribute('src').text
- response = Browser.instance.get(wp_item.url)
+ response = Browser.get(wp_item.url)
md5sum = Digest::MD5.hexdigest(response.body)
node.search('hash').each do |hash|
diff --git a/lib/wpscan/web_site.rb b/lib/wpscan/web_site.rb
index bce5b41f..a0c03cc8 100644
--- a/lib/wpscan/web_site.rb
+++ b/lib/wpscan/web_site.rb
@@ -18,11 +18,11 @@ class WebSite
# Checks if the remote website is up.
def online?
- Browser.instance.get(@uri.to_s).code != 0
+ Browser.get(@uri.to_s).code != 0
end
def has_basic_auth?
- Browser.instance.get(@uri.to_s).code == 401
+ Browser.get(@uri.to_s).code == 401
end
def has_xml_rpc?
@@ -38,7 +38,7 @@ class WebSite
end
def xml_rpc_url_from_headers
- headers = Browser.instance.get(@uri.to_s).headers_hash
+ headers = Browser.get(@uri.to_s).headers_hash
xmlrpc_url = nil
unless headers.nil?
@@ -51,7 +51,7 @@ class WebSite
end
def xml_rpc_url_from_body
- body = Browser.instance.get(@uri.to_s).body
+ body = Browser.get(@uri.to_s).body
body[%r{}, 1]
end
@@ -62,7 +62,7 @@ class WebSite
def redirection(url = nil)
redirection = nil
url ||= @uri.to_s
- response = Browser.instance.get(url)
+ response = Browser.get(url)
if response.code == 301 || response.code == 302
redirection = response.headers_hash['location']
@@ -78,7 +78,7 @@ class WebSite
# Return the MD5 hash of the page given by url
def self.page_hash(url)
- Digest::MD5.hexdigest(Browser.instance.get(url).body)
+ Digest::MD5.hexdigest(Browser.get(url).body)
end
def homepage_hash
@@ -100,13 +100,13 @@ class WebSite
# Will try to find the rss url in the homepage
# Only the first one found iw returned
def rss_url
- homepage_body = Browser.instance.get(@uri.to_s).body
+ homepage_body = Browser.get(@uri.to_s).body
homepage_body[%r{}, 1]
end
# Checks if a robots.txt file exists
def has_robots?
- Browser.instance.get(robots_url).code == 200
+ Browser.get(robots_url).code == 200
end
# Gets a robots.txt URL
diff --git a/lib/wpscan/wp_target.rb b/lib/wpscan/wp_target.rb
index 4dd7a33e..21d41a28 100644
--- a/lib/wpscan/wp_target.rb
+++ b/lib/wpscan/wp_target.rb
@@ -11,14 +11,14 @@ require 'wp_target/wp_custom_directories'
require 'wp_target/wp_full_path_disclosure'
class WpTarget < WebSite
- include Malwares
- include WpReadme
- include BruteForce
- include WpRegistrable
- include WpConfigBackup
- include WpLoginProtection
- include WpCustomDirectories
- include WpFullPathDisclosure
+ include WpTarget::Malwares
+ include WpTarget::WpReadme
+ include WpTarget::BruteForce
+ include WpTarget::WpRegistrable
+ include WpTarget::WpConfigBackup
+ include WpTarget::WpLoginProtection
+ include WpTarget::WpCustomDirectories
+ include WpTarget::WpFullPathDisclosure
attr_reader :verbose
@@ -38,17 +38,17 @@ class WpTarget < WebSite
def wordpress?
wordpress = false
- response = Browser.instance.get_and_follow_location(@uri.to_s)
+ response = Browser.get_and_follow_location(@uri.to_s)
if response.body =~ /["'][^"']*\/wp-content\/[^"']*["']/i
wordpress = true
else
- response = Browser.instance.get_and_follow_location(xml_rpc_url)
+ response = Browser.get_and_follow_location(xml_rpc_url)
if response.body =~ %r{XML-RPC server accepts POST requests only}i
wordpress = true
else
- response = Browser.instance.get_and_follow_location(login_url)
+ response = Browser.get_and_follow_location(login_url)
if response.code == 200 && response.body =~ %r{WordPress}i
wordpress = true
@@ -104,7 +104,7 @@ class WpTarget < WebSite
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.get(debug_log_url(), headers: {'range' => 'bytes=0-700'}).body
response_body[%r{\[[^\]]+\] PHP (?:Warning|Error|Notice):}] ? true : false
end
@@ -120,7 +120,7 @@ class WpTarget < WebSite
end
def search_replace_db_2_exists?
- resp = Browser.instance.get(search_replace_db_2_url)
+ resp = Browser.get(search_replace_db_2_url)
resp.code == 200 && resp.body[%r{by interconnect}i]
end
end
diff --git a/lib/wpscan/wp_target/malwares.rb b/lib/wpscan/wp_target/malwares.rb
index dd554393..0fcb5223 100644
--- a/lib/wpscan/wp_target/malwares.rb
+++ b/lib/wpscan/wp_target/malwares.rb
@@ -17,7 +17,7 @@ class WpTarget < WebSite
unless @malwares
malwares_found = []
malwares_file = Malwares.malwares_file(malwares_file_path)
- index_page_body = Browser.instance.get(@uri.to_s).body
+ index_page_body = Browser.get(@uri.to_s).body
File.open(malwares_file, 'r') do |file|
file.readlines.collect do |url|
diff --git a/lib/wpscan/wp_target/wp_custom_directories.rb b/lib/wpscan/wp_target/wp_custom_directories.rb
index c864ccdb..1e60791a 100644
--- a/lib/wpscan/wp_target/wp_custom_directories.rb
+++ b/lib/wpscan/wp_target/wp_custom_directories.rb
@@ -6,7 +6,7 @@ class WpTarget < WebSite
# @return [ String ] The wp-content directory
def wp_content_dir
unless @wp_content_dir
- index_body = Browser.instance.get(@uri.to_s).body
+ index_body = Browser.get(@uri.to_s).body
uri_path = @uri.path # Only use the path because domain can be text or an IP
if index_body[/\/wp-content\/(?:themes|plugins)\//i] || default_wp_content_dir_exists?
@@ -22,7 +22,7 @@ class WpTarget < WebSite
# @return [ Boolean ]
def default_wp_content_dir_exists?
- response = Browser.instance.get(@uri.merge('wp-content').to_s)
+ response = Browser.get(@uri.merge('wp-content').to_s)
hash = Digest::MD5.hexdigest(response.body)
if WpTarget.valid_response_codes.include?(response.code)
@@ -42,7 +42,7 @@ class WpTarget < WebSite
# @return [ Boolean ]
def wp_plugins_dir_exists?
- Browser.instance.get(@uri.merge(wp_plugins_dir)).code != 404
+ Browser.get(@uri.merge(wp_plugins_dir).to_s).code != 404
end
end
diff --git a/lib/wpscan/wp_target/wp_full_path_disclosure.rb b/lib/wpscan/wp_target/wp_full_path_disclosure.rb
index dcad94ab..2e97b404 100644
--- a/lib/wpscan/wp_target/wp_full_path_disclosure.rb
+++ b/lib/wpscan/wp_target/wp_full_path_disclosure.rb
@@ -7,7 +7,7 @@ class WpTarget < WebSite
#
# @return [ Boolean ]
def has_full_path_disclosure?
- response = Browser.instance.get(full_path_disclosure_url())
+ response = Browser.get(full_path_disclosure_url())
response.body[%r{Fatal error}i] ? true : false
end
diff --git a/lib/wpscan/wp_target/wp_login_protection.rb b/lib/wpscan/wp_target/wp_login_protection.rb
index fda54fe1..4aeac5d0 100644
--- a/lib/wpscan/wp_target/wp_login_protection.rb
+++ b/lib/wpscan/wp_target/wp_login_protection.rb
@@ -38,17 +38,17 @@ class WpTarget < WebSite
# Thanks to Alip Aswalid for providing this method.
# http://wordpress.org/extend/plugins/login-lockdown/
def has_login_lockdown_protection?
- Browser.instance.get(login_url).body =~ %r{Login LockDown}i ? true : false
+ Browser.get(login_url).body =~ %r{Login LockDown}i ? true : false
end
# http://wordpress.org/extend/plugins/login-lock/
def has_login_lock_protection?
- Browser.instance.get(login_url).body =~ %r{LOGIN LOCK} ? true : false
+ Browser.get(login_url).body =~ %r{LOGIN LOCK} ? true : false
end
# http://wordpress.org/extend/plugins/better-wp-security/
def has_better_wp_security_protection?
- Browser.instance.get(better_wp_security_url).code != 404
+ Browser.get(better_wp_security_url).code != 404
end
def plugin_url(plugin_name)
@@ -66,7 +66,7 @@ class WpTarget < WebSite
# http://wordpress.org/extend/plugins/simple-login-lockdown/
def has_simple_login_lockdown_protection?
- Browser.instance.get(simple_login_lockdown_url).code != 404
+ Browser.get(simple_login_lockdown_url).code != 404
end
def simple_login_lockdown_url
@@ -75,7 +75,7 @@ class WpTarget < WebSite
# http://wordpress.org/extend/plugins/login-security-solution/
def has_login_security_solution_protection?
- Browser.instance.get(login_security_solution_url()).code != 404
+ Browser.get(login_security_solution_url()).code != 404
end
def login_security_solution_url
@@ -84,7 +84,7 @@ class WpTarget < WebSite
# http://wordpress.org/extend/plugins/limit-login-attempts/
def has_limit_login_attempts_protection?
- Browser.instance.get(limit_login_attempts_url).code != 404
+ Browser.get(limit_login_attempts_url).code != 404
end
def limit_login_attempts_url
@@ -93,7 +93,7 @@ class WpTarget < WebSite
# http://wordpress.org/extend/plugins/bluetrait-event-viewer/
def has_bluetrait_event_viewer_protection?
- Browser.instance.get(bluetrait_event_viewer_url).code != 404
+ Browser.get(bluetrait_event_viewer_url).code != 404
end
def bluetrait_event_viewer_url
diff --git a/lib/wpscan/wp_target/wp_readme.rb b/lib/wpscan/wp_target/wp_readme.rb
index 9ff9619a..b3b7fae8 100644
--- a/lib/wpscan/wp_target/wp_readme.rb
+++ b/lib/wpscan/wp_target/wp_readme.rb
@@ -10,7 +10,7 @@ class WpTarget < WebSite
#
# @return [ Boolean ]
def has_readme?
- response = Browser.instance.get(readme_url())
+ response = Browser.get(readme_url())
unless response.code == 404
return response.body =~ %r{wordpress}i ? true : false
diff --git a/lib/wpscan/wp_target/wp_registrable.rb b/lib/wpscan/wp_target/wp_registrable.rb
index c8aaf344..72c4c307 100644
--- a/lib/wpscan/wp_target/wp_registrable.rb
+++ b/lib/wpscan/wp_target/wp_registrable.rb
@@ -7,7 +7,7 @@ class WpTarget < WebSite
#
# @return [ Boolean ]
def registration_enabled?
- resp = Browser.instance.get(registration_url)
+ resp = Browser.get(registration_url)
# redirect only on non multi sites
if resp.code == 302 and resp.headers_hash['location'] =~ /wp-login\.php\?registration=disabled/i
enabled = false
@@ -34,8 +34,7 @@ class WpTarget < WebSite
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')
- resp = Browser.instance.get(url)
+ resp = Browser.get(@uri.merge('wp-signup.php').to_s)
if resp.code == 302 and resp.headers_hash['location'] =~ /wp-login\.php\?action=register/
@multisite = false
diff --git a/wpscan.rb b/wpscan.rb
index 952d8c27..9078601e 100755
--- a/wpscan.rb
+++ b/wpscan.rb
@@ -48,7 +48,7 @@ def main
end
if wpscan_options.proxy
- proxy_response = Browser.instance.get(wp_target.url)
+ proxy_response = Browser.get(wp_target.url)
unless WpTarget::valid_response_codes.include?(proxy_response.code)
raise "Proxy Error :\r\n#{proxy_response.headers}"