From 79cad5cf55547634c89433ae80896a2d76795408 Mon Sep 17 00:00:00 2001 From: erwanlr Date: Thu, 21 Feb 2013 18:48:48 +0100 Subject: [PATCH] Ref #53 Typhoeus > 0.4.2 support --- conf/browser.conf.json | 2 +- lib/common/browser.rb | 38 ++++----- lib/common/cache_file_store.rb | 4 +- lib/wpscan/modules/brute_force.rb | 7 +- lib/wpscan/modules/wp_config_backup.rb | 15 +++- lib/wpscan/web_site.rb | 14 ++-- spec/lib/common/browser_spec.rb | 80 +++++++++---------- spec/lib/wpscan/modules/brute_force_spec.rb | 27 ++++--- .../wpscan/modules/wp_config_backup_spec.rb | 9 +-- spec/lib/wpscan/web_site_spec.rb | 12 ++- spec/lib/wpscan/wp_target_spec.rb | 4 +- spec/lib/wpscan/wpscan_helper.rb | 3 +- spec/samples/conf/browser/browser.conf.json | 2 +- .../conf/browser/browser.conf_proxy.json | 2 +- .../conf/browser/browser.conf_proxy_auth.json | 2 +- 15 files changed, 118 insertions(+), 103 deletions(-) diff --git a/conf/browser.conf.json b/conf/browser.conf.json index 7e901d45..21a3821d 100644 --- a/conf/browser.conf.json +++ b/conf/browser.conf.json @@ -14,7 +14,7 @@ //"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_ttl": 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 "request_timeout": 2000, // 2s diff --git a/lib/common/browser.rb b/lib/common/browser.rb index 0a43b520..86c51b79 100644 --- a/lib/common/browser.rb +++ b/lib/common/browser.rb @@ -30,7 +30,7 @@ class Browser :proxy, :proxy_auth, :max_threads, - :cache_timeout, + :cache_ttl, :request_timeout, :basic_auth ] @@ -113,13 +113,10 @@ class Browser if !auth.include?(:proxy_username) or !auth.include?(:proxy_password) raise_invalid_proxy_format() end - @proxy_auth = auth + @proxy_auth = auth[:proxy_username] + ':' + auth[:proxy_password] elsif auth.is_a?(String) - if matches = %r{([^:]+):(.*)}.match(auth) - @proxy_auth = { - proxy_username: matches[1], - proxy_password: matches[2] - } + if auth.index(':') != nil + @proxy_auth = auth else raise_invalid_proxy_auth_format() end @@ -176,10 +173,10 @@ class Browser def merge_request_params(params = {}) if @proxy - params = params.merge(:proxy => @proxy) + params = params.merge(proxy: @proxy) if @proxy_auth - params = params.merge(@proxy_auth) + params = params.merge(proxyauth: @proxy_auth) end end @@ -191,24 +188,23 @@ class Browser end end - # TODO : check if it's the default value into ethon. If so, removed the lines from here - unless params.has_key?(:ssl_verifyhost) - params = params.merge(ssl_verifyhost: 0) - end + #unless params.has_key?(:ssl_verifyhost) + # params = params.merge(ssl_verifyhost: 0) + #end - unless params.has_key?(:ssl_verifypeer) - params = params.merge(ssl_verifypeer: false) - end + #unless params.has_key?(:ssl_verifypeer) + # params = params.merge(ssl_verifypeer: 0) + #end if !params.has_key?(:headers) - params = params.merge(:headers => {'ser-agent' => self.user_agent}) - elsif !params[:headers].has_key?('user-agent') - params[:headers]['user-agent'] = self.user_agent + params = params.merge(:headers => {'User-Agent' => self.user_agent}) + elsif !params[:headers].has_key?('User-Agent') + params[:headers]['User-Agent'] = self.user_agent end - # Used to enable the cache system if :cache_timeout > 0 + # Used to enable the cache system if :cache_ttl > 0 unless params.has_key?(:cache_ttl) - params = params.merge(cache_ttl: @cache_timeout) + params = params.merge(cache_ttl: @cache_ttl) end params diff --git a/lib/common/cache_file_store.rb b/lib/common/cache_file_store.rb index 5ff42d17..665d5026 100644 --- a/lib/common/cache_file_store.rb +++ b/lib/common/cache_file_store.rb @@ -59,8 +59,8 @@ class CacheFileStore end end - def write_entry(key, data_to_store, cache_timeout) - if cache_timeout > 0 + def write_entry(key, data_to_store, cache_ttl) + if cache_ttl > 0 File.open(get_entry_file_path(key), 'w') do |f| f.write(@serializer.dump(data_to_store)) end diff --git a/lib/wpscan/modules/brute_force.rb b/lib/wpscan/modules/brute_force.rb index 04f560b3..6d43ffcf 100644 --- a/lib/wpscan/modules/brute_force.rb +++ b/lib/wpscan/modules/brute_force.rb @@ -36,10 +36,11 @@ module BruteForce password_found = false 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] == '#' + password.strip! + # keep a count of the amount of requests to be sent request_count += 1 queue_count += 1 @@ -52,8 +53,8 @@ module BruteForce request = Browser.instance.forge_request(login_url, { method: :post, - params: { log: URI::encode(username), pwd: URI::encode(password) }, - cache_timeout: 0 + body: { log: URI::encode(username), pwd: URI::encode(password) }, + cache_ttl: 0 } ) diff --git a/lib/wpscan/modules/wp_config_backup.rb b/lib/wpscan/modules/wp_config_backup.rb index 6055e329..e5411370 100644 --- a/lib/wpscan/modules/wp_config_backup.rb +++ b/lib/wpscan/modules/wp_config_backup.rb @@ -23,10 +23,11 @@ module WpConfigBackup # See http://www.feross.org/cmsploit/ # return an array of backup config files url def config_backup - found = [] - backups = WpConfigBackup.config_backup_files - browser = Browser.instance - hydra = browser.hydra + found = [] + backups = WpConfigBackup.config_backup_files + browser = Browser.instance + hydra = browser.hydra + queue_count = 0 backups.each do |file| file_url = @uri.merge(URI.escape(file)).to_s @@ -39,6 +40,12 @@ module WpConfigBackup end hydra.queue(request) + queue_count += 1 + + if queue_count == browser.max_threads + hydra.run + queue_count = 0 + end end hydra.run diff --git a/lib/wpscan/web_site.rb b/lib/wpscan/web_site.rb index d7b8f2fa..97fc88dd 100644 --- a/lib/wpscan/web_site.rb +++ b/lib/wpscan/web_site.rb @@ -48,12 +48,14 @@ class WebSite def xml_rpc_url unless @xmlrpc_url - headers = Browser.instance.get(@uri.to_s).headers_hash - value = headers['x-pingback'] - if value.nil? or value.empty? - @xmlrpc_url = nil - else - @xmlrpc_url = value + headers = Browser.instance.get(@uri.to_s).headers_hash + @xmlrpc_url = nil + + unless headers.nil? + value = headers['X-Pingback'] + unless value.nil? && value.empty? + @xmlrpc_url = value + end end end @xmlrpc_url diff --git a/spec/lib/common/browser_spec.rb b/spec/lib/common/browser_spec.rb index 58b056ea..9b790769 100644 --- a/spec/lib/common/browser_spec.rb +++ b/spec/lib/common/browser_spec.rb @@ -23,7 +23,7 @@ describe Browser do 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_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'] + INSTANCE_VARS_TO_CHECK = ['user_agent', 'user_agent_mode', 'available_user_agents', 'proxy', 'max_threads', 'request_timeout', 'cache_ttl'] before :all do @json_config_without_proxy = JSON.parse(File.read(CONFIG_FILE_WITHOUT_PROXY)) @@ -31,6 +31,7 @@ describe Browser do end before :each do + Browser::reset @browser = Browser.instance(config_file: CONFIG_FILE_WITHOUT_PROXY) end @@ -100,12 +101,12 @@ describe Browser do it 'should set the correct credentials' do @proxy_auth = { proxy_username: 'user', proxy_password: 'pass' } - @expected = @proxy_auth + @expected = 'user:pass' end it 'should set the correct credentials' do @proxy_auth = 'username:passwd' - @expected = { proxy_username: 'username', proxy_password: 'passwd' } + @expected = @proxy_auth end end @@ -206,10 +207,10 @@ describe Browser do describe '#merge_request_params without proxy' do it 'should return the default params' do expected_params = { - disable_ssl_host_verification: true, - disable_ssl_peer_verification: true, - headers: { 'user-agent' => @browser.user_agent }, - cache_timeout: @json_config_without_proxy['cache_timeout'] + #disable_ssl_host_verification: true, + #disable_ssl_peer_verification: true, + headers: { 'User-Agent' => @browser.user_agent }, + cache_ttl: @json_config_without_proxy['cache_ttl'] } @browser.merge_request_params().should == expected_params @@ -217,25 +218,25 @@ describe Browser do it 'should return the default params with some values overriden' do expected_params = { - disable_ssl_host_verification: false, - disable_ssl_peer_verification: true, - headers: { 'user-agent' => 'Fake IE' }, - cache_timeout: 0 + #disable_ssl_host_verification: false, + #disable_ssl_peer_verification: true, + headers: { 'User-Agent' => 'Fake IE' }, + cache_ttl: 0 } @browser.merge_request_params( - disable_ssl_host_verification: false, - headers: { 'user-agent' => 'Fake IE' }, - cache_timeout: 0 + #disable_ssl_host_verification: false, + headers: { 'User-Agent' => 'Fake IE' }, + cache_ttl: 0 ).should == expected_params 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 = { - disable_ssl_host_verification: true, - disable_ssl_peer_verification: true, - headers: { 'user-agent' => @browser.user_agent, 'accept' => 'text/html' }, - cache_timeout: @json_config_without_proxy['cache_timeout'] + #disable_ssl_host_verification: true, + #disable_ssl_peer_verification: true, + headers: { 'User-Agent' => @browser.user_agent, 'accept' => 'text/html' }, + cache_ttl: @json_config_without_proxy['cache_ttl'] } @browser.merge_request_params(headers: { 'accept' => 'text/html' }).should == expected_params @@ -244,19 +245,19 @@ describe Browser do it 'should merge the basic-auth' do @browser.basic_auth = 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' expected_params = { - disable_ssl_host_verification: true, - disable_ssl_peer_verification: true, - cache_timeout: @json_config_without_proxy['cache_timeout'], - headers: { + #disable_ssl_host_verification: true, + #disable_ssl_peer_verification: true, + cache_ttl: @json_config_without_proxy['cache_ttl'], + headers: { 'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', - 'user-agent' => @browser.user_agent + 'User-Agent' => @browser.user_agent } } @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 + expected_params[:headers].merge!('User-Agent' => 'Fake FF') + @browser.merge_request_params(headers: { 'User-Agent' => 'Fake FF' }).should == expected_params end end @@ -267,10 +268,10 @@ describe Browser do expected_params = { proxy: @json_config_with_proxy['proxy'], - 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'] + #disable_ssl_host_verification: true, + #disable_ssl_peer_verification: true, + headers: { 'User-Agent' => @json_config_with_proxy['user_agent'] }, + cache_ttl: @json_config_with_proxy['cache_ttl'] } browser.merge_request_params().should == expected_params @@ -282,12 +283,11 @@ describe Browser do 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'] + proxyauth: 'user:pass', + #disable_ssl_host_verification: true, + #disable_ssl_peer_verification: true, + headers: { 'User-Agent' => @json_config_with_proxy['user_agent'] }, + cache_ttl: @json_config_with_proxy['cache_ttl'] } browser.merge_request_params().should == expected_params @@ -300,16 +300,16 @@ describe Browser do end describe '#post' do - it 'should return a Typhoeus::Response wth body = "Welcome Master" if login=master&password=it\'s me !' do + it 'should return a Typhoeus::Response wth body = "Welcome Master" if login=master&password=itsme!' do url = 'http://example.com/' - stub_request(:post, url). - with(body: "login=master&password=it's me !"). + stub_request(:post, url).with(body: { login: 'master', password: 'itsme!' }). to_return(status: 200, body: 'Welcome Master') response = @browser.post( url, - params: { login: 'master', password: 'it\'s me !' } + body: 'login=master&password=itsme!' + #body: { login: 'master', password: 'hello' } # It's should be this line, but it fails ) response.should be_a Typhoeus::Response diff --git a/spec/lib/wpscan/modules/brute_force_spec.rb b/spec/lib/wpscan/modules/brute_force_spec.rb index 1f2b5a55..9470828f 100644 --- a/spec/lib/wpscan/modules/brute_force_spec.rb +++ b/spec/lib/wpscan/modules/brute_force_spec.rb @@ -48,26 +48,29 @@ shared_examples_for 'BruteForce' do passwords << password.strip unless password.strip[0, 1] == '#' end # Last status must be 302 to get full code coverage - passwords.each do |_| - stub_request(:any, @module.login_url).to_return( - { status: 200, body: 'login_error' }, - { status: 0, body: 'no reponse' }, - { status: 50, body: 'server error' }, - { status: 999, body: 'invalid' }, - { status: 302, body: 'FOUND!' } - ) + passwords.each do |password| + stub_request(:post, @module.login_url). + to_return( + { status: 200, body: 'login_error' }, + { status: 0, body: 'no reponse' }, + { status: 500, body: 'server error' }, + { status: 999, body: 'invalid' }, + { status: 302, body: 'FOUND!' } + ) end - user = WpUser.new('admin', 1, nil) + user = WpUser.new('admin', 1, nil) result = @module.brute_force([user], @wordlist) + result.length.should == 1 result.should === [{ name: 'admin', password: 'root' }] end it 'should cover the timeout branch and return an empty array' do - stub_request(:any, @module.login_url).to_timeout - user = WpUser.new('admin', 1, nil) - result = @module.brute_force([user], @wordlist) + stub_request(:post, @module.login_url).to_timeout + + user = WpUser.new('admin', 1, nil) + result = @module.brute_force([user], @wordlist) result.should == [] end end diff --git a/spec/lib/wpscan/modules/wp_config_backup_spec.rb b/spec/lib/wpscan/modules/wp_config_backup_spec.rb index 672e79a6..00d53576 100644 --- a/spec/lib/wpscan/modules/wp_config_backup_spec.rb +++ b/spec/lib/wpscan/modules/wp_config_backup_spec.rb @@ -34,8 +34,7 @@ shared_examples_for 'WpConfigBackup' do @config_backup_files.each do |backup_file| file_url = @module.uri.merge(URI.escape(backup_file)).to_s - stub_request(:get, file_url). - to_return(status: 404, body: '') + stub_request(:get, file_url).to_return(status: 404) end end @@ -50,8 +49,7 @@ shared_examples_for 'WpConfigBackup' do file_url = @module.uri.merge(URI.escape(backup_file)).to_s expected << file_url - stub_request(:get, file_url). - to_return(status: 200, body: File.new(@fixtures_dir + '/wp-config.php')) + stub_request_to_fixture(url: file_url, fixture: @fixtures_dir + '/wp-config.php') end wp_config_backup = @module.config_backup @@ -67,8 +65,7 @@ shared_examples_for 'WpConfigBackup' do file_url = @module.uri.merge(URI.escape(backup_file)).to_s expected << file_url - stub_request(:get, file_url). - to_return(status: 200, body: File.new(@fixtures_dir + '/wp-config.php')) + stub_request_to_fixture(url: file_url, fixture: @fixtures_dir + '/wp-config.php') end wp_config_backup = @module.config_backup diff --git a/spec/lib/wpscan/web_site_spec.rb b/spec/lib/wpscan/web_site_spec.rb index 67df2aca..e4196e27 100644 --- a/spec/lib/wpscan/web_site_spec.rb +++ b/spec/lib/wpscan/web_site_spec.rb @@ -21,6 +21,14 @@ describe 'WebSite' do let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WEB_SITE_DIR } subject(:web_site) { WebSite.new('http://example.localhost/') } + before :all do + Browser::reset + Browser.instance( + config_file: SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json', + cache_ttl: 0 + ) + end + describe "#new" do its(:url) { should === 'http://example.localhost/' } end @@ -74,7 +82,7 @@ describe 'WebSite' do it 'should return the correct url : http://example.localhost/xmlrpc.php' do xmlrpc = 'http://example.localhost/xmlrpc.php' stub_request(:get, web_site.url). - to_return(status: 200, body: '', headers: { 'X-Pingback' => xmlrpc}) + to_return(status: 200, headers: { 'X-Pingback' => xmlrpc }) web_site.xml_rpc_url.should === xmlrpc end @@ -88,7 +96,7 @@ describe 'WebSite' do describe '#has_xml_rpc?' do it 'should return true' do stub_request(:get, web_site.url). - to_return(status: 200, body: '', headers: { 'X-Pingback' => 'xmlrpc'}) + to_return(status: 200, headers: { 'X-Pingback' => 'xmlrpc' }) web_site.should have_xml_rpc end diff --git a/spec/lib/wpscan/wp_target_spec.rb b/spec/lib/wpscan/wp_target_spec.rb index 45ddf4c4..6d0e141f 100644 --- a/spec/lib/wpscan/wp_target_spec.rb +++ b/spec/lib/wpscan/wp_target_spec.rb @@ -24,11 +24,11 @@ describe WpTarget do let(:target_url) { 'http://example.localhost/' } before :each do - Browser.reset + Browser::reset @options = { config_file: SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json', - cache_timeout: 0, + cache_ttl: 0, wp_content_dir: 'wp-content', wp_plugins_dir: 'wp-content/plugins' } diff --git a/spec/lib/wpscan/wpscan_helper.rb b/spec/lib/wpscan/wpscan_helper.rb index c5ea8c63..57349a08 100644 --- a/spec/lib/wpscan/wpscan_helper.rb +++ b/spec/lib/wpscan/wpscan_helper.rb @@ -39,9 +39,10 @@ class WpScanModuleSpec def initialize(target_url) @uri = URI.parse(add_trailing_slash(add_http_protocol(target_url))) + Browser::reset Browser.instance( config_file: SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json', - cache_timeout: 0 + cache_ttl: 0 ) end diff --git a/spec/samples/conf/browser/browser.conf.json b/spec/samples/conf/browser/browser.conf.json index 356178c9..21464e08 100644 --- a/spec/samples/conf/browser/browser.conf.json +++ b/spec/samples/conf/browser/browser.conf.json @@ -1,7 +1,7 @@ { "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0", "user_agent_mode": "static", - "cache_timeout": 300, + "cache_ttl": 300, "request_timeout": 2000, "max_threads": 5 } diff --git a/spec/samples/conf/browser/browser.conf_proxy.json b/spec/samples/conf/browser/browser.conf_proxy.json index 37038c65..175569f4 100644 --- a/spec/samples/conf/browser/browser.conf_proxy.json +++ b/spec/samples/conf/browser/browser.conf_proxy.json @@ -2,6 +2,6 @@ "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", - "cache_timeout": 300, + "cache_ttl": 300, "request_timeout": 2000 } diff --git a/spec/samples/conf/browser/browser.conf_proxy_auth.json b/spec/samples/conf/browser/browser.conf_proxy_auth.json index 75e10fdc..b5fea1fd 100644 --- a/spec/samples/conf/browser/browser.conf_proxy_auth.json +++ b/spec/samples/conf/browser/browser.conf_proxy_auth.json @@ -3,6 +3,6 @@ "user_agent_mode": "static", "proxy": "127.0.0.1:3038", "proxy_auth": "user:pass", - "cache_timeout": 300, + "cache_ttl": 300, "request_timeout": 2000 }