Created
February 12, 2024 06:54
-
-
Save FrankSpierings/6cd848fc2f368f54cb762eda8696a3fc to your computer and use it in GitHub Desktop.
Impacket - Print NTLM packets in JSON format for further analysis
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime | |
import json | |
from impacket.structure import Structure | |
from enum import Flag, Enum | |
class NegotiateFlags(Flag): | |
NTLMSSP_NEGOTIATE_56 = 0x80000000 | |
NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000 | |
NTLMSSP_NEGOTIATE_128 = 0x20000000 | |
NTLMSSP_RESERVED_1 = 0x10000000 | |
NTLMSSP_RESERVED_2 = 0x08000000 | |
NTLMSSP_RESERVED_3 = 0x04000000 | |
NTLMSSP_NEGOTIATE_VERSION = 0x02000000 | |
NTLMSSP_RESERVED_4 = 0x01000000 | |
NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000 | |
NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000 | |
NTLMSSP_RESERVED_5 = 0x00200000 | |
NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000 | |
NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000 | |
NTLMSSP_NEGOTIATE_NTLM2 = 0x00080000 | |
NTLMSSP_TARGET_TYPE_SHARE = 0x00040000 | |
NTLMSSP_TARGET_TYPE_SERVER = 0x00020000 | |
NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000 | |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000 | |
NTLMSSP_RESERVED_6 = 0x00004000 | |
NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000 | |
NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000 | |
NTLMSSP_NEGOTIATE_ANONYMOUS = 0x00000800 | |
NTLMSSP_NEGOTIATE_NT_ONLY = 0x00000400 | |
NTLMSSP_NEGOTIATE_NTLM = 0x00000200 | |
NTLMSSP_RESERVED_8 = 0x00000100 | |
NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080 | |
NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040 | |
NTLMSSP_NEGOTIATE_SEAL = 0x00000020 | |
NTLMSSP_NEGOTIATE_SIGN = 0x00000010 | |
NTLMSSP_RESERVED_9 = 0x00000008 | |
NTLMSSP_REQUEST_TARGET = 0x00000004 | |
NTLM_NEGOTIATE_OEM = 0x00000002 | |
NTLMSSP_NEGOTIATE_UNICODE = 0x00000001 | |
class NTLMSSP_AV(Enum): | |
EOL = 0x00 | |
HOSTNAME = 0x01 | |
DOMAINNAME = 0x02 | |
DNS_HOSTNAME = 0x03 | |
DNS_DOMAINNAME = 0x04 | |
DNS_TREENAME = 0x05 | |
FLAGS = 0x06 | |
TIME = 0x07 | |
RESTRICTIONS = 0x08 | |
TARGET_NAME = 0x09 | |
CHANNEL_BINDINGS = 0x0a | |
class NTLMv2_CLIENT_CHALLENGE(Structure): | |
structure = ( | |
('RespType', '<B=1'), | |
('HiRespType', '<B=1'), | |
('Reserved1', '<H=0'), | |
('Reserved2', '<I=0'), | |
('TimeStamp', '<q=0'), | |
('ChallengeFromClient', '8s'), | |
('Reserved3', '<I=0'), | |
('AvPairs', ":") | |
) | |
def filetime_to_datetime(filetime): | |
filetime_int = int.from_bytes(filetime, byteorder='little') | |
seconds_since_epoch = (filetime_int - 116444736000000000) / 10000000 | |
return datetime.datetime.utcfromtimestamp(seconds_since_epoch) | |
def serialize_avpairs(av_pairs): | |
data = {} | |
for nr in av_pairs.fields: | |
av_type = NTLMSSP_AV(nr) | |
av_value = av_pairs.fields[nr][1] | |
if av_type not in [NTLMSSP_AV.EOL, NTLMSSP_AV.FLAGS, NTLMSSP_AV.TIME, NTLMSSP_AV.RESTRICTIONS, NTLMSSP_AV.CHANNEL_BINDINGS]: | |
data[av_type.name] = av_value.decode("utf-16-le") | |
elif av_type in [NTLMSSP_AV.TIME]: | |
data[av_type.name] = str(filetime_to_datetime(av_value)) | |
else: | |
try: | |
data[av_type.name] = av_value.decode() | |
except: | |
data[av_type.name] = repr(av_value) | |
return data | |
def json_ntlmssp(structure): | |
data = {} | |
for field in structure.fields: | |
value = structure.fields[field] | |
if field in ['user_name', 'domain_name', 'host_name']: | |
try: | |
data[field] = value.decode("utf-16-le") | |
except: | |
data[field] = value | |
elif field in ['flags']: | |
data[field] = [] | |
flag = NegotiateFlags(value) | |
for attribute in flag: | |
data[field].append(attribute.name) | |
elif field in ['session_key', 'lanman', 'challenge', 'MIC', 'reserved', 'Reserved']: | |
try: | |
data[field] = value.hex() | |
except: | |
data[field] = value | |
elif field in ['Version']: | |
data[field] = {} | |
version = VERSION(value) | |
for vfield in version.fields: | |
data[field][vfield] = version.fields[vfield] | |
elif field in ['TargetInfoFields']: | |
output = f'{field}: ' | |
av_pairs = AV_PAIRS(value) | |
data[field] = serialize_avpairs(av_pairs) | |
elif field in ['ntlm']: | |
response = value[:16] | |
data[field] = {} | |
data[field]['Response'] = response.hex() | |
data[field]['Challenge'] = {} | |
challenge = NTLMv2_CLIENT_CHALLENGE(value[16:]) | |
for cfield in challenge.fields: | |
cvalue = challenge.fields[cfield] | |
if cfield in ['ChallengeFromClient']: | |
data[field]['Challenge'][cfield] = cvalue.hex() | |
elif cfield not in ['AvPairs']: | |
data[field]['Challenge'][cfield] = cvalue | |
else: | |
av_pairs = AV_PAIRS(cvalue) | |
data[field]['Challenge'][cfield] = serialize_avpairs(av_pairs) | |
else: | |
data[field] = value | |
return json.dumps(data, indent=True, default=str) | |
print(json_ntlmssp(self)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment