Bedankt Paul-Jan, het ware nog beter geweest als ik het preciese probleem met de aes-256 decryptie kon omschrijven maar dat is nu net deel waar ik niets van af weet
PS de bron van het python project is https://github.com/taizan-hokuto/chrome_cookie
Bedankt Paul-Jan, het ware nog beter geweest als ik het preciese probleem met de aes-256 decryptie kon omschrijven maar dat is nu net deel waar ik niets van af weet
PS de bron van het python project is https://github.com/taizan-hokuto/chrome_cookie
Had je al een methode om AES-256 in GCM mode te decoderen?
Want tot zover was ik ook gekomen
De cookies bevatten #01 #00 #00 etc en dan kun je ze gewoon zonder key nogmaals door CryptUnprotectData gooien. Dit zijn de cookies van voor v80.
En daarna bevatten ze v10 + data (0x763130) Ik neem aan dat je die records structuur al gevonden hebt.
Alleen dan nog door een AES-256-GCM decryptor gooien samen met de nonce (onderdeel van dat record) en je voorheen verkregen key.
De tag (van 16 bytes) kun je tijdens het decrypten gebruiken om de decryptie te controleren.
Het grootste probleem zal dus die AES-256-GCM decryptor zijn.
Edit: Tadatadadaaaaaaaaaaaaa
Ik heb het werkend.
Wel in Lazarus en met heel veel gehannes om jwawincrypt.pas en jwabcrypt.pas werkend te krijgen.
Delphi Code:
Line := ''; Line := SQLQuery1.FieldByName('host_key').AsString; Line := Line + ' ' + SQLQuery1.FieldByName('name').AsString; Encrypted_Value := SQLQuery1.FieldByName('encrypted_value').AsBytes; // v10 (i.e. 0x763130) // of // DPAPI = 0x01000000D08C9DDF0115D1118C7A00C04FC297EB if Copy(TEncoding.ANSI.GetAnsiString(Encrypted_Value), 1, 3) = 'v10' then begin // v10 (i.e. 0x763130) // data = bytes.fromhex('763130...') # the encrypted cookie // nonce = data[3:3+12] // ciphertext = data[3+12:-16] // tag = data[-16:] // cipher = AES.new(decrypted_key, AES.MODE_GCM, nonce=nonce) // plaintext = cipher.decrypt_and_verify(ciphertext, tag) # the decrypted cookie if Length(Encrypted_Value) > 3 + 12 + 16 then begin SetLength(Nonce, 12); SetLength(InBuf, Length(Encrypted_Value) - 15); SetLength(OutBuf, Length(InBuf) - 16 { tag}); Move(Encrypted_Value[3], Nonce[0], 12); Move(Encrypted_Value[15], InBuf[0], Length(InBuf)); if bdecrypt_gcm('AES', InBuf, @OutBuf[0], Key, Nonce) <> 0 then begin Line := Line + ' - ' + TEncoding.UTF8.GetString(OutBuf); end; end; end else begin // DPAPI = 0x01000000D08C9DDF0115D1118C7A00C04FC297EB DataIn.pbData := @Encrypted_Value[0]; DataIn.cbData := Length(Encrypted_Value); if CryptUnprotectData(@DataIn, nil, nil, nil, nil, 0, @DataOut) then begin SetLength(Result, DataOut.cbData); Move(DataOut.pbData^, Result[0], DataOut.cbData); LocalFree(cardinal(DataOut.pbData)); LocalFree(cardinal(DataIn.pbData)); Line := Line + ' - ' + TEncoding.ANSI.GetAnsiString(Result); end; end;
Last edited by rvk; 15-Jun-20 at 23:06.
Alles nu in één unit gezet (geen jwa en geen andere dependencies meer).
Laat maar weten als je interesse hebt. Dan plop ik het voorbeeldprojectje hier neer.
(maar code uit voorgaand bericht was de essentie)
Ok. Hier is het Lazarus projectje.
Zelf even de AFilename aanpassen naar de directory voor je cookie file en Local state. Kan natuurlijk ook automatisch maar dit was meer even snel en dirty in elkaar gezet
Dit is op 2 plaatsen. Regel 92 voor de Local State file om de envrypted key op te halen en op regel 210 voor het SQLite bestand met de cookies.
Als eerste zoek je naar de encrypted key om chrome v80+ cookies te kunnen decrypten.
In de Local State zoek je dus naar os_crypt en dan naar encrypted_key.
Deze key wordt daarna alleen gebruikt voor de encryptie van de cookies van Chrome v80 en hoger.
De encrypted key moet door CryptUnprotectData gegooid worden om de key te krijgen
Voor cookies onder de v80 heb je die key echter niet nodig. Daarvan kun je de cookie value direct door CryptUnprotectData gooien.
Voor v80+ cookies heb je de key dus wel nodig en moet die met AES GCM mode decrypted worden.
Het was een beetje een gedoe om een decrypter te vinden daarvoor maar uiteindelijk kan dat dus door Windows API zelf gedaan worden in BCryptDecrypt.
De encrypted value van een v80+ cookie ziet er zo uit (en is eigenlijk een record)
data = bytes.fromhex('763130') # the encrypted cookie
nonce = data[3 tot 3+12]
ciphertext = data[3+12 tot eind-16]
tag = data[eind-16 tot eind]
Overigens doe ik wel een BCryptOpenAlgorithmProvider maart ik heb eigenlijk geen idee of het verplicht is om ook weer een Close te doen op die hProvider (BCryptCloseAlgorithmProvider). Het lijkt in ieder geval goed te gaan zonder.
Ik hoop dat je het een beetje naar Delphi kunt vertalen.
Bedankt Rik, als we elkaar ooit ontmoeten, ik trakteer
Ik probeer het morgen in een Delphi project te gieten.
Ondertussen succesvol overgenomen van Lazarus naar Delphi
Nu nog uitzoeken hoe ik de cookie expires_utc moet omzetten naar een Delphi datetime.
Blijkbaar is bevat deze timestamp het aantal microseconden sinds 1-1-1601. Zie ook WebKit/Chrome Timestamp Converter.
Ik kon geen bestaande Delphi functie vinden (in DateUtils) die deze conversie doet. Wel van een normale Unix timestamp, maar dat is dit niet.
Onderstaande functie lijkt het goed te converteren naar een UTC datetime, maar het lijkt me verstandig dat je 'm nog even test met wat waarden waarvan je weet wat het antwoord moet zijn.
Aangezien de timestamp in UTC is, is het resultaat van de functie dat ook. Met TTimeZone.ToLocalTime kan je deze omzetten naar je lokale timezone.
Delphi Code:
function CookieTimestampToUTCDateTime(const CookieTimeStamp: Int64): TDateTime; const DaysBetweenCookieAndDelphi= 109205; var CookieDateTime: TDateTime; begin CookieDateTime:= CookieTimeStamp / MSecsPerDay / 1000; Result := CookieDateTime- DaysBetweenCookieAndDelphi; end;
Last edited by GolezTrol; 22-Jun-20 at 16:59.
1+1=b
Had ik gisteren dan nog gevonden, probleem was wel dat die waarde in de SqlLite db een Int64 is en dat zowel Unidac als Firedac het als Int32 ophalen.
In Firedac heb ik dat kunnen oplossen door de Maprules aan te passen, in Unidac heb ik niet zo'n mogelijkheid gevonden, ik heb bij hen een ticket aangemaakt hiervoor.
Delphi Code:
CookieTime := qry_chromecookies.FieldByName('expires_utc').AsLargeInt; CookieTime := (CookieTime div 1000000) - 11644473600; Expires := UnixToDateTime(CookieTime);
Waar moet ik veranderen om wachtwoorden te decoderen (User Data\Default\Login Data)? Ik kan me voorstellen dat dat hier is:
Ik heb deze code op Delphi 10 geprobeerd, maar de functie BCryptDecrypt retourneert altijd fout STATUS_AUTH_TAG_MISMATCH:Code:SetLength(Nonce, 12); SetLength(InBuf, Length(Encrypted_Value) - 15); SetLength(OutBuf, Length(InBuf) - 16 { tag}); Move(Encrypted_Value[3], Nonce[0], 12); Move(Encrypted_Value[15], InBuf[0], Length(InBuf));
Delphi Code:
unit Unit2; interface uses Windows, Messages, SysUtils, StrUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, SQLiteTable3, SQLite3, JSON, IOUtils, System.NetEncoding, Types, Vcl.StdCtrls; type _CRYPTOAPI_BLOB = record cbData: DWORD; pbData: PByte; end; DATA_BLOB = _CRYPTOAPI_BLOB; PDATA_BLOB = ^DATA_BLOB; type _CRYPTPROTECT_PROMPTSTRUCT = record cbSize: DWORD; dwPromptFlags: DWORD; hwndApp: HWND; szPrompt: PWideChar; end; CRYPTPROTECT_PROMPTSTRUCT = _CRYPTPROTECT_PROMPTSTRUCT; PCRYPTPROTECT_PROMPTSTRUCT = ^CRYPTPROTECT_PROMPTSTRUCT; function CryptUnprotectData(pDataIn: PDATA_BLOB; ppszDataDescr: PPWideChar; pOptionalEntropy: PDATA_BLOB; pReserved: Pointer; pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT; dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall; external 'Crypt32.dll'; type BCRYPT_ALG_HANDLE = THandle; BCRYPT_HANDLE = THandle; BCRYPT_KEY_HANDLE = THandle; NTSTATUS = LONG; TNTStatus = NTSTATUS; type _BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = record cbSize: ULONG; dwInfoVersion: ULONG; pbNonce: PBYTE; cbNonce: ULONG; pbAuthData: PBYTE; cbAuthData: ULONG; pbTag: PBYTE; cbTag: ULONG; pbMacContext: PBYTE; cbMacContext: ULONG; cbAAD: ULONG; cbData: ULONGLONG; dwFlags: ULONG; end; BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = _BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO; const bcryptdll = 'bcrypt.dll'; BCRYPT_CHAINING_MODE_: WideString = 'ChainingMode'; BCRYPT_CHAIN_MODE_GCM_: WideString = 'ChainingModeGCM'; function BCryptOpenAlgorithmProvider(out phAlgorithm: BCRYPT_ALG_HANDLE; pszAlgId, pszImplementation: LPCWSTR; dwFlags: ULONG): TNTStatus; stdcall; external bcryptdll; function BCryptSetProperty(hObject: BCRYPT_HANDLE; pszProperty: LPCWSTR; pbInput: PUCHAR; cbInput: ULONG; dwFlags: ULONG): TNTStatus; stdcall; external bcryptdll; function BCryptGenerateSymmetricKey(hAlgorithm: BCRYPT_ALG_HANDLE; out phKey: BCRYPT_KEY_HANDLE; pbKeyObject: PUCHAR; cbKeyObject: ULONG; pbSecret: PUCHAR; cbSecret, dwFlags: ULONG): TNTStatus; stdcall; external bcryptdll; function BCryptDecrypt(hKey: BCRYPT_KEY_HANDLE; pbInput: PUCHAR; cbInput: ULONG; pPaddingInfo: Pointer; pbIV: PUCHAR; cbIV: ULONG; pbOutput: PUCHAR; cbOutput: ULONG; out pcbResult: ULONG; dwFlags: ULONG): TNTStatus; stdcall; external bcryptdll; type TForm2 = class(TForm) Button1: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form2: TForm2; implementation {$R *.dfm} function TempFolder: string; var BufFolder: array[0..MAX_PATH] of Char; begin GetTempPath(SizeOf(BufFolder), BufFolder); Result := IncludeTrailingPathDelimiter(string(BufFolder)); end; function ByteArrayToHexStr(const ByteArray: TByteDynArray): string; var i: Integer; begin Result := ''; try for i := 0 to Length(ByteArray) - 1 do Result := Result + Format('%x|', [ByteArray[i]]); except on e: Exception do begin Result := ''; end; end; end; function UnprotectData(fpDataIn: TBytes): TBytes; var DataIn, DataOut: DATA_BLOB; begin DataOut.cbData := 0; DataOut.pbData := nil; DataIn.cbData := Length(fpDataIn); DataIn.pbData := @fpDataIn[0]; if not CryptUnprotectData(@DataIn, nil, nil, nil, nil, 0, @DataOut) then RaiseLastOSError; SetLength(Result, DataOut.cbData * 2); Move(DataOut.pbData^, Result[0], DataOut.cbData * 2); LocalFree(HLOCAL(DataOut.pbData)); end; function GetEncryptedKey: TBytes; var FileName, DecodedKey: string; FJsonObj: TJSONObject; JsonObj: TJSONObject; JsonCrypt: TJSONString; BytesString: TBytes; begin FileName := TFile.ReadAllText(GetEnvironmentVariable('LOCALAPPDATA') + '\Google\Chrome\User Data\Local State'); FJsonObj := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(FileName), 0) as TJSONObject; try JsonObj := FJsonObj.GetValue('os_crypt') as TJSONObject; JsonCrypt := JsonObj.GetValue('encrypted_key') as TJSONString; BytesString := TNetEncoding.Base64.DecodeStringToBytes(JsonCrypt.Value); Delete(BytesString, 0, 5); Result := UnprotectData(BytesString); SetString(DecodedKey, PAnsiChar(@Result[0]), Length(Result)); Form2.Memo1.Lines.Add(#13#10 + DecodedKey + #13#10); Form2.Memo1.Lines.Add(#13#10 + ByteArrayToHexStr(Result) + #13#10#13#10); finally FJsonObj.Free; end; end; function BDecrypt_GCM(Algoritme: LPCWSTR; Encryped: array of Byte; Output: Pointer; const gKey, IV: array of Byte): ULONG; const AES_BLOCK_SIZE = 16; var hProvider: BCRYPT_ALG_HANDLE; Decrypted: array[0..1023] of Byte; hKey: BCRYPT_KEY_HANDLE; Status: NTSTATUS; Info: BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO; begin Result := 0; hProvider := 0; hKey := 0; Status := BCryptOpenAlgorithmProvider(hProvider, Algoritme, nil, 0); if Status <> 0 then Exit; Status := BCryptSetProperty(hProvider, pwidechar(BCRYPT_CHAINING_MODE_), @BCRYPT_CHAIN_MODE_GCM_[1], sizeof(BCRYPT_CHAIN_MODE_GCM_), 0); if Status <> 0 then Exit; Status := BCryptGenerateSymmetricKey(hProvider, hKey, nil, 0, @gKey[0], sizeof(gKey), 0); if Status <> 0 then Exit; FillChar(Decrypted, Length(Decrypted), 0); FillChar(Info, Sizeof(Info), 0); Info.cbSize := Sizeof(Info); Info.dwInfoVersion := 1; Info.pbNonce := @IV[0]; Info.cbNonce := SizeOf(IV); Info.pbTag := @Encryped[SizeOf(Encryped) - AES_BLOCK_SIZE]; Info.cbTag := AES_BLOCK_SIZE; Status := BCryptDecrypt(hKey, @Encryped[0], SizeOf(Encryped) - AES_BLOCK_SIZE, @Info, nil, 0, @Decrypted[0], Length(Decrypted), Result, 0); if Status <> 0 then begin Form2.Memo1.Lines.Add(IntToHex(Status)); Exit; end; if Output = nil then Output := AllocMem(Result); CopyMemory(Output, @Decrypted[0], Result); end; procedure TForm2.Button1Click(Sender: TObject); var DB: TSQLiteDatabase; Table: TSQLiteTable; DataIn, DataOut: DATA_BLOB; DataStream: TMemorystream; Key, Nonce, InBuf, OutBuf: TBytes; password, s: string; begin Key := GetEncryptedKey; DB := TSQLiteDatabase.Create(GetEnvironmentVariable('LOCALAPPDATA') + '\Google\Chrome\User Data\Default\Login Data'); Table := DB.GetTable('SELECT * FROM logins'); while not Table.EOF do begin password := Table.FieldAsString(Table.FieldIndex['password_value']); InBuf := TEncoding.ANSI.GetBytes(password); if (LeftStr(password, 3) = 'v10') then begin SetLength(Nonce, 12); CopyMemory(@Nonce[0], @InBuf[0 + 3], Length(Nonce)); SetLength(OutBuf, Length(InBuf) - 12 - 3); CopyMemory(@OutBuf[0], @InBuf[0 + 3 + 12], Length(OutBuf)); SetLength(OutBuf, Length(OutBuf) - 16); if BDecrypt_GCM('AES', InBuf, @OutBuf[0], Key, Nonce) <> 0 then password := TEncoding.ANSI.GetString(OutBuf); end else begin DataStream := TMemoryStream.Create; try DataStream := Table.FieldAsBlob(Table.FieldIndex['password_value']); DataIn.pbData := DataStream.Memory; DataIn.cbData := DataStream.Size; CryptUnProtectData(@DataIn, nil, nil, nil, nil, 0, @DataOut); SetString(password, PAnsiChar(DataOut.pbData), DataOut.cbData); finally DataStream.Free; end; end; Memo1.Lines.Add(Table.FieldAsString(Table.FieldIndex['origin_url']) + #13#10 + Table.FieldAsString(Table.FieldIndex['username_value']) + ' - ' + password + #13#10 + #13#10); Table.Next; end; end; end.
referentie voor lijnen van 222 tot 226.
Last edited by @someone; 03-Aug-22 at 23:11.
1601 is de originele start datum van julian. Dus misschien kan je het integer deel middels de julian functions doen?
https://docs.microsoft.com/en-us/win...-bcryptdecrypt
"
STATUS_AUTH_TAG_MISMATCH
The computed authentication tag did not match the value supplied in the pPaddingInfo parameter. "
@someone, Ik hoefde eigenlijk vrij weinig aan te passen in mijn oorspronkelijke project uit Lazarus om dit ook voor passwords werkend te maken.
Ik heb het wel voor Brave gedaan maar aangezien de oorspronkelijke voor de cookies voor Chrome was en bij Brave ook werkte, neem ik aan dat de password versie, die op Brave werkt, ook gewoon op Chrome werkt.
Ik heb de volgende regel aangepast.
Delphi Code:
//AFileName := 'C:\Users\Rik\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies'; // <- cookies //AFileName := 'C:\Users\Rik\AppData\Local\Google\Chrome\User Data\Default\Login Data'; // <- chrome passwords AFileName := 'C:\Users\Rik\AppData\Local\BraveSoftware\Brave-Browser\User Data\Default\Login Data'; // <- Brave passwords SQLite3Connection1.Databasename := AFilename; SQLQuery1.SQL.Text := 'SELECT origin_url, username_value, password_value FROM logins'; //... Line := SQLQuery1.FieldByName('origin_url').AsString; Line := Line + ' ' + SQLQuery1.FieldByName('username_value').AsString; Encrypted_Value := SQLQuery1.FieldByName('password_value').AsBytes;
Meer heb ik niet aan hoeven te passen.
(Als je er niet uitkomt kan ik de Lazarus versie van decrypt_password.zip ook wel uploaden, de decrypt_cookie.zip hangt aan de eerdere post.)
PS. Voor Brave passwords moest Brave wel afgesloten worden anders is de database locked. Ik neem aan dat dat bij Chrome ook zo is.
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks