Skip to content

Commit 72d699b

Browse files
committed
Adds more improvements re passive scanning when there are a lot of urls
1 parent 7d2b8a2 commit 72d699b

File tree

18 files changed

+214
-38
lines changed

18 files changed

+214
-38
lines changed

app/finders/interesting_findings/mu_plugins.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class MuPlugins < CMSScanner::Finders::Finder
99
def passive(_opts = {})
1010
pattern = %r{#{target.content_dir}/mu\-plugins/}i
1111

12-
target.in_scope_uris(target.homepage_res) do |uri|
12+
target.in_scope_uris(target.homepage_res, '(//@href|//@src)[contains(., "mu-plugins")]') do |uri|
1313
next unless uri.path&.match?(pattern)
1414

1515
url = target.url('wp-content/mu-plugins/')

app/finders/main_theme/css_style_in_homepage.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def passive(opts = {})
2020
end
2121

2222
def passive_from_css_href(res, opts)
23-
target.in_scope_uris(res, '//style/@src|//link/@href') do |uri|
23+
target.in_scope_uris(res, '//link/@href[contains(., "style.css")]') do |uri|
2424
next unless uri.path =~ %r{/themes/([^\/]+)/style.css\z}i
2525

2626
return create_theme(Regexp.last_match[1], uri.to_s, opts)

app/finders/users/author_id_brute_forcing.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,26 @@ def potential_username(res)
7171
return username, 'Display Name', 50 if username
7272
end
7373

74-
# @param [ String ] url
74+
# @param [ String, Addressable::URI ] uri
7575
#
7676
# @return [ String, nil ]
77-
def username_from_author_url(url)
78-
url[%r{/author/([^/\b]+)/?}i, 1]
77+
def username_from_author_url(uri)
78+
uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
79+
80+
uri.path[%r{/author/([^/\b]+)/?}i, 1]
7981
end
8082

8183
# @param [ Typhoeus::Response ] res
8284
#
8385
# @return [ String, nil ] The username found
8486
def username_from_response(res)
8587
# Permalink enabled
86-
target.in_scope_uris(res, '//link/@href|//a/@href') do |uri|
87-
username = username_from_author_url(uri.to_s)
88+
target.in_scope_uris(res, '//@href[contains(., "author/")]') do |uri|
89+
username = username_from_author_url(uri)
8890
return username if username
8991
end
9092

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

app/finders/users/author_posts.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def usernames(_opts = {})
4545
def potential_usernames(res)
4646
usernames = []
4747

48-
target.in_scope_uris(res, '//a/@href') do |uri, node|
48+
target.in_scope_uris(res, '//a/@href[contains(., "author")]') do |uri, node|
4949
if uri.path =~ %r{/author/([^/\b]+)/?\z}i
5050
usernames << [Regexp.last_match[1], 'Author Pattern', 100]
5151
elsif /author=[0-9]+/.match?(uri.query)

app/finders/wp_items/urls_in_page.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@ module UrlsInPage
88
# @param [ String ] type plugins / themes
99
# @param [ Boolean ] uniq Wether or not to apply the #uniq on the results
1010
#
11-
# @return [Array<String> ] The plugins/themes detected in the href, src attributes of the homepage
11+
# @return [ Array<String> ] The plugins/themes detected in the href, src attributes of the page
1212
def items_from_links(type, uniq = true)
1313
found = []
14+
xpath = format(
15+
'(//@href|//@src|//@data-src)[contains(., "%s")]',
16+
type == 'plugins' ? target.plugins_dir : target.content_dir
17+
)
1418

15-
target.in_scope_uris(page_res) do |uri|
19+
target.in_scope_uris(page_res, xpath) do |uri|
1620
next unless uri.to_s =~ item_attribute_pattern(type)
1721

1822
slug = Regexp.last_match[1]&.strip

lib/wpscan/target/platform/wordpress.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def wordpress_hosted?
100100

101101
unless content_dir
102102
pattern = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze
103-
xpath = '//@href[contains(., "wp.com")]|//@src[contains(., "wp.com")]'
103+
xpath = '(//@href|//@src)[contains(., "wp.com")]'
104104

105105
uris_from_page(homepage_res, xpath) do |uri|
106106
return true if uri.to_s.match?(pattern)

spec/app/finders/interesting_findings/mu_plugins_spec.rb

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,55 @@
66
let(:url) { 'http://ex.lo/' }
77
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'mu_plugins') }
88

9+
before do
10+
expect(target).to receive(:content_dir).at_least(1).and_return('wp-content')
11+
end
12+
913
describe '#passive' do
10-
xit
14+
before { stub_request(:get, url).to_return(body: body) }
15+
16+
context 'when no uris' do
17+
let(:body) { '' }
18+
19+
its(:passive) { should be nil }
20+
end
21+
22+
context 'when a large amount of unrelated uris' do
23+
let(:body) do
24+
Array.new(250) { |i| "<a href='#{url}#{i}.html'>Some Link</a><img src='#{url}img-#{i}.png'/>" }.join("\n")
25+
end
26+
27+
it 'should not take a while to process the page' do
28+
time_start = Time.now
29+
result = finder.passive
30+
time_end = Time.now
31+
32+
expect(result).to be nil
33+
expect(time_end - time_start).to be < 1
34+
end
35+
end
36+
37+
context 'when uris' do
38+
let(:body) { File.read(fixtures.join(fixture)) }
39+
40+
context 'when none matching' do
41+
let(:fixture) { 'no_match.html' }
42+
43+
its(:passive) { should be nil }
44+
end
45+
46+
context 'when matching via href' do
47+
let(:fixture) { 'match_href.html' }
48+
49+
its(:passive) { should be_a WPScan::Model::MuPlugins }
50+
end
51+
52+
context 'when matching from src' do
53+
let(:fixture) { 'match_src.html' }
54+
55+
its(:passive) { should be_a WPScan::Model::MuPlugins }
56+
end
57+
end
1158
end
1259

1360
describe '#aggressive' do

spec/app/finders/users/author_id_brute_forcing_spec.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
end
2020
end
2121

22-
describe '#potential_username' do
22+
describe '#username_from_response' do
2323
[
2424
'4.1.1', '4.1.1-permalink',
2525
'3.0', '3.0-permalink',
@@ -32,6 +32,19 @@
3232
expect(finder.username_from_response(res)).to eql 'admin'
3333
end
3434
end
35+
36+
context 'when a lot of unrelated links' do
37+
it 'should not take a while to process the page' do
38+
body = Array.new(300) { |i| "<a href='#{url}#{i}.html'>Some Link</a>" }.join("\n")
39+
body << '<a href="https://wp.lab/author/test/">Link</a>'
40+
41+
time_start = Time.now
42+
expect(finder.username_from_response(Typhoeus::Response.new(body: body))).to eql 'test'
43+
time_end = Time.now
44+
45+
expect(time_end - time_start).to be < 1
46+
end
47+
end
3548
end
3649

3750
describe '#display_name_from_body' do

spec/app/finders/users/author_posts_spec.rb

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,31 @@
1616

1717
results = finder.potential_usernames(res)
1818

19-
expect(results).to eql([
20-
['admin', 'Author Pattern', 100],
21-
['admin display_name', 'Display Name', 30],
22-
['editor', 'Author Pattern', 100],
23-
['editor', 'Display Name', 30]
24-
])
19+
expect(results).to eql [
20+
['admin', 'Author Pattern', 100],
21+
['admin display_name', 'Display Name', 30],
22+
['editor', 'Author Pattern', 100],
23+
['editor', 'Display Name', 30]
24+
]
25+
end
26+
27+
context 'when a lot of unrelated uris' do
28+
it 'should not take a while to process the page' do
29+
body = Array.new(300) { |i| "<a href='#{url}#{i}.html'>Some Link</a>" }.join("\n")
30+
body << "<a href='#{url}author/admin/'>Other Link</a>"
31+
body << "<a href='#{url}?author=2'>user display name</a>"
32+
33+
time_start = Time.now
34+
results = finder.potential_usernames(Typhoeus::Response.new(body: body))
35+
time_end = Time.now
36+
37+
expect(results).to eql [
38+
['admin', 'Author Pattern', 100],
39+
['user display name', 'Display Name', 30]
40+
]
41+
42+
expect(time_end - time_start).to be < 1
43+
end
2544
end
2645
end
2746
end

spec/cache/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Ignore everything in this directory
22
*
33
# Except this file
4-
!.gitignore
4+
!.gitignore

0 commit comments

Comments
 (0)