Path: blob/master/app/finders/users/author_id_brute_forcing.rb
485 views
# frozen_string_literal: true12module WPScan3module Finders4module Users5# Author Id Brute Forcing6class AuthorIdBruteForcing < CMSScanner::Finders::Finder7include CMSScanner::Finders::Finder::Enumerator89# @return [ Array<Integer> ]10def valid_response_codes11@valid_response_codes ||= [200, 301, 302]12end1314# @param [ Hash ] opts15# @option opts [ Range ] :range Mandatory16#17# @return [ Array<User> ]18def aggressive(opts = {})19found = []20found_by_msg = 'Author Id Brute Forcing - %s (Aggressive Detection)'2122enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, id|23username, found_by, confidence = potential_username(res)2425next unless username2627found << Model::User.new(28username,29id: id,30found_by: format(found_by_msg, found_by),31confidence: confidence32)33end3435found36end3738# @param [ Hash ] opts39# @option opts [ Range ] :range40#41# @return [ Hash ]42def target_urls(opts = {})43urls = {}4445opts[:range].each do |id|46urls[target.uri.join("?author=#{id}").to_s] = id47end4849urls50end5152def create_progress_bar(opts = {})53super(opts.merge(title: ' Brute Forcing Author IDs -'))54end5556def full_request_params57{ followlocation: true }58end5960# @param [ Typhoeus::Response ] res61#62# @return [ Array<String, String, Integer>, nil ] username, found_by, confidence63def potential_username(res)64username = username_from_author_url(res.effective_url) || username_from_response(res)6566return username, 'Author Pattern', 100 if username6768username = display_name_from_body(res.body)6970return username, 'Display Name', 50 if username71end7273# @param [ String, Addressable::URI ] uri74#75# @return [ String, nil ]76def username_from_author_url(uri)77uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)7879uri.path[%r{/author/([^/\b]+)/?}i, 1]80end8182# @param [ Typhoeus::Response ] res83#84# @return [ String, nil ] The username found85def username_from_response(res)86# Permalink enabled87target.in_scope_uris(res, '//@href[contains(., "author/")]') do |uri|88username = username_from_author_url(uri)89return username if username90end9192# No permalink, TODO Maybe use xpath to extract the classes ?93res.body[/<body class="archive author author-([^\s]+)[ "]/i, 1]94end9596# @param [ String ] body97#98# @return [ String, nil ]99def display_name_from_body(body)100page = Nokogiri::HTML.parse(body)101102# WP >= 3.0103page.css('h1.page-title span').each do |node|104text = node.text.to_s.strip105106return text unless text.empty?107end108109# WP < 3.0110page.xpath('//link[@rel="alternate" and @type="application/rss+xml"]').each do |node|111title = node['title']112113next unless title =~ /Posts by (.*) Feed\z/i114115return Regexp.last_match[1] unless Regexp.last_match[1].empty?116end117nil118end119end120end121end122end123124125