def MAC(self, handle, signingKey, seqNum, message): if self.is_extended_security() == True: msg = NTLMSSP_MESSAGE_SIGNATURE() if NegotiateFlags.NEGOTIATE_KEY_EXCH in self.ntlmChallenge.NegotiateFlags: tt = struct.pack('<i', seqNum) + message t = hmac.new(signingKey, digestmod='md5') t.update(tt) msg.Checksum = handle(t.digest()[:8]) msg.SeqNum = seqNum seqNum += 1 else: t = hmac.new(signingKey, digestmod='md5') t.update(struct.pack('<i', seqNum) + message) msg.Checksum = t.digest()[:8] msg.SeqNum = seqNum seqNum += 1 else: raise Exception('Not implemented!') #t = struct.pack('<I',binascii.crc32(message)& 0xFFFFFFFF) #randompad = 0 #msg = NTLMSSP_MESSAGE_SIGNATURE_NOEXT() #msg.RandomPad = handle(struct.pack('<I',randompad)) #msg.Checksum = struct.unpack('<I',handle(messageSignature['Checksum']))[0] return msg.to_bytes()
def NTOWFv2(Passwd, User, UserDom, PasswdHash = None): if PasswdHash is not None: fp = hmac.new(PasswdHash, digestmod='md5') else: fp = hmac.new(NTOWFv1(Passwd), digestmod='md5') fp.update((User.upper() + UserDom).encode('utf-16le')) return fp.digest()
def encrypt(cls, key, keyusage, plaintext, confounder): if confounder is None: confounder = get_random_bytes(8) ki = HMAC.new(key.contents, cls.usage_str(keyusage), MD5).digest() cksum = HMAC.new(ki, confounder + plaintext, MD5).digest() ke = HMAC.new(ki, cksum, MD5).digest() return cksum + ARC4(ke).encrypt(confounder + plaintext)
def verify(self, creds, credtype='plain'): """ Verifies the authentication data against the user credentials :param creds: dictionary containing the domain, user, hash/password :param credtype: can be 'plain' or 'hash' this indicates what type of credential lookup to perform :return: bool """ # print('Creds: %s' % creds) if creds is None: return True if self.domain not in creds: return False if self.username not in creds[self.domain]: return False if credtype == 'plain': lm_hash = LMOWFv2(creds[self.domain][self.username], self.username, self.domain) elif credtype == 'hash': lm_hash = LMOWFv2(None, self.username, self.domain, bytes.fromhex(creds[self.domain][self.username])) else: raise Exception('Unknown cred type!') hm = hmac.new(lm_hash, digestmod='md5') hm.update(bytes.fromhex(self.ServerChallenge)) hm.update(bytes.fromhex(self.ChallengeFromClinet)) return self.ClientResponse == hm.hexdigest()
def encrypt(cls, key, keyusage, plaintext, confounder): ki = cls.derive(key, pack('>IB', keyusage, 0x55)) ke = cls.derive(key, pack('>IB', keyusage, 0xAA)) if confounder is None: confounder = get_random_bytes(cls.blocksize) basic_plaintext = confounder + _zeropad(plaintext, cls.padsize) hmac = HMAC.new(ki.contents, basic_plaintext, cls.hashmod).digest() return cls.basic_encrypt(ke, basic_plaintext) + hmac[:cls.macsize]
def decrypt(cls, key, keyusage, ciphertext): if len(ciphertext) < 24: raise ValueError('ciphertext too short') cksum, basic_ctext = ciphertext[:16], ciphertext[16:] ki = HMAC.new(key.contents, cls.usage_str(keyusage), MD5).digest() ke = HMAC.new(ki, cksum, MD5).digest() basic_plaintext = ARC4(ke).decrypt(basic_ctext) exp_cksum = HMAC.new(ki, basic_plaintext, MD5).digest() ok = _mac_equal(cksum, exp_cksum) if not ok and keyusage == 9: # Try again with usage 8, due to RFC 4757 errata. ki = HMAC.new(key.contents, pack('<I', 8), MD5).digest() exp_cksum = HMAC.new(ki, basic_plaintext, MD5).digest() ok = _mac_equal(cksum, exp_cksum) if not ok: raise InvalidChecksum('ciphertext integrity failure') # Discard the confounder. return basic_plaintext[8:]
def construct(server_challenge, client_challenge, server_details, credentials, timestamp = None): ntlm_creds = netntlmv2() ntlm_creds.credentials = credentials ntlm_creds.ServerChallenge = server_challenge if not credentials.nt_hash and not credentials.password: raise Exception('Password or NT hash must be supplied!') if credentials.password: nt_hash_v2 = NTOWFv2(credentials.password, credentials.username, credentials.domain) else: nt_hash_v2 = NTOWFv2(None, credentials.username, credentials.domain, bytes.fromhex(credentials.nt_hash)) if not timestamp: timestamp = datetime.datetime.utcnow() cc = NTLMv2ClientChallenge.construct(timestamp, client_challenge, server_details) temp = cc.to_bytes() hm = hmac.new(nt_hash_v2, digestmod='md5') hm.update(server_challenge) hm.update(temp) NTProofStr = hm.digest() ntlm_creds.NTResponse = NTLMv2Response() ntlm_creds.NTResponse.Response = NTProofStr ntlm_creds.NTResponse.ChallengeFromClinet = cc hm = hmac.new(nt_hash_v2, digestmod='md5') hm.update(server_challenge) hm.update(client_challenge) ntlm_creds.LMResponse = LMv2Response() ntlm_creds.LMResponse.Response = hm.digest() ntlm_creds.LMResponse.ChallengeFromClinet = client_challenge hm = hmac.new(nt_hash_v2, digestmod='md5') hm.update(NTProofStr) ntlm_creds.SessionBaseKey = hm.digest() return ntlm_creds
def calc_key_exchange_key(self): if not self.credentials.nt_hash: nt_hash = NTOWFv1(self.credentials.password) else: nt_hash = bytes.fromhex(self.credentials.nt_hash) hm = hmac.new(self.SessionBaseKey, digestmod='md5') hm.update(self.ServerChallenge) hm.update(self.LMResponse.to_bytes()[:8]) return hm.digest()
def GSS_GetMIC(self, data, sequenceNumber, direction='init'): raise Exception('Not tested! Sure it needs some changes') GSS_GETMIC_HEADER = b'\x60\x23\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02' # Let's pad the data pad = (4 - (len(data) % 4)) & 0x3 padStr = bytes([pad]) * pad data += padStr mic = GSSMIC_RC4() if direction == 'init': mic.SND_SEQ = sequenceNumber.to_bytes(4, 'big', signed=False) + b'\x00' * 4 else: mic.SND_SEQ = sequenceNumber.to_bytes(4, 'big', signed=False) + b'\xff' * 4 Ksign_ctx = hmac.new(self.session_key.contents, digestmod='md5') Ksign_ctx.update(b'signaturekey\0') Ksign = Ksign_ctx.digest() id = 15 temp = md5( id.to_bytes(4, 'little', signed=False) + mic.to_bytes()[:8]).digest() chksum_ctx = hmac.new(Ksign, digestmod='md5') chksum_ctx.update(temp) mic.SGN_CKSUM = chksum_ctx.digest()[:8] id = 0 temp = hmac.new(self.session_key.contents, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) Kseq_ctx = hmac.new(temp.digest(), digestmod='md5') Kseq_ctx.update(mic.SGN_CKSUM) Kseq = Kseq_ctx.digest() mic.SGN_CKSUM = RC4(Kseq).encrypt(mic.SND_SEQ) return GSS_GETMIC_HEADER + mic.to_bytes()
def decrypt(cls, key, keyusage, ciphertext): ki = cls.derive(key, pack('>IB', keyusage, 0x55)) ke = cls.derive(key, pack('>IB', keyusage, 0xAA)) if len(ciphertext) < cls.blocksize + cls.macsize: raise ValueError('ciphertext too short') basic_ctext, mac = ciphertext[:-cls.macsize], ciphertext[-cls.macsize:] if len(basic_ctext) % cls.padsize != 0: raise ValueError('ciphertext does not meet padding requirement') basic_plaintext = cls.basic_decrypt(ke, basic_ctext) hmac = HMAC.new(ki.contents, basic_plaintext, cls.hashmod).digest() expmac = hmac[:cls.macsize] if not _mac_equal(mac, expmac): raise InvalidChecksum('ciphertext integrity failure') # Discard the confounder. return basic_plaintext[cls.blocksize:]
def dump_dcc(self): logger.debug('[SECURITY] dump_dcc invoked') cache_reg = self.hive.find_key('Cache', False) if cache_reg is None: logger.debug('[SECURITY] No DCC secrets found') return values = self.hive.list_values(cache_reg) if values == []: logger.debug('[SECURITY] No DCC secrets found') return if b'NL$Control' in values: values.remove(b'NL$Control') if b'NL$IterationCount' in values: logger.debug('[SECURITY] DCC Setting iteration count') values.remove(b'NL$IterationCount') record = self.hive.get_value('Cache\\NL$IterationCount')[1] if record > 10240: self.dcc_iteration_count = record & 0xfffffc00 else: self.dcc_iteration_count = record * 1024 self.get_lsa_key() self.get_NKLM_key() for value in values: logger.debug('[SECURITY] DCC Checking value: %s' % value) record_data = self.hive.get_value('Cache\\%s' % value.decode())[1] record = NL_RECORD.from_bytes(record_data) if record.IV != b'\x00' * 16: if record.Flags & 1 == 1: # Encrypted if self.lsa_secret_key_vista_type is True: plaintext = b'' cipher = AES(self.NKLM_key[16:32], MODE_CBC, IV=record.IV) n = 16 for block in [ record.EncryptedData[i:i + n] for i in range(0, len(record.EncryptedData), n) ]: #terrible, terrible workaround if len(block) < 16: block += b'\x00' * (16 - len(block)) plaintext += cipher.decrypt(block) else: key = hmac.new(self.NKLM_key, record.IV, digestmod='md5').digest() cipher = RC4(key) plaintext = cipher.decrypt(record.EncryptedData) else: # Plain! Until we figure out what this is, we skip it #plainText = record['EncryptedData'] logger.debug( '[SECURITY] DCC Skipping value %s, unknown formet' % value) continue dcc_hash = plaintext[:0x10] blob = io.BytesIO(plaintext[0x48:]) username = blob.read(record.UserLength).decode('utf-16-le') blob.seek( self.__pad(record.UserLength) + self.__pad(record.DomainNameLength)) domain = blob.read( record.DnsDomainNameLength).decode('utf-16-le') version = 2 if self.lsa_secret_key_vista_type is True else 1 secret = LSADCCSecret(version, domain, username, dcc_hash, iteration=self.dcc_iteration_count) self.dcc_hashes.append(secret) return self.dcc_hashes
def GSS_Wrap(self, data, seq_num, direction='init', encrypt=True, auth_data=None): GSS_WRAP_HEADER = b'\x60\x2b\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02' pad = (8 - (len(data) % 8)) & 0x7 padStr = bytes([pad]) * pad data += padStr token = GSSWRAP_RC4() token.SEAL_ALG = b'\x10\x00' if direction == 'init': token.SND_SEQ = seq_num.to_bytes(4, 'big', signed=False) + b'\x00' * 4 else: token.SND_SEQ = seq_num.to_bytes(4, 'big', signed=False) + b'\xff' * 4 token.Confounder = os.urandom(8) temp = hmac.new(self.session_key.contents, digestmod='md5') temp.update(b'signaturekey\0') Ksign = temp.digest() id = 13 Sgn_Cksum = md5( id.to_bytes(4, 'little', signed=False) + token.to_bytes()[:8] + token.Confounder + data).digest() temp = hmac.new(Ksign, digestmod='md5') temp.update(Sgn_Cksum) token.SGN_CKSUM = temp.digest()[:8] klocal = b'' for b in self.session_key.contents: klocal += bytes([b ^ 0xf0]) id = 0 temp = hmac.new(klocal, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) temp = hmac.new(temp.digest(), digestmod='md5') temp.update(seq_num.to_bytes(4, 'big', signed=False)) Kcrypt = temp.digest() id = 0 temp = hmac.new(self.session_key.contents, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) temp = hmac.new(temp.digest(), digestmod='md5') temp.update(token.SGN_CKSUM) Kseq = temp.digest() token.SND_SEQ = RC4(Kseq).encrypt(token.SND_SEQ) if auth_data is not None: wrap = GSSWRAP_RC4.from_bytes(auth_data[8 + len(GSS_WRAP_HEADER):]) id = 0 temp = hmac.new(self.session_key.contents, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) temp = hmac.new(temp.digest(), digestmod='md5') temp.update(wrap.SGN_CKSUM) snd_seq = RC4(temp.digest()).encrypt(wrap.SND_SEQ) id = 0 temp = hmac.new(klocal, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) temp = hmac.new(temp.digest(), digestmod='md5') temp.update(snd_seq[:4]) Kcrypt = temp.digest() rc4 = RC4(Kcrypt) cipherText = rc4.decrypt(token.Confounder + data)[8:] elif encrypt is True: rc4 = RC4(Kcrypt) token.Confounder = rc4.encrypt(token.Confounder) cipherText = rc4.encrypt(data) else: cipherText = data finalData = GSS_WRAP_HEADER + token.to_bytes() return cipherText, finalData
def GSS_Wrap(self, data, seq_num, direction='init', encrypt=True, cofounder=None): #direction = 'a' #seq_num = 0 #print('[GSS_Wrap] data: %s' % data) #print('[GSS_Wrap] seq_num: %s' % seq_num.to_bytes(4, 'big', signed = False).hex()) #print('[GSS_Wrap] direction: %s' % direction) #print('[GSS_Wrap] encrypt: %s' % encrypt) # #print('[GSS_Wrap] auth_data: %s' % auth_data) #pad = 0 if encrypt is True: data += b'\x01' #pad = (8 - (len(data) % 8)) & 0x7 #padStr = bytes([pad]) * pad #data += padStr # ##data += b'\x08' * 8 #print('[GSS_Wrap] pad: %s' % pad) #print('[GSS_Wrap] data padded: %s' % data) token = GSSWRAP_RC4() token.SEAL_ALG = b'\x10\x00' # RC4 if direction == 'init': token.SND_SEQ = seq_num.to_bytes(4, 'big', signed=False) + b'\x00' * 4 else: token.SND_SEQ = seq_num.to_bytes(4, 'big', signed=False) + b'\xff' * 4 token.Confounder = os.urandom(8) #if cofounder is not None: # token.Confounder = cofounder # #testing purposes only, pls remove temp = hmac.new(self.session_key.contents, digestmod='md5') temp.update(b'signaturekey\0') Ksign = temp.digest() id = 13 Sgn_Cksum = md5( id.to_bytes(4, 'little', signed=False) + token.to_bytes()[:8] + token.Confounder + data).digest() klocal = b'' for b in self.session_key.contents: klocal += bytes([b ^ 0xf0]) id = 0 temp = hmac.new(klocal, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) temp = hmac.new(temp.digest(), digestmod='md5') temp.update(seq_num.to_bytes(4, 'big', signed=False)) Kcrypt = temp.digest() temp = hmac.new(Ksign, digestmod='md5') temp.update(Sgn_Cksum) token.SGN_CKSUM = temp.digest()[:8] id = 0 temp = hmac.new(self.session_key.contents, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) temp = hmac.new(temp.digest(), digestmod='md5') temp.update(token.SGN_CKSUM) Kseq = temp.digest() token.SND_SEQ = RC4(Kseq).encrypt(token.SND_SEQ) #if auth_data is not None: if encrypt is False: #print('Unwrap sessionkey: %s' % self.session_key.contents.hex()) #print('Unwrap data : %s' % data.hex()) sspi_wrap = KRB5_MECH_INDEP_TOKEN.from_bytes(data) hdr = sspi_wrap.data[:32] data = sspi_wrap.data[32:] wrap = GSSWRAP_RC4.from_bytes(hdr) id = 0 temp = hmac.new(self.session_key.contents, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) temp = hmac.new(temp.digest(), digestmod='md5') temp.update(wrap.SGN_CKSUM) Kseq = temp.digest() snd_seq = RC4(Kseq).encrypt(wrap.SND_SEQ) id = 0 temp = hmac.new(klocal, digestmod='md5') temp.update(id.to_bytes(4, 'little', signed=False)) temp = hmac.new(temp.digest(), digestmod='md5') temp.update(snd_seq[:4]) Kcrypt = temp.digest() rc4 = RC4(Kcrypt) dec_cofounder = rc4.decrypt(wrap.Confounder) dec_data = rc4.decrypt(data) id = 13 Sgn_Cksum_calc = md5( id.to_bytes(4, 'little', signed=False) + wrap.to_bytes()[:8] + dec_cofounder + dec_data).digest() temp = hmac.new(Ksign, digestmod='md5') temp.update(Sgn_Cksum_calc) Sgn_Cksum_calc = temp.digest()[:8] if wrap.SGN_CKSUM != Sgn_Cksum_calc[:8]: return None, Exception('Integrity verification failed') pad = 1 return dec_data[:-pad], None elif encrypt is True: rc4 = RC4(Kcrypt) token.Confounder = rc4.encrypt(token.Confounder) cipherText = rc4.encrypt(data) finalData, cipherText = KRB5_MECH_INDEP_TOKEN( token.to_bytes() + cipherText, '1.2.840.113554.1.2.2').to_bytes() #print('cipherText %s' % cipherText.hex()) #print('finalData %s' % finalData.hex()) #print('sessionkey %s' % self.session_key.contents.hex()) return cipherText, finalData
def checksum(cls, key, keyusage, text): ksign = HMAC.new(key.contents, b'signaturekey\x00', MD5).digest() md5hash = MD5(_RC4.usage_str(keyusage) + text).digest() return HMAC.new(ksign, md5hash, MD5).digest()
def checksum(cls, key, keyusage, text): kc = cls.enc.derive(key, pack('>IB', keyusage, 0x99)) hmac = HMAC.new(kc.contents, text, cls.enc.hashmod).digest() return hmac[:cls.macsize]
def prf(cls, key, string): return HMAC.new(key.contents, string, SHA).digest()