1

I've created this, and it's able to get the cookies from Google Chrome when given a specific domain name. However, the values are decrypted. I know there must be a way I can modify my code to decrypt these values.

using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Test
{
    public class Test
    {
        public List<Data> GetCookies(string hostname)
        {
            List<Data> data = new List<Data>();
            if (ChromeCookiesExists())
            {
                try
                {
                    using (var conn = new SqliteConnection($"Data Source={ChromeCookiePath}"))
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = $"SELECT name,encrypted_value,host_key FROM cookies WHERE host_key = '{hostname}'";

                        conn.Open();
                        using (var reader = cmd.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                if (!data.Any(a => a.Name == reader.GetString(0)))
                                {
                                    data.Add(new Data()
                                    {
                                        Name = reader.GetString(0),
                                        Value = reader.GetString(1)  //HERE is my problem because this returns encrypted value not decrypted
                                    });
                                }
                            }
                        }
                        conn.Close();
                    }
                }
                catch { }
            }
            return data;
        }

        private string ChromeCookiePath = @"C:\Users\" + Environment.UserName + @"\AppData\Local\Google\Chrome\User Data\Default\Cookies";


        private bool ChromeCookiesExists()
        {
            if (File.Exists(ChromeCookiePath))
                return true;
            return false;
        }

        public class Data
        {
            public string Name { get; set; }
            public string Value { get; set; }
        }
    }
}

This code outputs a struct called Data which contains the name and the value of the cookie (just not decrypted atm).

zaitsman
  • 8,984
  • 6
  • 47
  • 79
  • I think you would first need to know the encryption algorithms and keys used to encrypt those cookies. – jwdonahue Aug 03 '21 at 21:46
  • @jwdonahue Thanks for the comment. I have seen some get keys by doing something like this but I'm not sure how to utilize it `private byte[] ChromeGetKey() { string encKey = File.ReadAllText(ChromeKeyPath); // reads the file (string) encKey = JObject.Parse(encKey)["os_crypt"]["encrypted_key"].ToString(); // parses the string return ProtectedData.Unprotect(Convert.FromBase64String(encKey).Skip(5).ToArray(), null, DataProtectionScope.LocalMachine); // decrypts the key and returns a byte Array }` – Flint Charles Aug 03 '21 at 21:49
  • I assume you're referring to Chrome v80.0+. Decrypting cookies is the same as decrypting passwords. In [this post](https://stackoverflow.com/q/60640302/16317602) you will find everything required for decryption. You need the following NuGet packages: _BouncyCastle_, _Microsoft.Data.Sqlite_, _Microsoft.Data.Sqlite.Core_, _Newtonsoft.Json_ and _System.Security.Cryptography.ProtectedData_. – Topaco Aug 04 '21 at 07:57
  • @Topaco Thank you! I will look at it soon. – Flint Charles Aug 04 '21 at 20:01

1 Answers1

5

Thanks to Topaco's comment and UnCavoHDMI, I was able to put this together.

class ChromeManager
        {
            public List<Cookie> GetCookies(string hostname)
            {
                string ChromeCookiePath = @"C:\Users\" + Environment.UserName + @"\AppData\Local\Google\Chrome\User Data\Default\Cookies";
                List<Cookie> data = new List<Cookie>();
                if (File.Exists(ChromeCookiePath))
                {
                    try
                    {
                        using (var conn = new SqliteConnection($"Data Source={ChromeCookiePath}"))
                        using (var cmd = conn.CreateCommand())
                        {
                            cmd.CommandText = $"SELECT name,encrypted_value,host_key FROM cookies WHERE host_key = '{hostname}'";
                            byte[] key = AesGcm256.GetKey();

                            conn.Open();
                            using (var reader = cmd.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    if (!data.Any(a => a.Name == reader.GetString(0)))
                                    {
                                        byte[] encryptedData = GetBytes(reader, 1);
                                        byte[] nonce, ciphertextTag;
                                        AesGcm256.prepare(encryptedData, out nonce, out ciphertextTag);
                                        string value = AesGcm256.decrypt(ciphertextTag, key, nonce);

                                        data.Add(new Cookie()
                                        {
                                            Name = reader.GetString(0),
                                            Value = value
                                        });
                                    }
                                }
                            }
                            conn.Close();
                        }
                    }
                    catch { }
                }
                return data;

            }

            private byte[] GetBytes(SqliteDataReader reader, int columnIndex)
            {
                const int CHUNK_SIZE = 2 * 1024;
                byte[] buffer = new byte[CHUNK_SIZE];
                long bytesRead;
                long fieldOffset = 0;
                using (MemoryStream stream = new MemoryStream())
                {
                    while ((bytesRead = reader.GetBytes(columnIndex, fieldOffset, buffer, 0, buffer.Length)) > 0)
                    {
                        stream.Write(buffer, 0, (int)bytesRead);
                        fieldOffset += bytesRead;
                    }
                    return stream.ToArray();
                }
            }

            public class Cookie
            {
                public string Name { get; set; }
                public string Value { get; set; }
            }

            

            class AesGcm256
            {
                public static byte[] GetKey()
                {
                    string sR = string.Empty;
                    var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
                    string path = @"C:\Users\" + Environment.UserName + @"\AppData\Local\Google\Chrome\User Data\Local State";

                    string v = File.ReadAllText(path);

                    dynamic json = JsonConvert.DeserializeObject(v);
                    string key = json.os_crypt.encrypted_key;

                    byte[] src = Convert.FromBase64String(key);
                    byte[] encryptedKey = src.Skip(5).ToArray();

                    byte[] decryptedKey = ProtectedData.Unprotect(encryptedKey, null, DataProtectionScope.CurrentUser);

                    return decryptedKey;
                }

                public static string decrypt(byte[] encryptedBytes, byte[] key, byte[] iv)
                {
                    string sR = String.Empty;
                    try
                    {
                        GcmBlockCipher cipher = new GcmBlockCipher(new AesEngine());
                        AeadParameters parameters = new AeadParameters(new KeyParameter(key), 128, iv, null);

                        cipher.Init(false, parameters);
                        byte[] plainBytes = new byte[cipher.GetOutputSize(encryptedBytes.Length)];
                        Int32 retLen = cipher.ProcessBytes(encryptedBytes, 0, encryptedBytes.Length, plainBytes, 0);
                        cipher.DoFinal(plainBytes, retLen);

                        sR = Encoding.UTF8.GetString(plainBytes).TrimEnd("\r\n\0".ToCharArray());
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        Console.WriteLine(ex.StackTrace);
                    }

                    return sR;
                }

                public static void prepare(byte[] encryptedData, out byte[] nonce, out byte[] ciphertextTag)
                {
                    nonce = new byte[12];
                    ciphertextTag = new byte[encryptedData.Length - 3 - nonce.Length];

                    System.Array.Copy(encryptedData, 3, nonce, 0, nonce.Length);
                    System.Array.Copy(encryptedData, 3 + nonce.Length, ciphertextTag, 0, ciphertextTag.Length);
                }
            }
        }
  • 3
    In my version of Chrome, which is the latest one currently, the cookies file was in a folder named "Network". Therefore, the fifth line should be replaced with the following: `string ChromeCookiePath = @"C:\Users\" + Environment.UserName + @"\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies";` – Amir Zare Mar 11 '22 at 06:45