Basic auth
This commit is contained in:
@@ -21,14 +21,15 @@ class Browser
|
||||
USER_AGENT_MODES = %w{ static semi-static random }
|
||||
|
||||
ACCESSOR_OPTIONS = [
|
||||
:user_agent,
|
||||
:user_agent_mode,
|
||||
:available_user_agents,
|
||||
:proxy,
|
||||
:proxy_auth,
|
||||
:max_threads,
|
||||
:cache_timeout,
|
||||
:request_timeout
|
||||
:user_agent,
|
||||
:user_agent_mode,
|
||||
:available_user_agents,
|
||||
:proxy,
|
||||
:proxy_auth,
|
||||
:max_threads,
|
||||
:cache_timeout,
|
||||
:request_timeout,
|
||||
:basic_auth
|
||||
]
|
||||
|
||||
attr_reader :hydra, :config_file
|
||||
@@ -36,6 +37,7 @@ class Browser
|
||||
|
||||
def initialize(options = {})
|
||||
@config_file = options[:config_file] || CONF_DIR + '/browser.conf.json'
|
||||
#@basic_auth = options[:basic_auth]
|
||||
options.delete(:config_file)
|
||||
|
||||
load_config()
|
||||
@@ -138,9 +140,9 @@ class Browser
|
||||
def setup_cache_handlers
|
||||
@hydra.cache_setter do |request|
|
||||
@cache.write_entry(
|
||||
Browser.generate_cache_key_from_request(request),
|
||||
request.response,
|
||||
request.cache_timeout
|
||||
Browser.generate_cache_key_from_request(request),
|
||||
request.response,
|
||||
request.cache_timeout
|
||||
)
|
||||
end
|
||||
|
||||
@@ -153,20 +155,20 @@ class Browser
|
||||
|
||||
def get(url, params = {})
|
||||
run_request(
|
||||
forge_request(url, params.merge(:method => :get))
|
||||
forge_request(url, params.merge(:method => :get))
|
||||
)
|
||||
end
|
||||
|
||||
def post(url, params = {})
|
||||
run_request(
|
||||
forge_request(url, params.merge(:method => :post))
|
||||
forge_request(url, params.merge(:method => :post))
|
||||
)
|
||||
end
|
||||
|
||||
def forge_request(url, params = {})
|
||||
Typhoeus::Request.new(
|
||||
url.to_s,
|
||||
merge_request_params(params)
|
||||
url.to_s,
|
||||
merge_request_params(params)
|
||||
)
|
||||
end
|
||||
|
||||
@@ -179,6 +181,14 @@ class Browser
|
||||
end
|
||||
end
|
||||
|
||||
if @basic_auth
|
||||
if !params.has_key?(:headers)
|
||||
params = params.merge(:headers => {'Authorization' => @basic_auth})
|
||||
elsif !params[:headers].has_key?('Authorization')
|
||||
params[:headers]['Authorization'] = @basic_auth
|
||||
end
|
||||
end
|
||||
|
||||
unless params.has_key?(:disable_ssl_host_verification)
|
||||
params = params.merge(:disable_ssl_host_verification => true)
|
||||
end
|
||||
|
||||
@@ -24,16 +24,16 @@ module WebSite
|
||||
wordpress = false
|
||||
|
||||
response = Browser.instance.get(
|
||||
login_url(),
|
||||
{:follow_location => true, :max_redirects => 2}
|
||||
login_url(),
|
||||
{:follow_location => true, :max_redirects => 2}
|
||||
)
|
||||
|
||||
if response.body =~ %r{WordPress}i
|
||||
wordpress = true
|
||||
else
|
||||
response = Browser.instance.get(
|
||||
xmlrpc_url(),
|
||||
{:follow_location => true, :max_redirects => 2}
|
||||
xmlrpc_url(),
|
||||
{:follow_location => true, :max_redirects => 2}
|
||||
)
|
||||
|
||||
if response.body =~ %r{XML-RPC server accepts POST requests only}i
|
||||
@@ -53,6 +53,10 @@ module WebSite
|
||||
Browser.instance.get(@uri.to_s).code != 0
|
||||
end
|
||||
|
||||
def has_basic_auth?
|
||||
Browser.instance.get(@uri.to_s).code == 401
|
||||
end
|
||||
|
||||
# see if the remote url returns 30x redirect
|
||||
# return a string with the redirection or nil
|
||||
def redirection(url = nil)
|
||||
|
||||
@@ -75,7 +75,7 @@ class WpTarget
|
||||
|
||||
# Valid HTTP return codes
|
||||
def self.valid_response_codes
|
||||
[200, 403, 301, 302, 500]
|
||||
[200, 301, 302, 401, 403, 500]
|
||||
end
|
||||
|
||||
# return WpTheme
|
||||
|
||||
@@ -43,7 +43,8 @@ class WpscanOptions
|
||||
:wp_plugins_dir,
|
||||
:help,
|
||||
:config_file,
|
||||
:exclude_content_based
|
||||
:exclude_content_based,
|
||||
:basic_auth
|
||||
]
|
||||
|
||||
attr_accessor *ACCESSOR_OPTIONS
|
||||
@@ -136,6 +137,11 @@ class WpscanOptions
|
||||
end
|
||||
end
|
||||
|
||||
def basic_auth=(basic_auth)
|
||||
raise "Invalid basic authentication format, login:password expected" if basic_auth.index(':').nil?
|
||||
@basic_auth = "Basic #{Base64.encode64(basic_auth).chomp}"
|
||||
end
|
||||
|
||||
def has_options?
|
||||
!to_h.empty?
|
||||
end
|
||||
@@ -225,22 +231,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]
|
||||
["--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
|
||||
|
||||
|
||||
@@ -235,6 +235,24 @@ describe Browser do
|
||||
|
||||
@browser.merge_request_params(:headers => {'accept' => 'text/html'}).should == expected_params
|
||||
end
|
||||
|
||||
it "should merge the basic-auth" do
|
||||
@browser.basic_auth = "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
||||
expected_params = {
|
||||
:disable_ssl_host_verification => true,
|
||||
:disable_ssl_peer_verification => true,
|
||||
:headers => {
|
||||
"Authorization" => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
|
||||
"user-agent" => @browser.user_agent
|
||||
},
|
||||
:cache_timeout => @json_config_without_proxy['cache_timeout']
|
||||
}
|
||||
|
||||
@browser.merge_request_params().should == expected_params
|
||||
|
||||
expected_params[:headers].merge!("user-agent" => "Fake FF")
|
||||
@browser.merge_request_params(:headers => {"user-agent" => "Fake FF"}).should == expected_params
|
||||
end
|
||||
end
|
||||
|
||||
describe "#merge_request_params with proxy" do
|
||||
|
||||
@@ -57,7 +57,7 @@ shared_examples_for "WebSite" do
|
||||
|
||||
it "should return true if the xmlrpc is found" do
|
||||
stub_request(:get, @module.xmlrpc_url).
|
||||
to_return(:status => 200, :body => File.new(fixtures_dir + '/xmlrpc.php'))
|
||||
to_return(:status => 200, :body => File.new(fixtures_dir + '/xmlrpc.php'))
|
||||
|
||||
@module.is_wordpress?.should be_true
|
||||
end
|
||||
@@ -75,6 +75,18 @@ shared_examples_for "WebSite" do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#has_basic_auth?" do
|
||||
it "should detect that the wpsite is basic auth protected" do
|
||||
stub_request(:get, "http://example.localhost/").to_return(:status => 401)
|
||||
@module.should have_basic_auth
|
||||
end
|
||||
|
||||
it "should not have a basic auth for a 200" do
|
||||
stub_request(:get, "http://example.localhost/").to_return(:status => 200)
|
||||
@module.should_not have_basic_auth
|
||||
end
|
||||
end
|
||||
|
||||
describe "#redirection" do
|
||||
it "should return nil if no redirection detected" do
|
||||
stub_request(:get, @module.url).to_return(:status => 200, :body => '')
|
||||
|
||||
@@ -197,17 +197,20 @@ describe "WpscanOptions" do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_h" do
|
||||
it "should return an empty hash" do
|
||||
@wpscan_options.to_h.should be_a Hash
|
||||
@wpscan_options.to_h.should be_empty
|
||||
describe "#basic_auth=" do
|
||||
context "invalid format" do
|
||||
it "should raise an error if the : is missing" do
|
||||
expect { @wpscan_options.basic_auth = "helloworld" }.to raise_error(
|
||||
RuntimeError, "Invalid basic authentication format, login:password expected"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it "should return a hash with :verbose = true" do
|
||||
expected = {:verbose => true}
|
||||
@wpscan_options.verbose = true
|
||||
|
||||
@wpscan_options.to_h.should === expected
|
||||
context "valid format" do
|
||||
it "should add the 'Basic' word and do the encode64. See RFC 2617" do
|
||||
@wpscan_options.basic_auth = "Aladdin:open sesame"
|
||||
@wpscan_options.basic_auth.should == "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -222,6 +225,20 @@ describe "WpscanOptions" do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_h" do
|
||||
it "should return an empty hash" do
|
||||
@wpscan_options.to_h.should be_a Hash
|
||||
@wpscan_options.to_h.should be_empty
|
||||
end
|
||||
|
||||
it "should return a hash with :verbose = true" do
|
||||
expected = {:verbose => true}
|
||||
@wpscan_options.verbose = true
|
||||
|
||||
@wpscan_options.to_h.should === expected
|
||||
end
|
||||
end
|
||||
|
||||
describe "#clean_option" do
|
||||
after :each do
|
||||
WpscanOptions.clean_option(@option).should === @expected
|
||||
|
||||
Reference in New Issue
Block a user