Class: WPScan::Finders::Users::AuthorIdBruteForcing

Inherits:
Finder
  • Object
show all
Includes:
Finder::Enumerator
Defined in:
app/finders/users/author_id_brute_forcing.rb

Overview

Author Id Brute Forcing

Constant Summary

Constants inherited from Finder

Finder::DIRECT_ACCESS

Instance Attribute Summary

Attributes inherited from Finder

#progress_bar, #target

Instance Method Summary collapse

Methods included from Finder::Enumerator

#enumerate, #head_or_get_request_params, #maybe_get_full_response

Methods inherited from Finder

#browser, #found_by, #hydra, #initialize, #passive, #titleize

Constructor Details

This class inherits a constructor from WPScan::Finders::Finder

Instance Method Details

#aggressive(opts = {}) ⇒ Array<User>

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

Returns:

  • (Array<User>)


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'app/finders/users/author_id_brute_forcing.rb', line 21

def aggressive(opts = {})
  shared       = opts[:found]
  local        = shared ? nil : []
  found_by_msg = 'Author Id Brute Forcing - %s (Aggressive Detection)'

  enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, id|
    username, found_by, confidence = potential_username(res)

    next unless username

    user = Model::User.new(
      username,
      id: id,
      found_by: format(found_by_msg, found_by),
      confidence: confidence
    )
    (shared || local) << user
  end

  local || []
end

#create_progress_bar(opts = {}) ⇒ Object



57
58
59
# File 'app/finders/users/author_id_brute_forcing.rb', line 57

def create_progress_bar(opts = {})
  super(opts.merge(title: ' Brute Forcing Author IDs -'))
end

#display_name_from_body(body) ⇒ String?

Parameters:

  • body (String)

Returns:

  • (String, nil)


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'app/finders/users/author_id_brute_forcing.rb', line 104

def display_name_from_body(body)
  page = Nokogiri::HTML.parse(body)

  # WP >= 3.0
  page.css('h1.page-title span').each do |node|
    text = node.text.to_s.strip

    return text unless text.empty?
  end

  # WP < 3.0
  page.xpath('//link[@rel="alternate" and @type="application/rss+xml"]').each do |node|
    title = node['title']

    next unless title =~ /Posts by (.*) Feed\z/i

    return Regexp.last_match[1] unless Regexp.last_match[1].empty?
  end
  nil
end

#full_request_paramsObject



61
62
63
# File 'app/finders/users/author_id_brute_forcing.rb', line 61

def full_request_params
  { followlocation: true, maxredirs: 10 }
end

#potential_username(res) ⇒ Array<String, String, Integer>?

Returns username, found_by, confidence.

Parameters:

Returns:

  • (Array<String, String, Integer>, nil)

    username, found_by, confidence



68
69
70
71
72
73
74
75
76
# File 'app/finders/users/author_id_brute_forcing.rb', line 68

def potential_username(res)
  username = username_from_author_url(res.effective_url) || username_from_response(res)

  return username, 'Author Pattern', 100 if username

  username = display_name_from_body(res.body)

  [username, 'Display Name', 50] if username
end

#target_urls(opts = {}) ⇒ Hash

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :range (Range)

Returns:

  • (Hash)


47
48
49
50
51
52
53
54
55
# File 'app/finders/users/author_id_brute_forcing.rb', line 47

def target_urls(opts = {})
  urls = {}

  opts[:range].each do |id|
    urls[target.uri.join("?author=#{id}").to_s] = id
  end

  urls
end

#username_from_author_url(uri) ⇒ String?

Parameters:

  • uri (String, Addressable::URI)

Returns:

  • (String, nil)


81
82
83
84
85
# File 'app/finders/users/author_id_brute_forcing.rb', line 81

def username_from_author_url(uri)
  uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)

  uri.path[%r{/author/([^/\b]+)/?}i, 1]
end

#username_from_response(res) ⇒ String?

Returns The username found.

Parameters:

Returns:

  • (String, nil)

    The username found



90
91
92
93
94
95
96
97
98
99
# File 'app/finders/users/author_id_brute_forcing.rb', line 90

def username_from_response(res)
  # Permalink enabled
  target.in_scope_uris(res, '//@href[contains(., "author/")]') do |uri|
    username = username_from_author_url(uri)
    return username if username
  end

  # No permalink, TODO Maybe use xpath to extract the classes ?
  res.body[/<body class="archive author author-([^\s]+)[ "]/i, 1]
end

#valid_response_codesArray<Integer>

Returns:

  • (Array<Integer>)


11
12
13
# File 'app/finders/users/author_id_brute_forcing.rb', line 11

def valid_response_codes
  @valid_response_codes ||= [200, 301, 302]
end