182 lines
6.6 KiB
Ruby
182 lines
6.6 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
class MetasploitModule < Msf::Auxiliary
|
|
include Msf::Exploit::Remote::HTTP::Wordpress
|
|
include Msf::Auxiliary::Scanner
|
|
include Msf::Auxiliary::Report
|
|
|
|
def initialize
|
|
super(
|
|
'Name' => 'Wordpress Scanner',
|
|
'Description' => 'Detects Wordpress Versions, Themes, Plugins, and Users',
|
|
'Author' => [
|
|
'Christian Mehlmauer', # original module
|
|
'h00die', # plugins and themes
|
|
'shoxxdj' # users
|
|
],
|
|
'License' => MSF_LICENSE
|
|
)
|
|
register_options [
|
|
OptBool.new('EXPLOITABLE', [false, 'Only scan plugins and themes which a MSF module exists for', true]),
|
|
OptPath.new('EXPLOITABLE_THEMES', [
|
|
true, 'File containing exploitable by MSF themes',
|
|
File.join(Msf::Config.data_directory, 'wordlists', 'wp-exploitable-themes.txt')
|
|
]),
|
|
OptPath.new('EXPLOITABLE_PLUGINS', [
|
|
true, 'File containing exploitable by MSF plugins',
|
|
File.join(Msf::Config.data_directory, 'wordlists', 'wp-exploitable-plugins.txt')
|
|
]),
|
|
OptBool.new('THEMES', [false, 'Detect themes', true]),
|
|
OptBool.new('PLUGINS', [false, 'Detect plugins', true]),
|
|
OptPath.new('THEMES_FILE', [
|
|
true, 'File containing themes to enumerate',
|
|
File.join(Msf::Config.data_directory, 'wordlists', 'wp-themes.txt')
|
|
]),
|
|
OptPath.new('PLUGINS_FILE', [
|
|
true, 'File containing plugins to enumerate',
|
|
File.join(Msf::Config.data_directory, 'wordlists', 'wp-plugins.txt')
|
|
]),
|
|
OptInt.new('PROGRESS', [true, 'how often to print progress', 1000]),
|
|
OptBool.new('USERS', [false, 'Detect users with API', true])
|
|
]
|
|
end
|
|
|
|
def print_progress(host, i, total)
|
|
print_status("#{host} - Progress #{i.to_s.rjust(Math.log10(total).ceil + 1)}/#{total} (#{((i.to_f / total) * 100).truncate(2)}%)")
|
|
end
|
|
|
|
def run_host(target_host)
|
|
print_status("Trying #{target_host}")
|
|
if wordpress_and_online?
|
|
version = wordpress_version
|
|
version_string = version || '(no version detected)'
|
|
print_good("#{target_host} - Detected Wordpress #{version_string}")
|
|
report_note(
|
|
{
|
|
host: target_host,
|
|
proto: 'tcp',
|
|
sname: (ssl ? 'https' : 'http'),
|
|
port: rport,
|
|
type: "Wordpress #{version_string}",
|
|
data: target_uri.to_s
|
|
}
|
|
)
|
|
if datastore['THEMES']
|
|
print_status("#{target_host} - Enumerating Themes")
|
|
|
|
if datastore['EXPLOITABLE']
|
|
f = File.open(datastore['EXPLOITABLE_THEMES'], 'rb')
|
|
else
|
|
f = File.open(datastore['THEMES_FILE'], 'rb')
|
|
end
|
|
total = f.readlines.size
|
|
f.rewind
|
|
f = f.readlines
|
|
f.each_with_index do |theme, i|
|
|
theme = theme.strip
|
|
print_progress(target_host, i, total) if i % datastore['PROGRESS'] == 0
|
|
vprint_status("#{target_host} - Checking theme: #{theme}")
|
|
version = check_theme_version_from_readme(theme)
|
|
next if version == Msf::Exploit::CheckCode::Unknown # aka not found
|
|
|
|
print_good("#{target_host} - Detected theme: #{theme} version #{version.details[:version]}")
|
|
report_note(
|
|
{
|
|
host: target_host,
|
|
proto: 'tcp',
|
|
sname: (ssl ? 'https' : 'http'),
|
|
port: rport,
|
|
type: "Wordpress Theme: #{theme} version #{version.details[:version]}"
|
|
# data: target_uri
|
|
}
|
|
)
|
|
end
|
|
print_status("#{target_host} - Finished scanning themes")
|
|
end
|
|
if datastore['PLUGINS']
|
|
print_status("#{target_host} - Enumerating plugins")
|
|
|
|
if datastore['EXPLOITABLE']
|
|
f = File.open(datastore['EXPLOITABLE_PLUGINS'], 'rb')
|
|
else
|
|
f = File.open(datastore['PLUGINS_FILE'], 'rb')
|
|
end
|
|
total = f.readlines.size
|
|
f.rewind
|
|
f = f.readlines
|
|
f.each_with_index do |plugin, i|
|
|
plugin = plugin.strip
|
|
print_progress(target_host, i, total) if i % datastore['PROGRESS'] == 0
|
|
vprint_status("#{target_host} - Checking plugin: #{plugin}")
|
|
version = check_plugin_version_from_readme(plugin)
|
|
next if version == Msf::Exploit::CheckCode::Unknown # aka not found
|
|
|
|
print_good("#{target_host} - Detected plugin: #{plugin} version #{version.details[:version]}")
|
|
report_note(
|
|
{
|
|
host: target_host,
|
|
proto: 'tcp',
|
|
sname: (ssl ? 'https' : 'http'),
|
|
port: rport,
|
|
type: "Wordpress Plugin: #{plugin} version #{version.details[:version]}"
|
|
# data: target_uri
|
|
}
|
|
)
|
|
end
|
|
print_status("#{target_host} - Finished scanning plugins")
|
|
end
|
|
|
|
if datastore['USERS']
|
|
print_status("#{target_host} - Searching Users")
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(wordpress_url_rest_api, 'users')
|
|
})
|
|
if res.nil?
|
|
print_error('Error getting response.')
|
|
elsif res.code == 200
|
|
parsed = res.get_json_document
|
|
if parsed.empty?
|
|
print_error('Response received, but no JSON content was provided.')
|
|
else
|
|
parsed.map do |child|
|
|
name = child['name']
|
|
wp_username = child['slug']
|
|
print_good("#{target_host} - Detected user: #{name} with username: #{wp_username}")
|
|
service_data = {
|
|
address: rhost,
|
|
port: rport,
|
|
service_name: (ssl ? 'https' : 'http'),
|
|
protocol: 'tcp',
|
|
workspace_id: myworkspace_id
|
|
}
|
|
|
|
credential_data = {
|
|
origin_type: :service,
|
|
module_fullname: fullname,
|
|
username: wp_username,
|
|
private_data: '',
|
|
private_type: :password
|
|
}.merge(service_data)
|
|
|
|
login_data = {
|
|
core: create_credential(credential_data),
|
|
status: Metasploit::Model::Login::Status::UNTRIED,
|
|
proof: nil
|
|
}.merge(service_data)
|
|
|
|
create_credential_login(login_data)
|
|
end
|
|
print_status("#{target_host} - Finished scanning users")
|
|
end
|
|
else
|
|
print_status("#{target_host} - Was not able to identify users on site using #{wordpress_url_rest_api}/users")
|
|
end
|
|
print_status("#{target_host} - Finished all scans")
|
|
end
|
|
end
|
|
end
|
|
end
|