2022 HackTheBox Business CTF Rogue [Encrypted SMBv3 Decryption]
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
#!/usr/bin/env python3 | |
""" | |
This function calculates the random secret key for SMB decryption. | |
This uses the NTLM hash or plaintext password of the user given a pre-existing packet capture. | |
""" | |
# Imports | |
from Cryptodome.Cipher import ARC4 | |
from Cryptodome.Hash import MD4 | |
import hashlib | |
import hmac | |
import argparse | |
from binascii import hexlify, unhexlify | |
def generateEncryptedSessionKey(keyExchangeKey, exportedSessionKey): | |
cipher = ARC4.new(keyExchangeKey) | |
cipher_encrypt = cipher.encrypt | |
sessionKey = cipher_encrypt(exportedSessionKey) | |
return sessionKey | |
def swap_bytes(x): | |
x = bytearray(unhexlify(x)); | |
x.reverse(); | |
return hexlify(x) | |
# Arguments | |
parser = argparse.ArgumentParser( | |
description="Calculate the Random Session Key based on data from a PCAP (maybe).") | |
parser.add_argument("-u", "--user", required=True, help="User name") | |
parser.add_argument("-d", "--domain", required=True, help="Domain name") | |
parser.add_argument("-p", "--password", required=False, | |
help="Password of User") | |
parser.add_argument("-ph", "--ntlmpassword", required=False, | |
help="NTLM Password Hash of User") | |
parser.add_argument("-n", "--ntproofstr", required=True, | |
help="NTProofStr (hex). This can be found in PCAP via ntlmssp.ntlmv2_response.ntproofstr") | |
parser.add_argument("-k", "--sessionkey", required=True, | |
help="Encrypted Session Key (hex). This can be found in PCAP.") | |
parser.add_argument("-i", "--sessionid", required=True, | |
help="Session ID (hex). This can be found in PCAP.") | |
parser.add_argument("-v", "--verbose", action="store_true", | |
help="increase output verbosity") | |
args = parser.parse_args() | |
# Upper CaseUsername and Domain | |
Username = str(args.user).upper().encode('utf-16le') | |
Domain = str(args.domain).upper().encode('utf-16le') | |
# Create 'NTLM' Hash of password | |
if args.ntlmpassword: | |
NTLMPassword = unhexlify(args.ntlmpassword) | |
elif args.password: | |
Password = args.password.encode('utf-16le') | |
hash1 = MD4.new(Password) | |
NTLMPassword = hash1.hexdigest() | |
NTLMPassword = unhexlify(NTLMPassword) | |
else: | |
exit("Requires password or NTLM hash") | |
# Calculate the ResponseNTKey | |
h = hmac.new(NTLMPassword, digestmod=hashlib.md5) | |
h.update(Username+Domain) | |
ResponseNTKey = h.digest() | |
# Use NTProofSTR and ResponseNTKey to calculate Key Exchange Key | |
NTProofStr = unhexlify(args.ntproofstr) | |
h = hmac.new(ResponseNTKey, digestmod=hashlib.md5) | |
h.update(NTProofStr) | |
KeyExchKey = h.digest() | |
# Calculate the Random Session Key by decrypting Encrypted Session Key with Key Exchange Key via RC4 | |
RsessKey = generateEncryptedSessionKey( | |
KeyExchKey, unhexlify(args.sessionkey)) | |
if args.verbose: | |
print("USER+DOMAIN: " + Username.decode() + "" + Domain.decode()) | |
print("PASS HASH: " + hexlify(NTLMPassword).decode()) | |
print("RESP NT: " + hexlify(ResponseNTKey).decode()) | |
print("NT PROOF: " + hexlify(NTProofStr).decode()) | |
print("KeyExKey: " + hexlify(KeyExchKey).decode()) | |
print("Session ID: " + swap_bytes(args.sessionid).decode()) | |
print('Random SK:', hexlify(RsessKey).decode()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note, if there are multiple SMB sessions with the same Session ID, decryption only works with the first random session key inserted in Protocols, SMB in version Wireshark version 3.6.6 which is the latest version available at this time.
Filter:
ntlmssp.ntlmv2_response.ntproofstr
to get inputs for the script.Also, older versions of Wireshark may not work.
Go to File, Exports, SMB to view any files decrypted.