Compare commits

...

10 Commits

Author SHA1 Message Date
erwanlr
c63804d1c5 Bumps version 2018-11-02 19:51:57 +00:00
erwanlr
c5e6752f75 Fixes #1232 2018-11-02 19:33:38 +00:00
erwanlr
e4f3e9d11c Fixes spec 2018-11-02 17:52:43 +00:00
erwanlr
f3713536b9 Adds missing spec files 2018-11-02 16:36:10 +00:00
erwanlr
fb751c0a51 Fixes #1228 2018-11-02 13:40:46 +00:00
erwanlr
9d3464055a Updates deps 2018-11-02 08:36:22 +00:00
erwanlr
0fea814f5d Fixes #1237 2018-11-02 08:33:21 +00:00
Ryan Dewhurst
ae70a6df9d Merge pull request #1233 from FenrirSec/fix_wordpress_hosted
Fixed pattern matching on target.wordpress_hosted
2018-10-29 13:46:50 +01:00
lp1
4afc756ccd Added spec for the new regex 2018-10-29 12:11:28 +01:00
lp1
adc5841261 Fixed too restrictive pattern matching on target.wordpress_hosted attribute 2018-10-25 18:09:04 +02:00
33 changed files with 204 additions and 51 deletions

View File

@@ -15,20 +15,20 @@ module WPScan
OptMultiChoices.new(
['-e', '--enumerate [OPTS]', 'Enumeration Process'],
choices: {
vp: OptBoolean.new(['--vulnerable-plugins']),
ap: OptBoolean.new(['--all-plugins']),
p: OptBoolean.new(['--plugins']),
vt: OptBoolean.new(['--vulnerable-themes']),
at: OptBoolean.new(['--all-themes']),
t: OptBoolean.new(['--themes']),
tt: OptBoolean.new(['--timthumbs']),
cb: OptBoolean.new(['--config-backups']),
vp: OptBoolean.new(['--vulnerable-plugins']),
ap: OptBoolean.new(['--all-plugins']),
p: OptBoolean.new(['--plugins']),
vt: OptBoolean.new(['--vulnerable-themes']),
at: OptBoolean.new(['--all-themes']),
t: OptBoolean.new(['--themes']),
tt: OptBoolean.new(['--timthumbs']),
cb: OptBoolean.new(['--config-backups']),
dbe: OptBoolean.new(['--db-exports']),
u: OptIntegerRange.new(['--users', 'User IDs range. e.g: u1-5'], value_if_empty: '1-10'),
m: OptIntegerRange.new(['--medias',
'Media IDs range. e.g m1-15',
'Note: Permalink setting must be set to "Plain" for those to be detected'],
value_if_empty: '1-100')
u: OptIntegerRange.new(['--users', 'User IDs range. e.g: u1-5'], value_if_empty: '1-10'),
m: OptIntegerRange.new(['--medias',
'Media IDs range. e.g m1-15',
'Note: Permalink setting must be set to "Plain" for those to be detected'],
value_if_empty: '1-100')
},
value_if_empty: 'vp,vt,tt,cb,dbe,u,m',
incompatible: [%i[vp ap p], %i[vt at t]],

View File

@@ -11,7 +11,7 @@ module WPScan
return unless [200, 403].include?(res.code) && !target.homepage_or_404?(res)
WPScan::InterestingFinding.new(
WPScan::BackupDB.new(
url,
confidence: 70,
found_by: DIRECT_ACCESS,

View File

@@ -9,7 +9,7 @@ module WPScan
return unless target.debug_log?(path)
WPScan::InterestingFinding.new(
WPScan::DebugLog.new(
target.url(path),
confidence: 100, found_by: DIRECT_ACCESS
)

View File

@@ -10,7 +10,7 @@ module WPScan
return unless res.body =~ /DUPLICATOR INSTALL-LOG/
WPScan::InterestingFinding.new(
WPScan::DuplicatorInstallerLog.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -10,7 +10,7 @@ module WPScan
return unless res.code == 200 && !target.homepage_or_404?(res)
WPScan::InterestingFinding.new(
WPScan::EmergencyPwdResetScript.new(
url,
confidence: res.body =~ /password/i ? 100 : 40,
found_by: DIRECT_ACCESS,

View File

@@ -10,7 +10,7 @@ module WPScan
return if fpd_entries.empty?
WPScan::InterestingFinding.new(
WPScan::FullPathDisclosure.new(
target.url(path),
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -12,7 +12,7 @@ module WPScan
url = target.url('wp-content/mu-plugins/')
return WPScan::InterestingFinding.new(
return WPScan::MuPlugins.new(
url,
confidence: 70,
found_by: 'URLs In Homepage (Passive Detection)',
@@ -35,7 +35,7 @@ module WPScan
target.mu_plugins = true
WPScan::InterestingFinding.new(
WPScan::MuPlugins.new(
url,
confidence: 80,
found_by: DIRECT_ACCESS,

View File

@@ -15,7 +15,7 @@ module WPScan
target.multisite = true
WPScan::InterestingFinding.new(
WPScan::Multisite.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -10,7 +10,7 @@ module WPScan
res = Browser.get(url)
if res.code == 200 && res.body =~ /wordpress/i
return WPScan::InterestingFinding.new(url, confidence: 100, found_by: DIRECT_ACCESS)
return WPScan::Readme.new(url, confidence: 100, found_by: DIRECT_ACCESS)
end
end
nil

View File

@@ -18,7 +18,7 @@ module WPScan
target.registration_enabled = true
WPScan::InterestingFinding.new(
WPScan::Registration.new(
res.effective_url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -11,7 +11,7 @@ module WPScan
return unless res.code == 200 && res.headers['Content-Type'] =~ %r{\Aapplication/zip}i
WPScan::InterestingFinding.new(
WPScan::TmmDbMigrate.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -11,7 +11,7 @@ module WPScan
url = target.url(path)
WPScan::InterestingFinding.new(
WPScan::UploadDirectoryListing.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -3,7 +3,7 @@ module WPScan
module InterestingFindings
# UploadSQLDump finder
class UploadSQLDump < CMSScanner::Finders::Finder
SQL_PATTERN = /(?:(?:(?:DROP|CREATE) TABLE)|INSERT INTO)/
SQL_PATTERN = /(?:(?:(?:DROP|CREATE) TABLE)|INSERT INTO)/.freeze
# @return [ InterestingFinding ]
def aggressive(_opts = {})
@@ -12,7 +12,7 @@ module WPScan
return unless res.code == 200 && res.body =~ SQL_PATTERN
WPScan::InterestingFinding.new(
WPScan::UploadSQLDump.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS

View File

@@ -3,9 +3,9 @@ module WPScan
module MainTheme
# From the WooFramework meta generators
class WooFrameworkMetaGenerator < CMSScanner::Finders::Finder
THEME_PATTERN = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?"\s+/?>}
FRAMEWORK_PATTERN = %r{<meta name="generator" content="WooFramework\s?([^"]+)?"\s+/?>}
PATTERN = /#{THEME_PATTERN}\s+#{FRAMEWORK_PATTERN}/i
THEME_PATTERN = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?"\s+/?>}.freeze
FRAMEWORK_PATTERN = %r{<meta name="generator" content="WooFramework\s?([^"]+)?"\s+/?>}.freeze
PATTERN = /#{THEME_PATTERN}\s+#{FRAMEWORK_PATTERN}/i.freeze
def passive(opts = {})
return unless target.homepage_res.body =~ PATTERN

View File

@@ -4,6 +4,7 @@ require_relative 'users/oembed_api'
require_relative 'users/rss_generator'
require_relative 'users/author_id_brute_forcing'
require_relative 'users/login_error_messages'
require_relative 'users/yoast_seo_author_sitemap.rb'
module WPScan
module Finders
@@ -19,6 +20,7 @@ module WPScan
Users::WpJsonApi.new(target) <<
Users::OembedApi.new(target) <<
Users::RSSGenerator.new(target) <<
Users::YoastSeoAuthorSitemap.new(target) <<
Users::AuthorIdBruteForcing.new(target) <<
Users::LoginErrorMessages.new(target)
end

View File

@@ -0,0 +1,34 @@
module WPScan
module Finders
module Users
# The YOAST SEO plugin has an author-sitemap.xml which can leak usernames
# See https://github.com/wpscanteam/wpscan/issues/1228
class YoastSeoAuthorSitemap < 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 << CMSScanner::User.new(username,
found_by: found_by,
confidence: 100,
interesting_entries: [sitemap_url])
end
found
end
# @return [ String ] The URL of the author-sitemap
def sitemap_url
@sitemap_url ||= target.url('author-sitemap.xml')
end
end
end
end
end

View File

@@ -3,4 +3,43 @@ module WPScan
class InterestingFinding < CMSScanner::InterestingFinding
include References
end
#
# Empty classes for the #type to be correctly displayed (as taken from the self.class from the parent)
#
class BackupDB < InterestingFinding
end
class DebugLog < InterestingFinding
end
class DuplicatorInstallerLog < InterestingFinding
end
class EmergencyPwdResetScript < InterestingFinding
end
class FullPathDisclosure < InterestingFinding
end
class MuPlugins < InterestingFinding
end
class Multisite < InterestingFinding
end
class Readme < InterestingFinding
end
class Registration < InterestingFinding
end
class TmmDbMigrate < InterestingFinding
end
class UploadDirectoryListing < InterestingFinding
end
class UploadSQLDump < InterestingFinding
end
end

View File

@@ -60,12 +60,11 @@ module WPScan
end
# @return [ Hash ] The params for Typhoeus::Request
# @note Those params can't be overriden by CLI options
def request_params
{
ssl_verifyhost: 2,
ssl_verifypeer: true,
timeout: 300,
connecttimeout: 120,
timeout: 600,
connecttimeout: 300,
accept_encoding: 'gzip, deflate',
cache_ttl: 0
}

View File

@@ -9,7 +9,7 @@ module WPScan
module WordPress
include CMSScanner::Target::Platform::PHP
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu\-)?plugins|uploads))|wp-includes)/}i
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu\-)?plugins|uploads))|wp-includes)/}i.freeze
# These methods are used in the associated interesting_findings finders
# to keep the boolean state of the finding rather than re-check the whole thing again
@@ -41,7 +41,7 @@ module WPScan
end
def wordpress_hosted?
uri.host =~ /wordpress.com$/i ? true : false
uri.host =~ /\.wordpress\.com$/i ? true : false
end
# @param [ String ] username

View File

@@ -1,4 +1,4 @@
# Version
module WPScan
VERSION = '3.3.2'.freeze
VERSION = '3.3.3'.freeze
end

View File

@@ -37,7 +37,7 @@ describe WPScan::Finders::InterestingFindings::BackupDB do
after do
found = finder.aggressive
expect(found).to eql WPScan::InterestingFinding.new(
expect(found).to eql WPScan::BackupDB.new(
dir_url,
confidence: 70,
found_by: described_class::DIRECT_ACCESS

View File

@@ -23,7 +23,7 @@ describe WPScan::Finders::InterestingFindings::DebugLog do
let(:body) { File.read(File.join(fixtures, 'debug.log')) }
it 'returns the InterestingFinding' do
expect(finder.aggressive).to eql WPScan::InterestingFinding.new(
expect(finder.aggressive).to eql WPScan::DebugLog.new(
log_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -24,7 +24,7 @@ describe WPScan::Finders::InterestingFindings::DuplicatorInstallerLog do
let(:body) { File.read(File.join(fixtures, filename)) }
it 'returns the InterestingFinding' do
expect(finder.aggressive).to eql WPScan::InterestingFinding.new(
expect(finder.aggressive).to eql WPScan::DuplicatorInstallerLog.new(
log_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -25,7 +25,7 @@ describe WPScan::Finders::InterestingFindings::FullPathDisclosure do
it 'returns the InterestingFinding' do
found = finder.aggressive
expect(found).to eql WPScan::InterestingFinding.new(
expect(found).to eql WPScan::FullPathDisclosure.new(
file_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -27,7 +27,7 @@ describe WPScan::Finders::InterestingFindings::Readme do
before { stub_request(:get, target.url(file)).to_return(body: readme) }
it 'returns the expected InterestingFinding' do
expected = WPScan::InterestingFinding.new(
expected = WPScan::Readme.new(
target.url(file),
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -38,7 +38,7 @@ describe WPScan::Finders::InterestingFindings::UploadSQLDump do
let(:fixture) { 'dump.sql' }
it 'returns the interesting findings' do
@expected = WPScan::InterestingFinding.new(
@expected = WPScan::UploadSQLDump.new(
finder.dump_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -0,0 +1,48 @@
require 'spec_helper'
describe WPScan::Finders::Users::YoastSeoAuthorSitemap do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { FINDERS_FIXTURES.join('users', 'yoast_seo_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 'editor'
expect(users.first.confidence).to eql 100
expect(users.first.interesting_entries).to eql ['http://wp.lab/author-sitemap.xml']
expect(users.last.username).to eql 'admin'
expect(users.last.confidence).to eql 100
expect(users.last.interesting_entries).to eql ['http://wp.lab/author-sitemap.xml']
end
end
end
end
end

View File

@@ -8,7 +8,8 @@ describe WPScan::Finders::Users::Base do
describe '#finders' do
it 'contains the expected finders' do
expect(user.finders.map { |f| f.class.to_s.demodulize })
.to eq %w[AuthorPosts WpJsonApi OembedApi RSSGenerator AuthorIdBruteForcing LoginErrorMessages]
.to eq %w[AuthorPosts WpJsonApi OembedApi RSSGenerator YoastSeoAuthorSitemap
AuthorIdBruteForcing LoginErrorMessages]
end
end
end

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//wp.lab/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl"?>
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
</urlset>
<!-- XML Sitemap generated by Yoast SEO -->

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//wp.lab/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl"?>
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://wp.lab/author/editor/</loc>
<lastmod>2018-10-22T19:56:51+00:00</lastmod>
</url>
<url>
<loc>http://wp.lab/author/admin/</loc>
<lastmod>2018-10-22T19:54:23+00:00</lastmod>
</url>
<url>
<loc>http://wp.lab/author//</loc>
<lastmod>2018-10-22T19:54:23+00:00</lastmod>
</url>
<url>
<loc>http://wp.lab/author/ /</loc>
<lastmod>2018-10-22T19:54:23+00:00</lastmod>
</url>
</urlset>
<!-- XML Sitemap generated by Yoast SEO -->

View File

@@ -37,5 +37,11 @@ shared_examples WPScan::Target::Platform::WordPress do
its(:wordpress_hosted?) { should be true }
end
context 'when the target host doesn\'t matches' do
let(:url) { 'http://ex-wordpress.com' }
its(:wordpress_hosted?) { should be false }
end
end
end

View File

@@ -97,9 +97,9 @@ module WebMock
end
# rubocop:enabled all
SPECS = Pathname.new(__FILE__).dirname.to_s
FIXTURES = File.join(SPECS, 'fixtures')
FINDERS_FIXTURES = File.join(FIXTURES, 'finders')
DYNAMIC_FINDERS_FIXTURES = File.join(FIXTURES, 'dynamic_finders')
SPECS = Pathname.new(__FILE__).dirname
FIXTURES = SPECS.join('fixtures')
FINDERS_FIXTURES = FIXTURES.join('finders')
DYNAMIC_FINDERS_FIXTURES = FIXTURES.join('dynamic_finders')
redefine_constant(:DB_DIR, File.join(FIXTURES, 'db'))
redefine_constant(:DB_DIR, FIXTURES.join('db'))

View File

@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
s.executables = ['wpscan']
s.require_paths = ['lib']
s.add_dependency 'cms_scanner', '~> 0.0.40.2'
s.add_dependency 'cms_scanner', '~> 0.0.40.3'
# Already required by CMSScanner, so version restrictions loosen
s.add_dependency 'activesupport', '~> 5.2'
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'rake', '~> 12.3'
s.add_development_dependency 'rspec', '~> 3.8.0'
s.add_development_dependency 'rspec-its', '~> 1.2.0'
s.add_development_dependency 'rubocop', '~> 0.59.2'
s.add_development_dependency 'rubocop', '~> 0.60.0'
s.add_development_dependency 'simplecov', '~> 0.16.1'
s.add_development_dependency 'webmock', '~> 3.4.2'
end