From 4c57a006603c18c24f46f4e7756187d3912b3e36 Mon Sep 17 00:00:00 2001 From: erwanlr Date: Wed, 17 Apr 2013 12:48:18 +0200 Subject: [PATCH] Trying to reduce the complexity of WpUser#brute_force --- lib/common/models/wp_user.rb | 5 ++ lib/common/models/wp_user/brute_forcable.rb | 68 +++++++++++++------ spec/lib/common/models/wp_user_spec.rb | 4 ++ .../shared_examples/wp_user/brute_forcable.rb | 10 ++- 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/lib/common/models/wp_user.rb b/lib/common/models/wp_user.rb index d10116d5..13efcf96 100755 --- a/lib/common/models/wp_user.rb +++ b/lib/common/models/wp_user.rb @@ -21,6 +21,11 @@ class WpUser < WpItem end end + # @return [ String ] + def login_url + @uri.merge('wp-login.php').to_s + end + # @return [ String ] def to_s s = "#{id}" diff --git a/lib/common/models/wp_user/brute_forcable.rb b/lib/common/models/wp_user/brute_forcable.rb index 130f7af6..a98b4345 100644 --- a/lib/common/models/wp_user/brute_forcable.rb +++ b/lib/common/models/wp_user/brute_forcable.rb @@ -3,6 +3,16 @@ class WpUser < WpItem module BruteForcable + # Brute force the user with the wordlist supplied + # + # It can take a long time to queue 2 million requests, + # for that reason, we queue browser.max_threads, send browser.max_threads, + # queue browser.max_threads and so on. + # + # hydra.run only returns when it has recieved all of its, responses. + # This means that while we are waiting for browser.max_threads, + # responses, we are waiting... + # # @param [ String, Array ] wordlist The wordlist path # @param [ Hash ] options # @option options [ Boolean ] :verbose @@ -13,21 +23,12 @@ class WpUser < WpItem browser = Browser.instance hydra = browser.hydra passwords = BruteForcable.passwords_from_wordlist(wordlist) - login = self.login - login_url = @uri.merge('wp-login.php').to_s queue_count = 0 found = false - progress_bar = ProgressBar.create(format: '%t %a <%B> (%c / %C) %P%% %e', - title: " Brute Forcing '#{login}'", - length: 120, - total: passwords.size) if options[:show_progression] + progress_bar = self.progress_bar(passwords.size) if options[:show_progression] passwords.each do |password| - request = Browser.instance.forge_request(login_url, - method: :post, - body: { log: login, pwd: password }, - cache_ttl: 0 - ) + request = login_request(password) request.on_complete do |response| progress_bar.progress += 1 if options[:show_progression] && !found @@ -35,20 +36,15 @@ class WpUser < WpItem puts "\n Trying Username : #{login} Password : #{password}" if options[:verbose] if valid_password?(response, password, options) - self.password = password found = true - return # Used as break + self.password = password + return end end hydra.queue(request) queue_count += 1 - # it can take a long time to queue 2 million requests, - # for that reason, we queue @threads, send @threads, queue @threads and so on. - # hydra.run only returns when it has recieved all of its, - # responses. This means that while we are waiting for @threads, - # responses, we are waiting... if queue_count >= browser.max_threads hydra.run queue_count = 0 @@ -60,6 +56,31 @@ class WpUser < WpItem hydra.run end + # @param [ Integer ] password_size + # + # @return [ ProgressBar ] + # :nocov: + def progress_bar(passwords_size) + ProgressBar.create( + format: '%t %a <%B> (%c / %C) %P%% %e', + title: " Brute Forcing '#{login}'", + length: 120, + total: passwords_size + ) + end + # :nocov: + + # @param [ String ] password + # + # @return [ Typhoeus::Request ] + def login_request(password) + Browser.instance.forge_request(login_url, + method: :post, + body: { log: login, pwd: password }, + cache_ttl: 0 + ) + end + # @param [ Typhoeus::Response ] response # @param [ String ] password # @param [ Hash ] options @@ -82,10 +103,13 @@ class WpUser < WpItem else puts "\n " + red('ERROR:') + " We received an unknown response for #{password}..." if options[:show_progression] - # HACK to get the coverage :/ (otherwise some output is present in the rspec) - puts red(" Code: #{response.code}") if options[:verbose] - puts red(" Body: #{response.body}") if options[:verbose] - puts if options[:verbose] + # :nocov: + if options[:verbose] + puts red(" Code: #{response.code}") + puts red(" Body: #{response.body}") + puts + end + # :nocov: end false end diff --git a/spec/lib/common/models/wp_user_spec.rb b/spec/lib/common/models/wp_user_spec.rb index ce5c6e14..d144b5dc 100644 --- a/spec/lib/common/models/wp_user_spec.rb +++ b/spec/lib/common/models/wp_user_spec.rb @@ -34,6 +34,10 @@ describe WpUser do end end + describe '#login_url' do + its(:login_url) { should == 'http://example.com/wp-login.php' } + end + describe '#to_s' do after do subject.id = 1 diff --git a/spec/shared_examples/wp_user/brute_forcable.rb b/spec/shared_examples/wp_user/brute_forcable.rb index 32ac74a5..f3734cbf 100644 --- a/spec/shared_examples/wp_user/brute_forcable.rb +++ b/spec/shared_examples/wp_user/brute_forcable.rb @@ -5,7 +5,6 @@ shared_examples 'WpUser::BruteForcable' do let(:wordlist_iso) { fixtures_dir + '/wordlist-iso-8859-1.txt' } let(:wordlist_utf8) { fixtures_dir + '/wordlist-utf-8.txt' } let(:mod) { WpUser::BruteForcable } - let(:login_url) { uri.merge('wp-login.php').to_s } before { Browser.instance.max_threads = 1 } @@ -113,6 +112,11 @@ shared_examples 'WpUser::BruteForcable' do end end + # TODO + describe '#login_request' do + + end + describe '#brute_force' do let(:passwords) { %w{pass1 pass2 yolo kansei£Ô} } let(:login) { 'someuser' } @@ -125,7 +129,7 @@ shared_examples 'WpUser::BruteForcable' do context 'when no password is valid' do before do - stub_request(:post, login_url). + stub_request(:post, wp_user.login_url). #with(body: { log: login }). # produces an error : undefined method `split' for {:log=>"someuser", :pwd=>"password1"}:Hash to_return(body: 'login_error') end @@ -139,7 +143,7 @@ shared_examples 'WpUser::BruteForcable' do # Due to the error with .with(body: { log: login }) above # We can't use it to stub the request for a specific password # So, the first one will be valid - before { stub_request(:post, login_url).to_return(status: 302) } + before { stub_request(:post, wp_user.login_url).to_return(status: 302) } it 'sets the @password' do @expected = passwords[0]