From 9d321e9d88bb11ea2ad0c55ae48849ed10acd675 Mon Sep 17 00:00:00 2001 From: Shutdown <40902872+ShutdownRepo@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:33:10 +0100 Subject: [PATCH] ncchanges baby! --- impacket/krb5/crypto.py | 20 ++---------------- impacket/krb5/gssapi.py | 47 ++++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 42 deletions(-) diff --git a/impacket/krb5/crypto.py b/impacket/krb5/crypto.py index 9fc9fd0e8..885387c5d 100644 --- a/impacket/krb5/crypto.py +++ b/impacket/krb5/crypto.py @@ -61,10 +61,6 @@ from Cryptodome.Util.number import GCD as gcd from six import b, PY3, indexbytes, binary_type -from R2Log import logger -import hexdump - - def get_random_bytes(lenBytes): # We don't really need super strong randomness here to use PyCrypto.Random return urandom(lenBytes) @@ -226,13 +222,11 @@ def encrypt(cls, key, keyusage, plaintext, confounder, integrity_blob=None): hmac = HMAC.new(ki.contents, integrity_blob, cls.hashmod).digest() else: hmac = HMAC.new(ki.contents, basic_plaintext, cls.hashmod).digest() - logger.info("full hmac in crypto encrypt") - hexdump.hexdump(hmac) enc = cls.basic_encrypt(ke, basic_plaintext) + hmac[:cls.macsize] return enc @classmethod - def decrypt(cls, key, keyusage, ciphertext, ignore_integrity=False, dce_rpc_header=None, auth_data_header=None): + def decrypt(cls, key, keyusage, ciphertext, ignore_integrity=False, wrap_token_len=None, filler_len=None, dce_rpc_header=None, auth_data_header=None): ki = cls.derive(key, pack('>IB', keyusage, 0x55)) ke = cls.derive(key, pack('>IB', keyusage, 0xAA)) if len(ciphertext) < cls.blocksize + cls.macsize: @@ -248,21 +242,11 @@ def decrypt(cls, key, keyusage, ciphertext, ignore_integrity=False, dce_rpc_head # = confounder + dce_rpc_header + data + auth_data_header + filler + wrap_token # no need to implement, assuming the other client is legit if dce_rpc_header is not None and auth_data_header is not None: - plaintext_data, plaintext_filler_and_wrap_token_header = plaintext[:-8-16], plaintext[-8-16:] # FIXME dynamically set the filler size + plaintext_data, plaintext_filler_and_wrap_token_header = plaintext[:-filler_len-wrap_token_len], plaintext[-filler_len-wrap_token_len:] # FIXME dynamically set the filler size integrity_blob = confounder + dce_rpc_header + plaintext_data + auth_data_header + plaintext_filler_and_wrap_token_header - logger.info("integrity blob in crypto decrypt") - hexdump.hexdump(confounder) - hexdump.hexdump(dce_rpc_header) - hexdump.hexdump(plaintext_data) - hexdump.hexdump(auth_data_header) - hexdump.hexdump(plaintext_filler_and_wrap_token_header) hmac = bytearray(HMAC.new(ki.contents, integrity_blob, SHA).digest()) - logger.info("HMAC calculated") - hexdump.hexdump(hmac) else: hmac = bytearray(HMAC.new(ki.contents, basic_plaintext, SHA).digest()) - logger.info("HMAC from packet") - hexdump.hexdump(mac) expmac = hmac[:cls.macsize] if not _mac_equal(mac, expmac) and not ignore_integrity: raise InvalidChecksum('ciphertext integrity failure') diff --git a/impacket/krb5/gssapi.py b/impacket/krb5/gssapi.py index c695026a1..d213ba928 100644 --- a/impacket/krb5/gssapi.py +++ b/impacket/krb5/gssapi.py @@ -18,8 +18,6 @@ import struct import random import string -import hexdump -from R2Log import logger from six import b @@ -249,8 +247,15 @@ def unrotate(self, data, numBytes): numBytes %= len(data) result = data[numBytes:] + data[:numBytes] return result + + def get_filler(self, data): + cipher = self.cipherType() + length = (cipher.blocksize - (len(data) % cipher.blocksize)) or 16 + filler = b'\x00' * length + return filler + - def GSS_Wrap(self, sessionKey, data, sequenceNumber, direction='init', encrypt=True, keyUsage=KG_USAGE_INITIATOR_SEAL, confounder=None, padding=None, dce_rpc_header=None, auth_data_header=None): + def GSS_Wrap(self, sessionKey, data, sequenceNumber, direction='init', encrypt=True, acceptorSubkey=False, keyUsage=KG_USAGE_INITIATOR_SEAL, confounder=None, dce_rpc_header=None, auth_data_header=None): token = self.WRAP() cipher = self.cipherType() @@ -258,23 +263,19 @@ def GSS_Wrap(self, sessionKey, data, sequenceNumber, direction='init', encrypt=T if confounder is None: confounder = b(''.join([rand.choice(string.ascii_letters) for _ in range(16)])) - if padding: - padStr = padding - pad = len(padding) - else: - #Let's pad the data - pad = (cipher.blocksize - (len(data) % cipher.blocksize)) & 16 - padStr = b'\x00' * pad - # The RRC field ([RFC4121] section 4.2.5) is 12 if no encryption is requested or 28 if encryption # is requested. The RRC field is chosen such that all the data can be encrypted in place. rrc = 28 + token['Flags'] = 0 + if acceptorSubkey: + token['Flags'] |= 4 # AcceptorSubKey flag (0x00100) + if encrypt: + token['Flags'] |= 2 # Sealed flag (0x00010) if direction != 'init': - token['Flags'] = 3 - else: - token['Flags'] = 2 - token['EC'] = pad + token['Flags'] |= 1 # SendByAcceptor flag (0x00001) + + token['EC'] = len(self.get_filler(data)) # (RFC4121 section 4.2.4) the RRC field (as # defined in section 4.2.5) in the to-be-encrypted header contains the # hex value 00 00. @@ -285,15 +286,9 @@ def GSS_Wrap(self, sessionKey, data, sequenceNumber, direction='init', encrypt=T # This is undocumented, and was identified by reversing cryptodll.dll integrity_blob = None if dce_rpc_header is not None and auth_data_header is not None: - integrity_blob = confounder + dce_rpc_header + data + auth_data_header + padStr + token.getData() - logger.info("integrity blob in gssapi wrap") - hexdump.hexdump(confounder) - hexdump.hexdump(dce_rpc_header) - hexdump.hexdump(data) - hexdump.hexdump(auth_data_header) - hexdump.hexdump(padStr + token.getData()) + integrity_blob = confounder + dce_rpc_header + data + auth_data_header + self.get_filler(data) + token.getData() - data += padStr + data += self.get_filler(data) cipherText = cipher.encrypt(key=sessionKey, keyusage=keyUsage, plaintext=data + token.getData(), confounder=confounder, integrity_blob=integrity_blob) token['RRC'] = rrc @@ -321,7 +316,11 @@ def GSS_Unwrap(self, sessionKey, data, sequenceNumber, direction='init', encrypt # For Kerberos SSP: blob for HMAC calculation is different from blob for encryption. # This is undocumented, and was identified by reversing cryptodll.dll # no need to implement, assuming the other client is legit - plainText, confounder = cipher.decrypt(sessionKey, keyUsage, cipherText, dce_rpc_header=dce_rpc_header, auth_data_header=auth_data_header, ignore_integrity=ignore_integrity) + plainText, confounder = cipher.decrypt( + sessionKey, keyUsage, cipherText, + dce_rpc_header=dce_rpc_header, auth_data_header=auth_data_header, ignore_integrity=ignore_integrity, + wrap_token_len=len(self.WRAP()), filler_len=token['EC'] + ) encdata, enchdr = plainText[:-len(self.WRAP())], plainText[-len(self.WRAP()):] encdata, filler = encdata[:-token['EC']], encdata[-token['EC']:]