Сбой DPAPI с CryptographicException при попытке расшифровать файлы cookie Chrome - PullRequest
2 голосов
/ 14 февраля 2020

Я пытаюсь получить сеанс из моего Chrome браузера. я могу видеть 2 файла cook ie в инструментах разработчика. но это неудобно для пользователя, чтобы получить значения cook ie из браузера, я хотел бы сделать это в коде. поэтому я использую этот код, чтобы получить Chrome профиль по умолчанию Cook ie sqlite DB:

string local = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string path = @"Google\Chrome\User Data\Default\Cookies";

path = Path.Combine(local, path);

Далее я создаю соединение SQLite и запрашиваю

var cmd = new SQLiteCommand("SELECT encrypted_value, name FROM cookies WHERE host_key = 'my_host_ip'", con);

, затем я читаю результаты

byte[] encryptedCookie = (byte[])r.GetValue(r.GetOrdinal("encrypted_value"));

и попробуйте расшифровать его:

var decodedData = ProtectedData.Unprotect(encryptedCookie, null, DataProtectionScope.CurrentUser);
var plainText = Encoding.ASCII.GetString(decodedData);

и здесь я получил исключение

System.Security.Cryptography.CryptographicException

я знаю, что ДОЛЖЕН расшифровать повар ie содержимое под той же учетной записью пользователя, под которой был запущен браузер (на той же машине), и для этого используется параметр DataProtectionScope.CurrentUser

Я вижу 63 байта в отладчике (в массиве encryptedCookie) Я также вижу эти байты в поле BLOB БД SQLite. но метод Unprotect выдает ошибку System.Security.Cryptography.CryptographicException: Invalid data.

мой код работает нормально на 5 разных P C в моем офисе (win10, win7), но не работает на моем разработчику P C ( win10, vs2019).

Я думаю, что проблема в моих Windows настройках или где-то еще, а не в моем коде. так что я делаю не так?

интересное замечание - я нашел скрипт PowerShell, который делает то же самое (через Add-Type -AssemblyName System.Security) - получаю cook ie и расшифровываю его. этот сценарий также отлично работает в 5 офисных P C, но не работает на моем P C.

моя Windows установка является новой, у меня нет программного обеспечения AV. мы подключились к одному и тому же корпоративному домену, и у нас одинаковые настройки безопасности.

UPD 1 небольшой бонус:

  1. get cook ie value from * Браузер 1080 * (32 символа, JSESSIONID)
  2. создает простое приложение, которое защищает это значение с помощью CurrentUser области защиты. теперь у меня есть массив из 178 байт (результат # 1)
  3. просмотр базы данных кукисов Chrome с а) https://sqliteonline.com/ и б) База данных. Net настольное приложение. эти два метода дают мне один и тот же результат: только 63 байта зашифрованных данных cook ie (результат # 2). я также могу получить тот же результат с моим c# приложением, используя System.Data.SQLite

, поэтому результаты не равны по длине или содержанию # 1! = результат # 2

выглядит как Chrome значение cook ie, защищенное другой областью действия (возможно, учетной записью администратора?), но я вижу имя моей учетной записи пользователя в диспетчере задач в процессе Chrome

PS Я использую . net 4.7.2

UPD 2 Я нашел этот метод в источниках хрома

bool OSCrypt::DecryptString(const std::string& ciphertext,
                            std::string* plaintext) {
  if (!base::StartsWith(ciphertext, kEncryptionVersionPrefix,
                        base::CompareCase::SENSITIVE))
    return DecryptStringWithDPAPI(ciphertext, plaintext);

  crypto::Aead aead(crypto::Aead::AES_256_GCM);

  auto key = GetEncryptionKeyInternal();
  aead.Init(&key);

  // Obtain the nonce.
  std::string nonce =
      ciphertext.substr(sizeof(kEncryptionVersionPrefix) - 1, kNonceLength);
  // Strip off the versioning prefix before decrypting.
  std::string raw_ciphertext =
      ciphertext.substr(kNonceLength + (sizeof(kEncryptionVersionPrefix) - 1));

  return aead.Open(raw_ciphertext, nonce, std::string(), plaintext);
}

, поэтому DPAPI используется только тогда, когда BLOB NOT начинается с v10 символы. но мой повар ie BLOB начинается с v10 символов, и, согласно коду, используется другой криптоалгоритм, но я не понимаю, ПОЧЕМУ.

Ответы [ 2 ]

6 голосов
/ 10 марта 2020

Для людей, которые ищут код, я расширяю ответ Цербера. Начиная с версии Chrome 80, cookie-файлы шифруются с использованием алгоритма AES256-GCM, а ключ шифрования AES шифруется системой шифрования DPAPI, а зашифрованный ключ сохраняется в файле «Local State».

byte[] encryptedData=<data stored in cookie file>
string encKey = File.ReadAllText(localAppDataPath + @"\Google\Chrome\User Data\Local State");
encKey = JObject.Parse(encKey)["os_crypt"]["encrypted_key"].ToString();
var decodedKey = System.Security.Cryptography.ProtectedData.Unprotect(Convert.FromBase64String(encKey).Skip(5).ToArray(), null, System.Security.Cryptography.DataProtectionScope.LocalMachine);
_cookie = _decryptWithKey(encryptedData, decodedKey, 3);

Размер ключа составляет 256 бит. Формат зашифрованного сообщения: полезная нагрузка ('v12') + nonce (12 байт) + cipherText

private string _decryptWithKey(byte[] message, byte[] key, int nonSecretPayloadLength)
{
    const int KEY_BIT_SIZE = 256;
    const int MAC_BIT_SIZE = 128;
    const int NONCE_BIT_SIZE = 96;

    if (key == null || key.Length != KEY_BIT_SIZE / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KEY_BIT_SIZE), "key");
    if (message == null || message.Length == 0)
        throw new ArgumentException("Message required!", "message");

    using (var cipherStream = new MemoryStream(message))
    using (var cipherReader = new BinaryReader(cipherStream))
    {
        var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength);
        var nonce = cipherReader.ReadBytes(NONCE_BIT_SIZE / 8);
        var cipher = new GcmBlockCipher(new AesEngine());
        var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce);
        cipher.Init(false, parameters);
        var cipherText = cipherReader.ReadBytes(message.Length);
        var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];
        try
        {
            var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
            cipher.DoFinal(plainText, len);
        }
        catch (InvalidCipherTextException)
        {
            return null;
        }
        return Encoding.Default.GetString(plainText);
    }
}

Необходимые пакеты

1) Newtonsoft JSON. net

2) Пакет Крипто Bouncy Castle

5 голосов
/ 25 февраля 2020

Я наконец понял это. согласно источникам хрома, для расшифровки значения повара ie используются два метода.

  1. если значение повара ie начинается с v10 символов, мы используем AES_256_GCM
  2. в противном случае DPAPI используется

для первого метода, нам нужен ключ и одноразовый номер. ключ находится в файлах Google Chrome, а nonce - в зашифрованном значении cook ie.

мне остается неясным - что определяет, какой метод используется

...