chrome password decrypter

Just starting out? Need help? Post your questions and find answers here.
lisa_beille
User
User
Posts: 24
Joined: Fri Dec 22, 2023 5:59 pm

chrome password decrypter

Post by lisa_beille »

Hello
i'm trying to decryt chrome v80+ passwords. i know the theory and successed with Python but i can't make it work in PB.

any help ?
User avatar
NicTheQuick
Addict
Addict
Posts: 1523
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: chrome password decrypter

Post by NicTheQuick »

How about more information? Where's the Python code that has succeeded? And where is the Purebasic code that has not?
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
SPH
Enthusiast
Enthusiast
Posts: 574
Joined: Tue Jan 04, 2011 6:21 pm

Re: chrome password decrypter

Post by SPH »

NicTheQuick wrote: Mon Jan 01, 2024 3:15 pm How about more information? Where's the Python code that has succeeded? And where is the Purebasic code that has not?
+1

!i!i!i!i!i!i!i!i!i!
!i!i!i!i!i!i!
!i!i!i!
//// Informations ////
Portable LENOVO ideapad 110-17ACL 64 bits
Version de PB : 6.12LTS - 64 bits
lisa_beille
User
User
Posts: 24
Joined: Fri Dec 22, 2023 5:59 pm

Re: chrome password decrypter

Post by lisa_beille »

SPH wrote: Mon Jan 01, 2024 3:37 pm
NicTheQuick wrote: Mon Jan 01, 2024 3:15 pm How about more information? Where's the Python code that has succeeded? And where is the Purebasic code that has not?
+1
here is the Purebasic code :

this function gets and decrypts the master key :

Code: Select all

; 
Structure pdata
  cbdata.l
  pbdata.l
EndStructure

Procedure get_secret_key()
  Define blob_key.pdata
  
  OpenFile(0,GetEnvironmentVariable("temp")+"\local state") ; the "local state" file containing base64 encoded encrypted master key
  Repeat
    line$=ReadString(0,#PB_Ascii,4096)
    start = FindString(line$,Chr(34)+"encrypted_key"+Chr(34))
    If start
      finish = FindString(Mid(line$,start),"}")
      If finish
        masterkey$ = Mid(line$,start+17,finish-19)
        Break
      EndIf
    EndIf  
  Until Eof(0)
  CloseFile(0)
  decbuff = AllocateMemory(1024)
  x = Base64Decoder(masterkey$, decbuff, 1024)
  blob_key\cbdata = x - 5
  blob_key\pbdata = decbuff + 5	; the master key has the word 'DPAPI' appended at the beginning. so we ignore it
  CallFunctionFast(CryptUnprotectData,blob_key,0,0,0,0,0,master_key)
  FreeMemory(decbuff)
  ProcedureReturn master_key\pbdata
 EndProcedure
   
and the function that decrypts a password :

Code: Select all

Procedure.s DecryptPassword(buffer, master_key, size)
    ; the structure of a an encrypted chrome password is : 
    ; a string 'v10' (3 bytes)
    ; initialization vector (12 bytes)
    ; encrypted password (variable length)
    ; padding (16 bytes) 
    
    ; buffer conatins the encrypted password
 
    If size > 31
    	decrypted_password = AllocateMemory(size - 15)
    	iv = AllocateMemory(12)
    	CopyMemory(buff + 3, iv, 12)
    	password = AllocateMemory(size - 15 )
    	CopyMemory(buff + 15, password, size - 15 )
	AESDecoder(password, decrypted_password, size  - 15 , master_key, 256, iv)
   	 procedureReturn Peeks(decrypted_password)
    	FreeMemory(decrypted_password)
    	FreeMemory(iv)
    	FreeMemory(password)
    	
    EndIf
EndProcedure
the returned supposedly decrypted password is rubbish

the python code :

Code: Select all

#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\AppData\Local\Google\Chrome\User Data\Local State"%(os.environ['USERPROFILE']))
CHROME_PATH = os.path.normpath(r"%s\AppData\Local\Google\Chrome\User Data"%(os.environ['USERPROFILE']))

def get_secret_key():
    try:
        #(1) Get secretkey from chrome local state
        with open( CHROME_PATH_LOCAL_STATE, "r", encoding='utf-8') as f:
            local_state = f.read()
            local_state = json.loads(local_state)
        secret_key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
        #Remove suffix DPAPI
        secret_key = secret_key[5:] 
        secret_key = win32crypt.CryptUnprotectData(secret_key, None, None, None, 0)[1]
        return secret_key
    except Exception as e:
        print("%s"%str(e))
        print("[ERR] Chrome secretkey cannot be found")
        return None
    
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 action_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))
here is an example of an encrypted password
76 31 30 F4 51 3C 11 D0 B2 FF 11 BA 44 59 31 7F 4D 4B F8 95 DE AA 9B B5 AC 6B AC 96 70 5C D2 0D F7 07 9D 8E 9D 0D 13 38 C8 42 75 47 E0 37 6C 6A B6 10 AE 0A 88 67 9D CC
Thank you all
User avatar
NicTheQuick
Addict
Addict
Posts: 1523
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: chrome password decrypter

Post by NicTheQuick »

There are things missing. Especially the type and structure of the `master_key` variable and the function `CryptUnprotectData`. Besides that you should avoid using `CallFunctionFast()` and instead use Prototypes.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
lisa_beille
User
User
Posts: 24
Joined: Fri Dec 22, 2023 5:59 pm

Re: chrome password decrypter

Post by lisa_beille »

NicTheQuick wrote: Mon Jan 01, 2024 7:16 pm There are things missing. Especially the type and structure of the `master_key` variable and the function `CryptUnprotectData`. Besides that you should avoid using `CallFunctionFast()` and instead use Prototypes.
the master key is of type pdata (see code).
i don't understand what's wrong since i do the same as the python code. it should work...
lisa_beille
User
User
Posts: 24
Joined: Fri Dec 22, 2023 5:59 pm

Re: chrome password decrypter

Post by lisa_beille »

lisa_beille wrote: Mon Jan 01, 2024 8:42 pm
NicTheQuick wrote: Mon Jan 01, 2024 7:16 pm There are things missing. Especially the type and structure of the `master_key` variable and the function `CryptUnprotectData`. Besides that you should avoid using `CallFunctionFast()` and instead use Prototypes.
the master key is of type pdata (see code).
i don't understand what's wrong since i do the same as the python code. it should work...
I think the problem is that Purebasic doesn't support AES-GCM decryption :( :(
lisa_beille
User
User
Posts: 24
Joined: Fri Dec 22, 2023 5:59 pm

Re: chrome password decrypter

Post by lisa_beille »

So now my question becomes : How to use Bcrypt library functions like BCryptOpenAlgorithmProvider in PB
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: chrome password decrypter

Post by ChrisR »

lisa_beille wrote: Mon Jan 01, 2024 8:42 pm the master key is of type pdata (see code).
i don't understand what's wrong since i do the same as the python code. it should work...
For pdata structure look: https://www.purebasic.fr/german/viewtop ... 78#p345878

Are you sure you're doing the same as the python code ?

Code: Select all

If OpenFile(0,GetEnvironmentVariable("TEMP") + "\local state")
vs 
If OpenFile(0,GetEnvironmentVariable("LOCALAPPDATA") + "\Google\Chrome\User Data\Local State")
lisa_beille
User
User
Posts: 24
Joined: Fri Dec 22, 2023 5:59 pm

Re: chrome password decrypter

Post by lisa_beille »

The problem is that PB does not support AES-GCM mode.
i found this C++ code, if anyone can help me convert it to PB, it would be very kind :

Code: Select all

std::string AESDecrypter(std::string EncryptedBlob, DATA_BLOB MasterKey)
{
	BCRYPT_ALG_HANDLE hAlgorithm = 0;
	BCRYPT_KEY_HANDLE hKey = 0;
	NTSTATUS status = 0;
	SIZE_T EncryptedBlobSize = EncryptedBlob.length();
	SIZE_T TagOffset = EncryptedBlobSize - 15;
	ULONG PlainTextSize = 0;

	std::vector<BYTE> CipherPass(EncryptedBlobSize); // hold the passwords ciphertext.
	std::vector<BYTE> PlainText;
	std::vector<BYTE> IV(IV_SIZE); // Will hold initial vector data.

	// Parse iv and password from the buffer using std::copy
	std::copy(EncryptedBlob.data() + 3, EncryptedBlob.data() + 3 + IV_SIZE, IV.begin());
	std::copy(EncryptedBlob.data() + 15, EncryptedBlob.data() + EncryptedBlobSize, CipherPass.begin());

	// Open algorithm provider for decryption
	status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0);
	if (!BCRYPT_SUCCESS(status))
	{
		std::cout << "BCryptOpenAlgorithmProvider failed with status: " << status << std::endl;
		return "";
	}

	// Set chaining mode for decryption
	status = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (UCHAR*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
	if (!BCRYPT_SUCCESS(status))
	{
		std::cout << "BCryptSetProperty failed with status: " << status << std::endl;
		BCryptCloseAlgorithmProvider(hAlgorithm, 0);
		return "";
	}

	// Generate symmetric key
	status = BCryptGenerateSymmetricKey(hAlgorithm, &hKey, NULL, 0, MasterKey.pbData, MasterKey.cbData, 0);
	if (!BCRYPT_SUCCESS(status))
	{
		std::cout << "BcryptGenertaeSymmetricKey failed with status: " << status << std::endl;
		BCryptCloseAlgorithmProvider(hAlgorithm, 0);
		return "";
	}

	// Auth cipher mode info
	BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO AuthInfo;
	BCRYPT_INIT_AUTH_MODE_INFO(AuthInfo);
	TagOffset = TagOffset - 16;
	AuthInfo.pbNonce = IV.data();
	AuthInfo.cbNonce = IV_SIZE;
	AuthInfo.pbTag = CipherPass.data() + TagOffset;
	AuthInfo.cbTag = TAG_SIZE;

	// Get size of plaintext buffer
	status = BCryptDecrypt(hKey, CipherPass.data(), TagOffset, &AuthInfo, NULL, 0, NULL, NULL, &PlainTextSize, 0);
	if (!BCRYPT_SUCCESS(status))
	{
		std::cout << "BCryptDecrypt (1) failed with status: " << status << std::endl;
		return "";
	}

	// Allocate memory for the plaintext
	PlainText.resize(PlainTextSize);

	status = BCryptDecrypt(hKey, CipherPass.data(), TagOffset, &AuthInfo, NULL, 0, PlainText.data(), PlainTextSize, &PlainTextSize, 0);
	if (!BCRYPT_SUCCESS(status))
	{
		std::cout << "BCrypt Decrypt (2) failed with status: " << status << std::endl;
		return "";
	}

	// Close the algorithm handle
	BCryptCloseAlgorithmProvider(hAlgorithm, 0);

	return std::string(PlainText.begin(), PlainText.end());
}

Post Reply