Adds more improvements re passive scanning when there are a lot of urls
This commit is contained in:
@@ -9,7 +9,7 @@ module WPScan
|
||||
def passive(_opts = {})
|
||||
pattern = %r{#{target.content_dir}/mu\-plugins/}i
|
||||
|
||||
target.in_scope_uris(target.homepage_res) do |uri|
|
||||
target.in_scope_uris(target.homepage_res, '(//@href|//@src)[contains(., "mu-plugins")]') do |uri|
|
||||
next unless uri.path&.match?(pattern)
|
||||
|
||||
url = target.url('wp-content/mu-plugins/')
|
||||
|
||||
@@ -20,7 +20,7 @@ module WPScan
|
||||
end
|
||||
|
||||
def passive_from_css_href(res, opts)
|
||||
target.in_scope_uris(res, '//style/@src|//link/@href') do |uri|
|
||||
target.in_scope_uris(res, '//link/@href[contains(., "style.css")]') do |uri|
|
||||
next unless uri.path =~ %r{/themes/([^\/]+)/style.css\z}i
|
||||
|
||||
return create_theme(Regexp.last_match[1], uri.to_s, opts)
|
||||
|
||||
@@ -71,11 +71,13 @@ module WPScan
|
||||
return username, 'Display Name', 50 if username
|
||||
end
|
||||
|
||||
# @param [ String ] url
|
||||
# @param [ String, Addressable::URI ] uri
|
||||
#
|
||||
# @return [ String, nil ]
|
||||
def username_from_author_url(url)
|
||||
url[%r{/author/([^/\b]+)/?}i, 1]
|
||||
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
|
||||
|
||||
# @param [ Typhoeus::Response ] res
|
||||
@@ -83,12 +85,12 @@ module WPScan
|
||||
# @return [ String, nil ] The username found
|
||||
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)
|
||||
target.in_scope_uris(res, '//@href[contains(., "author/")]') do |uri|
|
||||
username = username_from_author_url(uri)
|
||||
return username if username
|
||||
end
|
||||
|
||||
# No permalink
|
||||
# No permalink, TODO Maybe use xpath to extract the classes ?
|
||||
res.body[/<body class="archive author author-([^\s]+)[ "]/i, 1]
|
||||
end
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ module WPScan
|
||||
def potential_usernames(res)
|
||||
usernames = []
|
||||
|
||||
target.in_scope_uris(res, '//a/@href') do |uri, node|
|
||||
target.in_scope_uris(res, '//a/@href[contains(., "author")]') do |uri, node|
|
||||
if uri.path =~ %r{/author/([^/\b]+)/?\z}i
|
||||
usernames << [Regexp.last_match[1], 'Author Pattern', 100]
|
||||
elsif /author=[0-9]+/.match?(uri.query)
|
||||
|
||||
@@ -8,11 +8,15 @@ module WPScan
|
||||
# @param [ String ] type plugins / themes
|
||||
# @param [ Boolean ] uniq Wether or not to apply the #uniq on the results
|
||||
#
|
||||
# @return [Array<String> ] The plugins/themes detected in the href, src attributes of the homepage
|
||||
# @return [ Array<String> ] The plugins/themes detected in the href, src attributes of the page
|
||||
def items_from_links(type, uniq = true)
|
||||
found = []
|
||||
xpath = format(
|
||||
'(//@href|//@src|//@data-src)[contains(., "%s")]',
|
||||
type == 'plugins' ? target.plugins_dir : target.content_dir
|
||||
)
|
||||
|
||||
target.in_scope_uris(page_res) do |uri|
|
||||
target.in_scope_uris(page_res, xpath) do |uri|
|
||||
next unless uri.to_s =~ item_attribute_pattern(type)
|
||||
|
||||
slug = Regexp.last_match[1]&.strip
|
||||
|
||||
@@ -100,7 +100,7 @@ module WPScan
|
||||
|
||||
unless content_dir
|
||||
pattern = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze
|
||||
xpath = '//@href[contains(., "wp.com")]|//@src[contains(., "wp.com")]'
|
||||
xpath = '(//@href|//@src)[contains(., "wp.com")]'
|
||||
|
||||
uris_from_page(homepage_res, xpath) do |uri|
|
||||
return true if uri.to_s.match?(pattern)
|
||||
|
||||
@@ -6,8 +6,55 @@ describe WPScan::Finders::InterestingFindings::MuPlugins do
|
||||
let(:url) { 'http://ex.lo/' }
|
||||
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'mu_plugins') }
|
||||
|
||||
before do
|
||||
expect(target).to receive(:content_dir).at_least(1).and_return('wp-content')
|
||||
end
|
||||
|
||||
describe '#passive' do
|
||||
xit
|
||||
before { stub_request(:get, url).to_return(body: body) }
|
||||
|
||||
context 'when no uris' do
|
||||
let(:body) { '' }
|
||||
|
||||
its(:passive) { should be nil }
|
||||
end
|
||||
|
||||
context 'when a large amount of unrelated uris' do
|
||||
let(:body) do
|
||||
Array.new(250) { |i| "<a href='#{url}#{i}.html'>Some Link</a><img src='#{url}img-#{i}.png'/>" }.join("\n")
|
||||
end
|
||||
|
||||
it 'should not take a while to process the page' do
|
||||
time_start = Time.now
|
||||
result = finder.passive
|
||||
time_end = Time.now
|
||||
|
||||
expect(result).to be nil
|
||||
expect(time_end - time_start).to be < 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'when uris' do
|
||||
let(:body) { File.read(fixtures.join(fixture)) }
|
||||
|
||||
context 'when none matching' do
|
||||
let(:fixture) { 'no_match.html' }
|
||||
|
||||
its(:passive) { should be nil }
|
||||
end
|
||||
|
||||
context 'when matching via href' do
|
||||
let(:fixture) { 'match_href.html' }
|
||||
|
||||
its(:passive) { should be_a WPScan::Model::MuPlugins }
|
||||
end
|
||||
|
||||
context 'when matching from src' do
|
||||
let(:fixture) { 'match_src.html' }
|
||||
|
||||
its(:passive) { should be_a WPScan::Model::MuPlugins }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#aggressive' do
|
||||
|
||||
@@ -19,7 +19,7 @@ describe WPScan::Finders::Users::AuthorIdBruteForcing do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#potential_username' do
|
||||
describe '#username_from_response' do
|
||||
[
|
||||
'4.1.1', '4.1.1-permalink',
|
||||
'3.0', '3.0-permalink',
|
||||
@@ -32,6 +32,19 @@ describe WPScan::Finders::Users::AuthorIdBruteForcing do
|
||||
expect(finder.username_from_response(res)).to eql 'admin'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a lot of unrelated links' do
|
||||
it 'should not take a while to process the page' do
|
||||
body = Array.new(300) { |i| "<a href='#{url}#{i}.html'>Some Link</a>" }.join("\n")
|
||||
body << '<a href="https://wp.lab/author/test/">Link</a>'
|
||||
|
||||
time_start = Time.now
|
||||
expect(finder.username_from_response(Typhoeus::Response.new(body: body))).to eql 'test'
|
||||
time_end = Time.now
|
||||
|
||||
expect(time_end - time_start).to be < 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#display_name_from_body' do
|
||||
|
||||
@@ -16,12 +16,31 @@ describe WPScan::Finders::Users::AuthorPosts do
|
||||
|
||||
results = finder.potential_usernames(res)
|
||||
|
||||
expect(results).to eql([
|
||||
['admin', 'Author Pattern', 100],
|
||||
['admin display_name', 'Display Name', 30],
|
||||
['editor', 'Author Pattern', 100],
|
||||
['editor', 'Display Name', 30]
|
||||
])
|
||||
expect(results).to eql [
|
||||
['admin', 'Author Pattern', 100],
|
||||
['admin display_name', 'Display Name', 30],
|
||||
['editor', 'Author Pattern', 100],
|
||||
['editor', 'Display Name', 30]
|
||||
]
|
||||
end
|
||||
|
||||
context 'when a lot of unrelated uris' do
|
||||
it 'should not take a while to process the page' do
|
||||
body = Array.new(300) { |i| "<a href='#{url}#{i}.html'>Some Link</a>" }.join("\n")
|
||||
body << "<a href='#{url}author/admin/'>Other Link</a>"
|
||||
body << "<a href='#{url}?author=2'>user display name</a>"
|
||||
|
||||
time_start = Time.now
|
||||
results = finder.potential_usernames(Typhoeus::Response.new(body: body))
|
||||
time_end = Time.now
|
||||
|
||||
expect(results).to eql [
|
||||
['admin', 'Author Pattern', 100],
|
||||
['user display name', 'Display Name', 30]
|
||||
]
|
||||
|
||||
expect(time_end - time_start).to be < 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
2
spec/cache/.gitignore
vendored
2
spec/cache/.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
!.gitignore
|
||||
|
||||
16
spec/fixtures/finders/interesting_findings/mu_plugins/match_href.html
vendored
Normal file
16
spec/fixtures/finders/interesting_findings/mu_plugins/match_href.html
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="profile" href="http://gmpg.org/xfn/11">
|
||||
<link rel="pingback" href="http://ex.lo/xmlrpc.php">
|
||||
<script>(function(){document.documentElement.className='js'})();</script>
|
||||
<title>WP 5.3.2 | Just another WordPress site</title>
|
||||
<meta name='robots' content='noindex,follow' />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 5.3.2 » Feed" href="http://ex.lo/feed/" />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 5.3.2 » Comments Feed" href="http://ex.lo/comments/feed/" />
|
||||
<link rel='stylesheet' id='twentyfifteen-style-css' href='http://g.com/wp-content/themes/twentyfifteen/style.css?ver=5.3.2' type='text/css' media='all' />
|
||||
|
||||
|
||||
<link rel='stylesheet' id='wbb-css' href='https://ex.lo/wp-content/mu-plugins/bb-mods/css/style.css' type='text/css' media='all' />
|
||||
16
spec/fixtures/finders/interesting_findings/mu_plugins/match_src.html
vendored
Normal file
16
spec/fixtures/finders/interesting_findings/mu_plugins/match_src.html
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="profile" href="http://gmpg.org/xfn/11">
|
||||
<link rel="pingback" href="http://ex.lo/xmlrpc.php">
|
||||
<script>(function(){document.documentElement.className='js'})();</script>
|
||||
<title>WP 5.3.2 | Just another WordPress site</title>
|
||||
<meta name='robots' content='noindex,follow' />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 5.3.2 » Feed" href="http://ex.lo/feed/" />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 5.3.2 » Comments Feed" href="http://ex.lo/comments/feed/" />
|
||||
<link rel='stylesheet' id='twentyfifteen-style-css' href='http://g.com/wp-content/themes/twentyfifteen/style.css?ver=5.3.2' type='text/css' media='all' />
|
||||
|
||||
|
||||
<script type='text/javascript' src='https://ex.lo/wp-content/mu-plugins/library/js/jquery-core.js'></script>
|
||||
13
spec/fixtures/finders/interesting_findings/mu_plugins/no_match.html
vendored
Normal file
13
spec/fixtures/finders/interesting_findings/mu_plugins/no_match.html
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<link rel="profile" href="http://gmpg.org/xfn/11">
|
||||
<link rel="pingback" href="http://ex.lo/xmlrpc.php">
|
||||
<script>(function(){document.documentElement.className='js'})();</script>
|
||||
<title>WP 5.3.2 | Just another WordPress site</title>
|
||||
<meta name='robots' content='noindex,follow' />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 5.3.2 » Feed" href="http://ex.lo/feed/" />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 5.3.2 » Comments Feed" href="http://ex.lo/comments/feed/" />
|
||||
<link rel='stylesheet' id='twentyfifteen-style-css' href='http://g.com/wp-content/themes/twentyfifteen/style.css?ver=5.3.2' type='text/css' media='all' />
|
||||
@@ -15,8 +15,12 @@
|
||||
|
||||
<meta name='robots' content='noindex,follow' />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 4.1.1 » Feed" href="http://wp.lab/wordpress-4.1.1/feed/" />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 4.1.1 » Comments Feed" href="http://wp.lab/wordpress-4.1.1/comments/feed/" />
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 4.1.1 » Comments Feed" href="http://wp.lab/wordpress-4.1.1/comments
|
||||
/feed/" />
|
||||
|
||||
<a href="https://wp.lab/?s=/author/should-not-be-detected/">Link</a>
|
||||
<link rel="alternate" type="application/rss+xml" title="WP 4.1.1 » Posts by admin display_name Feed" href="http://wp.lab/wordpress-4.1.1/author/admin/feed/" />
|
||||
|
||||
<link rel='stylesheet' id='twentyfifteen-fonts-css' href='//fonts.googleapis.com/css?family=Noto+Sans%3A400italic%2C700italic%2C400%2C700%7CNoto+Serif%3A400italic%2C700italic%2C400%2C700%7CInconsolata%3A400%2C700&subset=latin%2Clatin-ext' type='text/css' media='all' />
|
||||
<link rel='stylesheet' id='genericons-css' href='http://wp.lab/wordpress-4.1.1/wp-content/themes/twentyfifteen/genericons/genericons.css?ver=3.2' type='text/css' media='all' />
|
||||
<link rel='stylesheet' id='twentyfifteen-style-css' href='http://wp.lab/wordpress-4.1.1/wp-content/themes/twentyfifteen/style.css?ver=4.1.1' type='text/css' media='all' />
|
||||
|
||||
@@ -215,10 +215,6 @@ div.break_footer
|
||||
document.adoffset = 0;
|
||||
document.adPopupFile = '/cnn_adspaces/adsPopup2.html';
|
||||
</script>
|
||||
<script type="text/javascript" src="https://s2.wp.com/wp-content/themes/vip/lab/js/jquery-1.8.3.min.js"></script>
|
||||
<script type="text/javascript" src="https://s2.wp.com/wp-content/themes/vip/lab/js/jquery.vticker-min.js"></script>
|
||||
<script type="text/javascript" src="https://s2.wp.com/wp-content/themes/vip/lab/js/adspaces.js"></script>
|
||||
<script type="text/javascript" src="https://s2.wp.com/wp-content/themes/vip/lab/js/nbaOmEvent.js"></script>
|
||||
<script type="text/javascript" src="http://i.cdn.turner.com/ads/adfuel/ais/nba-ais.js"></script>
|
||||
|
||||
<script language="JavaScript">
|
||||
12
spec/fixtures/target/platform/wordpress/wordpress_hosted/match_src.html
vendored
Normal file
12
spec/fixtures/target/platform/wordpress/wordpress_hosted/match_src.html
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
||||
|
||||
<head profile="http://gmpg.org/xfn/11">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
<title>WP Lab</title>
|
||||
|
||||
<script type="text/javascript" src="https://s2.wp.com/wp-content/themes/vip/lab/js/jquery-1.8.3.min.js"></script>
|
||||
<script type="text/javascript" src="https://s2.wp.com/wp-content/themes/vip/lab/js/jquery.vticker-min.js"></script>
|
||||
<script type="text/javascript" src="https://s2.wp.com/wp-content/themes/vip/lab/js/adspaces.js"></script>
|
||||
<script type="text/javascript" src="https://s2.wp.com/wp-content/themes/vip/lab/js/nbaOmEvent.js"></script>
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
shared_examples 'App::Finders::WpItems::UrlsInPage' do
|
||||
before do
|
||||
stub_request(:get, page_url).to_return(body: File.read(fixtures.join(file)))
|
||||
allow(finder.target).to receive(:content_dir).and_return('wp-content')
|
||||
|
||||
stub_request(:get, page_url).to_return(body: defined?(body) ? body : File.read(fixtures.join(fixture)))
|
||||
end
|
||||
|
||||
describe '#items_from_links' do
|
||||
context 'when none found' do
|
||||
let(:file) { 'none.html' }
|
||||
let(:fixture) { 'none.html' }
|
||||
|
||||
it 'returns an empty array' do
|
||||
expect(finder.items_from_links(type)).to eql([])
|
||||
@@ -15,21 +17,31 @@ shared_examples 'App::Finders::WpItems::UrlsInPage' do
|
||||
end
|
||||
|
||||
context 'when found' do
|
||||
let(:file) { 'found.html' }
|
||||
let(:fixture) { 'found.html' }
|
||||
|
||||
it 'returns the expected array' do
|
||||
expect(finder.target).to receive(:content_dir).at_least(1).and_return('wp-content')
|
||||
|
||||
expect(finder.items_from_links(type, uniq_links)).to eql expected_from_links
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a lof of unrelated links' do
|
||||
let(:body) do
|
||||
Array.new(250) { |i| "<a href='#{url}#{i}.html'>Link</a><img src='#{url}img-#{i}.gif'/>" }.join("\n")
|
||||
end
|
||||
|
||||
it 'should not take a while to process the page' do
|
||||
time_start = Time.now
|
||||
expect(finder.items_from_links(type)).to eql []
|
||||
time_end = Time.now
|
||||
|
||||
expect(time_end - time_start).to be < 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#items_from_codes' do
|
||||
before { expect(finder.target).to receive(:content_dir).at_least(1).and_return('wp-content') }
|
||||
|
||||
context 'when none found' do
|
||||
let(:file) { 'none.html' }
|
||||
let(:fixture) { 'none.html' }
|
||||
|
||||
it 'returns an empty array' do
|
||||
expect(finder.items_from_codes(type)).to eql([])
|
||||
@@ -37,7 +49,7 @@ shared_examples 'App::Finders::WpItems::UrlsInPage' do
|
||||
end
|
||||
|
||||
context 'when found' do
|
||||
let(:file) { 'found.html' }
|
||||
let(:fixture) { 'found.html' }
|
||||
|
||||
it 'returns the expected array' do
|
||||
expect(finder.items_from_codes(type, uniq_codes)).to eql expected_from_codes
|
||||
|
||||
@@ -172,11 +172,19 @@ shared_examples WPScan::Target::Platform::WordPress do
|
||||
context 'when wp-content not detected' do
|
||||
before do
|
||||
expect(target).to receive(:content_dir).and_return(nil)
|
||||
stub_request(:get, target.url).to_return(body: File.read(fixtures.join(fixture).to_s))
|
||||
|
||||
stub_request(:get, target.url)
|
||||
.to_return(body: defined?(body) ? body : File.read(fixtures.join(fixture).to_s))
|
||||
end
|
||||
|
||||
context 'when an URL matches a WP hosted' do
|
||||
let(:fixture) { 'matches.html' }
|
||||
context 'when an src URL matches a WP hosted' do
|
||||
let(:fixture) { 'match_src.html' }
|
||||
|
||||
its(:wordpress_hosted?) { should be true }
|
||||
end
|
||||
|
||||
context 'when an href URL matches a WP hosted' do
|
||||
let(:fixture) { 'match_href.html' }
|
||||
|
||||
its(:wordpress_hosted?) { should be true }
|
||||
end
|
||||
@@ -186,6 +194,20 @@ shared_examples WPScan::Target::Platform::WordPress do
|
||||
|
||||
its(:wordpress_hosted?) { should be false }
|
||||
end
|
||||
|
||||
context 'when a lof of unrelated urls' do
|
||||
let(:body) do
|
||||
Array.new(250) { |i| "<a href='#{url}#{i}.html'>Some Link</a><img src='#{url}img-#{i}.png'/>" }.join("\n")
|
||||
end
|
||||
|
||||
it 'should not take a while to process the page' do
|
||||
time_start = Time.now
|
||||
expect(target.wordpress_hosted?).to be false
|
||||
time_end = Time.now
|
||||
|
||||
expect(time_end - time_start).to be < 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when wp-content detected' do
|
||||
|
||||
Reference in New Issue
Block a user