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

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

Overview

Author Id Brute Forcing

Instance Method Summary collapse

Instance Method Details

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

Parameters:

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

Options Hash (opts):

  • :range (Range)

    Mandatory

Returns:

  • (Array<User>)


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

def aggressive(opts = {})
  found = []
  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

    found << Model::User.new(
      username,
      id: id,
      found_by: format(found_by_msg, found_by),
      confidence: confidence
    )
  end

  found
end

#create_progress_bar(opts = {}) ⇒ Object



53
54
55
# File 'app/finders/users/author_id_brute_forcing.rb', line 53

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)


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'app/finders/users/author_id_brute_forcing.rb', line 98

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



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

def full_request_params
  { followlocation: true }
end

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

Returns username, found_by, confidence.

Parameters:

Returns:

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

    username, found_by, confidence



64
65
66
67
68
69
70
71
72
# File 'app/finders/users/author_id_brute_forcing.rb', line 64

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)

  return username, 'Display Name', 50 if username
end

#target_urls(opts = {}) ⇒ Hash

Parameters:

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

Options Hash (opts):

  • :range (Range)

Returns:

  • (Hash)


43
44
45
46
47
48
49
50
51
# File 'app/finders/users/author_id_brute_forcing.rb', line 43

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(url) ⇒ String?

Parameters:

  • url (String)

Returns:

  • (String, nil)


77
78
79
# File 'app/finders/users/author_id_brute_forcing.rb', line 77

def username_from_author_url(url)
  url[%r{/author/([^/\b]+)/?}i, 1]
end

#username_from_response(res) ⇒ String?

Returns The username found.

Parameters:

Returns:

  • (String, nil)

    The username found



84
85
86
87
88
89
90
91
92
93
# File 'app/finders/users/author_id_brute_forcing.rb', line 84

def username_from_response(res)
  # Permalink enabled
  target.in_scope_uris(res, '//link/@href|//a/@href') do |uri|
    username = username_from_author_url(uri.to_s)
    return username if username
  end

  # No permalink
  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