Fixes #1529
This commit is contained in:
@@ -6,6 +6,7 @@ require_relative 'users/oembed_api'
|
|||||||
require_relative 'users/rss_generator'
|
require_relative 'users/rss_generator'
|
||||||
require_relative 'users/author_id_brute_forcing'
|
require_relative 'users/author_id_brute_forcing'
|
||||||
require_relative 'users/login_error_messages'
|
require_relative 'users/login_error_messages'
|
||||||
|
require_relative 'users/author_sitemap'
|
||||||
require_relative 'users/yoast_seo_author_sitemap'
|
require_relative 'users/yoast_seo_author_sitemap'
|
||||||
|
|
||||||
module WPScan
|
module WPScan
|
||||||
@@ -22,6 +23,7 @@ module WPScan
|
|||||||
Users::WpJsonApi.new(target) <<
|
Users::WpJsonApi.new(target) <<
|
||||||
Users::OembedApi.new(target) <<
|
Users::OembedApi.new(target) <<
|
||||||
Users::RSSGenerator.new(target) <<
|
Users::RSSGenerator.new(target) <<
|
||||||
|
Users::AuthorSitemap.new(target) <<
|
||||||
Users::YoastSeoAuthorSitemap.new(target) <<
|
Users::YoastSeoAuthorSitemap.new(target) <<
|
||||||
Users::AuthorIdBruteForcing.new(target) <<
|
Users::AuthorIdBruteForcing.new(target) <<
|
||||||
Users::LoginErrorMessages.new(target)
|
Users::LoginErrorMessages.new(target)
|
||||||
|
|||||||
36
app/finders/users/author_sitemap.rb
Normal file
36
app/finders/users/author_sitemap.rb
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module WPScan
|
||||||
|
module Finders
|
||||||
|
module Users
|
||||||
|
# Since WP 5.5, /wp-sitemap-users-1.xml is generated and contains
|
||||||
|
# the usernames of accounts who made a post
|
||||||
|
class AuthorSitemap < CMSScanner::Finders::Finder
|
||||||
|
# @param [ Hash ] opts
|
||||||
|
#
|
||||||
|
# @return [ Array<User> ]
|
||||||
|
def aggressive(_opts = {})
|
||||||
|
found = []
|
||||||
|
|
||||||
|
Browser.get(sitemap_url).html.xpath('//url/loc').each do |user_tag|
|
||||||
|
username = user_tag.text.to_s[%r{/author/([^/]+)/}, 1]
|
||||||
|
|
||||||
|
next unless username && !username.strip.empty?
|
||||||
|
|
||||||
|
found << Model::User.new(username,
|
||||||
|
found_by: found_by,
|
||||||
|
confidence: 100,
|
||||||
|
interesting_entries: [sitemap_url])
|
||||||
|
end
|
||||||
|
|
||||||
|
found
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String ] The URL of the sitemap
|
||||||
|
def sitemap_url
|
||||||
|
@sitemap_url ||= target.url('wp-sitemap-users-1.xml')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -5,27 +5,7 @@ module WPScan
|
|||||||
module Users
|
module Users
|
||||||
# The YOAST SEO plugin has an author-sitemap.xml which can leak usernames
|
# The YOAST SEO plugin has an author-sitemap.xml which can leak usernames
|
||||||
# See https://github.com/wpscanteam/wpscan/issues/1228
|
# See https://github.com/wpscanteam/wpscan/issues/1228
|
||||||
class YoastSeoAuthorSitemap < CMSScanner::Finders::Finder
|
class YoastSeoAuthorSitemap < AuthorSitemap
|
||||||
# @param [ Hash ] opts
|
|
||||||
#
|
|
||||||
# @return [ Array<User> ]
|
|
||||||
def aggressive(_opts = {})
|
|
||||||
found = []
|
|
||||||
|
|
||||||
Browser.get(sitemap_url).html.xpath('//url/loc').each do |user_tag|
|
|
||||||
username = user_tag.text.to_s[%r{/author/([^/]+)/}, 1]
|
|
||||||
|
|
||||||
next unless username && !username.strip.empty?
|
|
||||||
|
|
||||||
found << Model::User.new(username,
|
|
||||||
found_by: found_by,
|
|
||||||
confidence: 100,
|
|
||||||
interesting_entries: [sitemap_url])
|
|
||||||
end
|
|
||||||
|
|
||||||
found
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [ String ] The URL of the author-sitemap
|
# @return [ String ] The URL of the author-sitemap
|
||||||
def sitemap_url
|
def sitemap_url
|
||||||
@sitemap_url ||= target.url('author-sitemap.xml')
|
@sitemap_url ||= target.url('author-sitemap.xml')
|
||||||
|
|||||||
48
spec/app/finders/users/author_sitemap_spec.rb
Normal file
48
spec/app/finders/users/author_sitemap_spec.rb
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
describe WPScan::Finders::Users::AuthorSitemap do
|
||||||
|
subject(:finder) { described_class.new(target) }
|
||||||
|
let(:target) { WPScan::Target.new(url) }
|
||||||
|
let(:url) { 'http://wp.lab/' }
|
||||||
|
let(:fixtures) { FINDERS_FIXTURES.join('users', 'author_sitemap') }
|
||||||
|
|
||||||
|
describe '#aggressive' do
|
||||||
|
before do
|
||||||
|
allow(target).to receive(:sub_dir).and_return(false)
|
||||||
|
|
||||||
|
stub_request(:get, finder.sitemap_url).to_return(body: body)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not an XML response' do
|
||||||
|
let(:body) { '' }
|
||||||
|
|
||||||
|
its(:aggressive) { should eql([]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when an XML response' do
|
||||||
|
context 'when no usernames disclosed' do
|
||||||
|
let(:body) { File.read(fixtures.join('no_usernames.xml')) }
|
||||||
|
|
||||||
|
its(:aggressive) { should eql([]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when usernames disclosed' do
|
||||||
|
let(:body) { File.read(fixtures.join('usernames.xml')) }
|
||||||
|
|
||||||
|
it 'returns the expected array of users' do
|
||||||
|
users = finder.aggressive
|
||||||
|
|
||||||
|
expect(users.size).to eql 2
|
||||||
|
|
||||||
|
expect(users.first.username).to eql 'admin'
|
||||||
|
expect(users.first.confidence).to eql 100
|
||||||
|
expect(users.first.interesting_entries).to eql ['http://wp.lab/wp-sitemap-users-1.xml']
|
||||||
|
|
||||||
|
expect(users.last.username).to eql 'author'
|
||||||
|
expect(users.last.confidence).to eql 100
|
||||||
|
expect(users.last.interesting_entries).to eql ['http://wp.lab/wp-sitemap-users-1.xml']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -8,7 +8,7 @@ describe WPScan::Finders::Users::Base do
|
|||||||
describe '#finders' do
|
describe '#finders' do
|
||||||
it 'contains the expected finders' do
|
it 'contains the expected finders' do
|
||||||
expect(user.finders.map { |f| f.class.to_s.demodulize })
|
expect(user.finders.map { |f| f.class.to_s.demodulize })
|
||||||
.to eq %w[AuthorPosts WpJsonApi OembedApi RSSGenerator YoastSeoAuthorSitemap
|
.to eq %w[AuthorPosts WpJsonApi OembedApi RSSGenerator AuthorSitemap YoastSeoAuthorSitemap
|
||||||
AuthorIdBruteForcing LoginErrorMessages]
|
AuthorIdBruteForcing LoginErrorMessages]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
4
spec/fixtures/finders/users/author_sitemap/no_usernames.xml
vendored
Normal file
4
spec/fixtures/finders/users/author_sitemap/no_usernames.xml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<?xml-stylesheet type="text/xsl" href="http://wp.lab/wp-sitemap.xsl" ?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
</urlset>
|
||||||
6
spec/fixtures/finders/users/author_sitemap/usernames.xml
vendored
Normal file
6
spec/fixtures/finders/users/author_sitemap/usernames.xml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<?xml-stylesheet type="text/xsl" href="http://wp.lab/wp-sitemap.xsl" ?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<url><loc>http://wp.lab/author/admin/</loc></url>
|
||||||
|
<url><loc>http://wp.lab//author/author/</loc></url>
|
||||||
|
</urlset>
|
||||||
Reference in New Issue
Block a user