Ref #69, #7 Proxy Auth Support Added

This commit is contained in:
Erwan
2012-11-22 15:23:59 +01:00
parent 1c2487c272
commit d802799bd2
6 changed files with 186 additions and 67 deletions

View File

@@ -11,7 +11,8 @@
SOCKS proxies (4, 4A, 5) are supported, ie : "proxy": "socks5://127.0.0.1:9000" SOCKS proxies (4, 4A, 5) are supported, ie : "proxy": "socks5://127.0.0.1:9000"
If you do not specify the protocol, http will be used If you do not specify the protocol, http will be used
*/ */
//"proxy": "127.0.0.1:3038", //"proxy": "127.0.0.1:3128",
//"proxy_auth": "username:password"
"cache_timeout": 600, // 10 minutes, at this time the cache is cleaned before each scan. If this value is set to 0, the cache will be disabled "cache_timeout": 600, // 10 minutes, at this time the cache is cleaned before each scan. If this value is set to 0, the cache will be disabled

View File

@@ -25,6 +25,7 @@ class Browser
:user_agent_mode, :user_agent_mode,
:available_user_agents, :available_user_agents,
:proxy, :proxy,
:proxy_auth,
:max_threads, :max_threads,
:cache_timeout, :cache_timeout,
:request_timeout :request_timeout
@@ -98,6 +99,29 @@ class Browser
@max_threads = max_threads @max_threads = max_threads
end end
def proxy_auth=(auth)
unless auth.nil?
if auth.is_a?(Hash)
if !auth.include?(:proxy_username) or !auth.include?(:proxy_password)
raise_invalid_proxy_format()
end
@proxy_auth = auth
elsif auth.is_a?(String)
if matches = %r{([^:]+):(.*)}.match(auth)
@proxy_auth = {:proxy_username => matches[1], :proxy_password => matches[2]}
else
raise_invalid_proxy_format()
end
else
raise_invalid_proxy_format()
end
end
end
def raise_invalid_proxy_format
raise "Invalid proxy auth format, expected username:password or {:proxy_username => username, :proxy_password => password}"
end
# TODO reload hydra (if the .load_config is called on a browser object, hydra will not have the new @max_threads and @request_timeout) # TODO reload hydra (if the .load_config is called on a browser object, hydra will not have the new @max_threads and @request_timeout)
def load_config(config_file = nil) def load_config(config_file = nil)
@config_file = config_file || @config_file @config_file = config_file || @config_file
@@ -149,6 +173,10 @@ class Browser
def merge_request_params(params = {}) def merge_request_params(params = {})
if @proxy if @proxy
params = params.merge(:proxy => @proxy) params = params.merge(:proxy => @proxy)
if @proxy_auth
params = params.merge(@proxy_auth)
end
end end
unless params.has_key?(:disable_ssl_host_verification) unless params.has_key?(:disable_ssl_host_verification)

View File

@@ -27,6 +27,7 @@ class WpscanOptions
:enumerate_usernames, :enumerate_usernames,
:enumerate_usernames_range, :enumerate_usernames_range,
:proxy, :proxy,
:proxy_auth,
:threads, :threads,
:url, :url,
:wordlist, :wordlist,
@@ -76,6 +77,14 @@ class WpscanOptions
end end
end end
def proxy_auth=(auth)
if auth.index(':') == nil
raise "Invalid proxy auth format, username:password expected"
else
@proxy_auth = auth
end
end
def enumerate_plugins=(enumerate_plugins) def enumerate_plugins=(enumerate_plugins)
if enumerate_plugins === true and @enumerate_only_vulnerable_plugins === true if enumerate_plugins === true and @enumerate_only_vulnerable_plugins === true
raise "You can't enumerate plugins and only vulnerable plugins at the same time, please choose only one" raise "You can't enumerate plugins and only vulnerable plugins at the same time, please choose only one"
@@ -150,7 +159,7 @@ class WpscanOptions
cli_value cli_value
) )
elsif cli_option === "--enumerate" # Special cases elsif cli_option === "--enumerate" # Special cases
# Default value if no argument is given # Default value if no argument is given
cli_value = "vt,tt,u,vp" if cli_value.length == 0 cli_value = "vt,tt,u,vp" if cli_value.length == 0
enumerate_options_from_string(cli_value) enumerate_options_from_string(cli_value)
@@ -201,7 +210,8 @@ class WpscanOptions
["--force", "-f", GetoptLong::NO_ARGUMENT], ["--force", "-f", GetoptLong::NO_ARGUMENT],
["--help", "-h", GetoptLong::NO_ARGUMENT], ["--help", "-h", GetoptLong::NO_ARGUMENT],
["--verbose", "-v", GetoptLong::NO_ARGUMENT], ["--verbose", "-v", GetoptLong::NO_ARGUMENT],
["--proxy", GetoptLong::OPTIONAL_ARGUMENT], ["--proxy", GetoptLong::REQUIRED_ARGUMENT],
["--proxy-auth", GetoptLong::REQUIRED_ARGUMENT],
["--update", GetoptLong::NO_ARGUMENT], ["--update", GetoptLong::NO_ARGUMENT],
["--follow-redirection", GetoptLong::NO_ARGUMENT], ["--follow-redirection", GetoptLong::NO_ARGUMENT],
["--wp-content-dir", GetoptLong::REQUIRED_ARGUMENT], ["--wp-content-dir", GetoptLong::REQUIRED_ARGUMENT],
@@ -226,7 +236,7 @@ class WpscanOptions
def self.option_to_instance_variable_setter(option) def self.option_to_instance_variable_setter(option)
cleaned_option = WpscanOptions.clean_option(option) cleaned_option = WpscanOptions.clean_option(option)
option_syms = ACCESSOR_OPTIONS.grep(%r{^#{cleaned_option}}) option_syms = ACCESSOR_OPTIONS.grep(%r{^#{cleaned_option}$})
option_syms.length == 1 ? :"#{option_syms.at(0)}=" : nil option_syms.length == 1 ? :"#{option_syms.at(0)}=" : nil
end end

View File

@@ -0,0 +1,8 @@
{
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0) Gecko/20100101 Firefox/11.0",
"user_agent_mode": "static",
"proxy": "127.0.0.1:3038",
"proxy_auth": "user:pass",
"cache_timeout": 300,
"request_timeout": 2000
}

View File

@@ -19,13 +19,14 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe Browser do describe Browser do
CONFIG_FILE_WITHOUT_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json' CONFIG_FILE_WITHOUT_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json'
CONFIG_FILE_WITH_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy.json' CONFIG_FILE_WITH_PROXY = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy.json'
INSTANCE_VARS_TO_CHECK = ['user_agent', 'user_agent_mode', 'available_user_agents', 'proxy', 'max_threads', 'request_timeout', 'cache_timeout'] CONFIG_FILE_WITH_PROXY_AND_AUTH = SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf_proxy_auth.json'
INSTANCE_VARS_TO_CHECK = ['user_agent', 'user_agent_mode', 'available_user_agents', 'proxy', 'max_threads', 'request_timeout', 'cache_timeout']
before :all do before :all do
@json_config_without_proxy = JSON.parse(File.read(CONFIG_FILE_WITHOUT_PROXY)) @json_config_without_proxy = JSON.parse(File.read(CONFIG_FILE_WITHOUT_PROXY))
@json_config_with_proxy = JSON.parse(File.read(CONFIG_FILE_WITH_PROXY)) @json_config_with_proxy = JSON.parse(File.read(CONFIG_FILE_WITH_PROXY))
end end
before :each do before :each do
@@ -71,6 +72,42 @@ describe Browser do
end end
end end
describe "#proxy_auth=" do
after :each do
if @raise_error
expect { @browser.proxy_auth = @proxy_auth }.to raise_error
else
@browser.proxy_auth = @proxy_auth
@browser.proxy_auth.should === @expected
end
end
it "should raise an error if the format is not correct" do
@proxy_auth = "invaludauthformat"
@raise_error = true
end
it "should raise an error if the hash does not contain :proxy_username and :proxy_password" do
@proxy_auth = { :proxy_password => "hello" }
@raise_error = true
end
it "should raise an error if the auth if not a string or a hash" do
@proxy_auth = 10
@raise_error = true
end
it "should set the correct credentials" do
@proxy_auth = {:proxy_username => "user", :proxy_password => "pass" }
@expected = @proxy_auth
end
it "should set the correct credentials" do
@proxy_auth = "username:passwd"
@expected = {:proxy_username => "username", :proxy_password => "passwd" }
end
end
describe "#user_agent" do describe "#user_agent" do
available_user_agents = %w{ ua-1 ua-2 ua-3 ua-4 ua-6 ua-7 ua-8 ua-9 ua-10 ua-11 ua-12 ua-13 ua-14 ua-15 ua-16 ua-17} available_user_agents = %w{ ua-1 ua-2 ua-3 ua-4 ua-6 ua-7 ua-8 ua-9 ua-10 ua-11 ua-12 ua-13 ua-14 ua-15 ua-16 ua-17}
@@ -115,8 +152,8 @@ describe Browser do
it "will check the instance vars" do it "will check the instance vars" do
Browser.reset Browser.reset
check_instance_variables( check_instance_variables(
Browser.instance(:config_file => CONFIG_FILE_WITHOUT_PROXY), Browser.instance(:config_file => CONFIG_FILE_WITHOUT_PROXY),
@json_config_without_proxy @json_config_without_proxy
) )
end end
end end
@@ -125,8 +162,8 @@ describe Browser do
it "will check the instance vars" do it "will check the instance vars" do
Browser.reset Browser.reset
check_instance_variables( check_instance_variables(
Browser.instance(:config_file => CONFIG_FILE_WITH_PROXY), Browser.instance(:config_file => CONFIG_FILE_WITH_PROXY),
@json_config_with_proxy @json_config_with_proxy
) )
end end
end end
@@ -136,26 +173,27 @@ describe Browser do
it "will check the instance vars, with an overriden one" do it "will check the instance vars, with an overriden one" do
Browser.reset Browser.reset
check_instance_variables( check_instance_variables(
Browser.instance( Browser.instance(
:config_file => CONFIG_FILE_WITHOUT_PROXY, :config_file => CONFIG_FILE_WITHOUT_PROXY,
:user_agent => "fake IE" :user_agent => "fake IE"
), ),
@json_config_without_proxy.merge("user_agent" => "fake IE") @json_config_without_proxy.merge("user_agent" => "fake IE")
) )
end end
it "should not override the max_threads if max_threads = nil" do it "should not override the max_threads if max_threads = nil" do
Browser.reset Browser.reset
check_instance_variables( check_instance_variables(
Browser.instance( Browser.instance(
:config_file => CONFIG_FILE_WITHOUT_PROXY, :config_file => CONFIG_FILE_WITHOUT_PROXY,
:max_threads => nil :max_threads => nil
), ),
@json_config_without_proxy @json_config_without_proxy
) )
end end
end end
# TODO
describe "#load_config" do describe "#load_config" do
end end
@@ -163,10 +201,10 @@ describe Browser do
describe "#merge_request_params without proxy" do describe "#merge_request_params without proxy" do
it "should return the default params" do it "should return the default params" do
expected_params = { expected_params = {
:disable_ssl_host_verification => true, :disable_ssl_host_verification => true,
:disable_ssl_peer_verification => true, :disable_ssl_peer_verification => true,
:headers => {'user-agent' => @browser.user_agent}, :headers => {'user-agent' => @browser.user_agent},
:cache_timeout => @json_config_without_proxy['cache_timeout'] :cache_timeout => @json_config_without_proxy['cache_timeout']
} }
@browser.merge_request_params().should == expected_params @browser.merge_request_params().should == expected_params
@@ -174,25 +212,25 @@ describe Browser do
it "should return the default params with some values overriden" do it "should return the default params with some values overriden" do
expected_params = { expected_params = {
:disable_ssl_host_verification => false, :disable_ssl_host_verification => false,
:disable_ssl_peer_verification => true, :disable_ssl_peer_verification => true,
:headers => {'user-agent' => 'Fake IE'}, :headers => {'user-agent' => 'Fake IE'},
:cache_timeout => 0 :cache_timeout => 0
} }
@browser.merge_request_params( @browser.merge_request_params(
:disable_ssl_host_verification => false, :disable_ssl_host_verification => false,
:headers => {'user-agent' => 'Fake IE'}, :headers => {'user-agent' => 'Fake IE'},
:cache_timeout => 0 :cache_timeout => 0
).should == expected_params ).should == expected_params
end end
it "should return the defaul params with :headers:accept = 'text/html' (should not override :headers:user-agent)" do it "should return the defaul params with :headers:accept = 'text/html' (should not override :headers:user-agent)" do
expected_params = { expected_params = {
:disable_ssl_host_verification => true, :disable_ssl_host_verification => true,
:disable_ssl_peer_verification => true, :disable_ssl_peer_verification => true,
:headers => {'user-agent' => @browser.user_agent, 'accept' => 'text/html'}, :headers => {'user-agent' => @browser.user_agent, 'accept' => 'text/html'},
:cache_timeout => @json_config_without_proxy['cache_timeout'] :cache_timeout => @json_config_without_proxy['cache_timeout']
} }
@browser.merge_request_params(:headers => {'accept' => 'text/html'}).should == expected_params @browser.merge_request_params(:headers => {'accept' => 'text/html'}).should == expected_params
@@ -205,11 +243,28 @@ describe Browser do
browser = Browser.instance(:config_file => CONFIG_FILE_WITH_PROXY) browser = Browser.instance(:config_file => CONFIG_FILE_WITH_PROXY)
expected_params = { expected_params = {
:proxy => @json_config_with_proxy['proxy'], :proxy => @json_config_with_proxy['proxy'],
:disable_ssl_host_verification => true, :disable_ssl_host_verification => true,
:disable_ssl_peer_verification => true, :disable_ssl_peer_verification => true,
:headers => {'user-agent' => @json_config_with_proxy['user_agent']}, :headers => {'user-agent' => @json_config_with_proxy['user_agent']},
:cache_timeout => @json_config_with_proxy['cache_timeout'] :cache_timeout => @json_config_with_proxy['cache_timeout']
}
browser.merge_request_params().should == expected_params
end
it "should return the default params (proxy_auth set)" do
Browser.reset
browser = Browser.instance(:config_file => CONFIG_FILE_WITH_PROXY_AND_AUTH)
expected_params = {
:proxy => @json_config_with_proxy['proxy'],
:proxy_username => "user",
:proxy_password => "pass",
:disable_ssl_host_verification => true,
:disable_ssl_peer_verification => true,
:headers => {'user-agent' => @json_config_with_proxy['user_agent']},
:cache_timeout => @json_config_with_proxy['cache_timeout']
} }
browser.merge_request_params().should == expected_params browser.merge_request_params().should == expected_params
@@ -226,11 +281,12 @@ describe Browser do
url = 'http://example.com/' url = 'http://example.com/'
stub_request(:post, url). stub_request(:post, url).
with(:body => "login=master&password=it's me !"). with(:body => "login=master&password=it's me !").
to_return(:status => 200, :body => "Welcome Master") to_return(:status => 200, :body => "Welcome Master")
response = @browser.post(url, response = @browser.post(
:params => {:login => "master", :password => "it's me !"} url,
:params => {:login => "master", :password => "it's me !"}
) )
response.should be_a Typhoeus::Response response.should be_a Typhoeus::Response
@@ -243,7 +299,7 @@ describe Browser do
url = 'http://example.com/' url = 'http://example.com/'
stub_request(:get, url). stub_request(:get, url).
to_return(:status => 200, :body => "Hello World !") to_return(:status => 200, :body => "Hello World !")
response = @browser.get(url) response = @browser.get(url)
@@ -278,7 +334,7 @@ describe Browser do
url = 'http://example.localhost' url = 'http://example.localhost'
stub_request(:get, url). stub_request(:get, url).
to_return(:status => 200, :body => "Hello World !") to_return(:status => 200, :body => "Hello World !")
response1 = @browser.get(url) response1 = @browser.get(url)
response2 = @browser.get(url) response2 = @browser.get(url)

View File

@@ -89,11 +89,24 @@ describe "WpscanOptions" do
end end
end end
describe "#proxy_auth=" do
it "should raise an error if the format is not correct" do
expect { @wpscan_options.proxy_auth = "invalidauth" }.to raise_error
end
it "should not raise en error" do
proxy_auth = "user:pass"
@wpscan_options.proxy_auth = proxy_auth
@wpscan_options.proxy_auth.should === proxy_auth
end
end
describe "#enumerate_plugins=" do describe "#enumerate_plugins=" do
it "should raise an error" do it "should raise an error" do
@wpscan_options.enumerate_only_vulnerable_plugins = true @wpscan_options.enumerate_only_vulnerable_plugins = true
expect { @wpscan_options.enumerate_plugins = true }.to raise_error(RuntimeError, expect { @wpscan_options.enumerate_plugins = true }.to raise_error(
"You can't enumerate plugins and only vulnerable plugins at the same time, please choose only one") RuntimeError, "You can't enumerate plugins and only vulnerable plugins at the same time, please choose only one"
)
end end
it "should not raise an error" do it "should not raise an error" do
@@ -107,8 +120,9 @@ describe "WpscanOptions" do
describe "#enumerate_themes=" do describe "#enumerate_themes=" do
it "should raise an error" do it "should raise an error" do
@wpscan_options.enumerate_only_vulnerable_themes = true @wpscan_options.enumerate_only_vulnerable_themes = true
expect { @wpscan_options.enumerate_themes = true }.to raise_error(RuntimeError, expect { @wpscan_options.enumerate_themes = true }.to raise_error(
"You can't enumerate themes and only vulnerable themes at the same time, please choose only one") RuntimeError, "You can't enumerate themes and only vulnerable themes at the same time, please choose only one"
)
end end
it "should not raise an error" do it "should not raise an error" do
@@ -122,8 +136,9 @@ describe "WpscanOptions" do
describe "#enumerate_only_vulnerable_plugins=" do describe "#enumerate_only_vulnerable_plugins=" do
it "should raise an error" do it "should raise an error" do
@wpscan_options.enumerate_plugins = true @wpscan_options.enumerate_plugins = true
expect { @wpscan_options.enumerate_only_vulnerable_plugins = true }.to raise_error(RuntimeError, expect { @wpscan_options.enumerate_only_vulnerable_plugins = true }.to raise_error(
"You can't enumerate plugins and only vulnerable plugins at the same time, please choose only one") RuntimeError, "You can't enumerate plugins and only vulnerable plugins at the same time, please choose only one"
)
end end
it "should not raise an error" do it "should not raise an error" do
@@ -137,8 +152,9 @@ describe "WpscanOptions" do
describe "#enumerate_only_vulnerable_themes=" do describe "#enumerate_only_vulnerable_themes=" do
it "should raise an error" do it "should raise an error" do
@wpscan_options.enumerate_themes = true @wpscan_options.enumerate_themes = true
expect { @wpscan_options.enumerate_only_vulnerable_themes = true }.to raise_error(RuntimeError, expect { @wpscan_options.enumerate_only_vulnerable_themes = true }.to raise_error(
"You can't enumerate themes and only vulnerable themes at the same time, please choose only one") RuntimeError, "You can't enumerate themes and only vulnerable themes at the same time, please choose only one"
)
end end
it "should not raise an error" do it "should not raise an error" do
@@ -200,19 +216,19 @@ describe "WpscanOptions" do
WpscanOptions.option_to_instance_variable_setter(@argument).should === @expected WpscanOptions.option_to_instance_variable_setter(@argument).should === @expected
end end
it "should return @url" do it "should return :url=" do
@argument = "--url" @argument = "--url"
@expected = :url= @expected = :url=
end end
it "should return @verbose" do it "should return :verbose=" do
@argument = "-v" @argument = "--verbose"
@expected = :verbose= @expected = :verbose=
end end
it "should return nil for -U" do it "should return :proxy= for --proxy" do
@argument = "-U" @argument = "--proxy"
@expected = nil @expected = :proxy=
end end
it "should return nil for --enumerate" do it "should return nil for --enumerate" do
@@ -220,9 +236,9 @@ describe "WpscanOptions" do
@expected = nil @expected = nil
end end
it "should return nil for -e" do it "should return :proxy_auth= for --proxy_auth" do
@argument = "-e" @argument = "--proxy_auth"
@expected = nil @expected = :proxy_auth=
end end
end end
@@ -279,8 +295,8 @@ describe "WpscanOptions" do
it "should set enumerate_timthumbs to true, enumerate_usernames to true, enumerate_usernames_range to (1..2)" do it "should set enumerate_timthumbs to true, enumerate_usernames to true, enumerate_usernames_range to (1..2)" do
@argument = "u[1-2],tt" @argument = "u[1-2],tt"
@expected_hash = { @expected_hash = {
:enumerate_usernames => true, :enumerate_usernames_range => (1..2), :enumerate_usernames => true, :enumerate_usernames_range => (1..2),
:enumerate_timthumbs => true :enumerate_timthumbs => true
} }
end end
end end