diff --git a/README.md b/README.md
index 4528d72..1f070ce 100644
--- a/README.md
+++ b/README.md
@@ -4,21 +4,37 @@ hack-browser-data is an open-source tool that could help you export data from br
### Supported Browser
+#### Windows
| Browser | Password | Cookie | Bookmark | History |
| :---------------------------------- | :------: | :----: | :------: | :-----: |
-| Chrome <= 80 [Windows] | ✅ | ✅ | ✅ | ✅ |
-| Chrome > 80 [Windows] | ✅ | ✅ | ✅ | ✅ |
-| Chrome [MacOS]
(require password) | ✅ | ✅ | ✅ | ✅ |
-| Edge [Windows] | ✅ | ✅ | ✅ | ✅ |
-| Edge [MacOS]
(require password) | ✅ | ✅ | ✅ | ✅ |
-| 360 Speed Browser [Windows] | ✅ | ✅ | ✅ | ✅ |
-| QQ Browser [Windows] | ✅ | ✅ | ✅ | ✅ |
-| FireFox [MacOS] | ✅ | ✅ | ✅ | ✅ |
-| FireFox [Windows] | ❌ | ❌ | ❌ | ❌ |
-| Safari [MacOS] | ❌ | ❌ | ❌ | ❌ |
-| Internet Explorer [Windows] | ❌ | ❌ | ❌ | ❌ |
-| 360 Secure Browser [Windows] | ❌ | ❌ | ❌ | ❌ |
-| Chrome [Linux] | ❌ | ❌ | ❌ | ❌ |
+| Google Chrome (Full Version) | ✅ | ✅ | ✅ | ✅ |
+| Firefox | ✅ | ✅ | ✅ | ✅ |
+| Microsoft Edge | ✅ | ✅ | ✅ | ✅ |
+| 360 Speed Browser | ✅ | ✅ | ✅ | ✅ |
+| QQ Browser | ✅ | ✅ | ✅ | ✅ |
+| Internet Explorer | ❌ | ❌ | ❌ | ❌ |
+
+#### MacOS
+
+Because of the security policies, all those browsers require a password.
+
+| Browser | Password | Cookie | Bookmark | History |
+| :---------------------------------- | :------: | :----: | :------: | :-----: |
+| Google Chrome | ✅ | ✅ | ✅ | ✅ |
+| Firefox | ✅ | ✅ | ✅ | ✅ |
+| Microsoft Edge | ✅ | ✅ | ✅ | ✅ |
+| Safari | ❌ | ❌ | ❌ | ❌ |
+
+#### Linux
+
+These browsers will be supported in the future.
+
+| Browser | Password | Cookie | Bookmark | History |
+| :---------------------------------- | :------: | :----: | :------: | :-----: |
+| Firefox | ❌ | ❌ | ❌ | ❌ |
+| Google Chrome | ❌ | ❌ | ❌ | ❌ |
+| Microsoft Edge | ❌ | ❌ | ❌ | ❌ |
+
### Install
diff --git a/cmd/cmd.go b/cmd/cmd.go
index f7727f2..33a791a 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -1,7 +1,6 @@
package cmd
import (
- "fmt"
"hack-browser-data/core"
"hack-browser-data/log"
"hack-browser-data/utils"
@@ -41,8 +40,6 @@ func Execute() {
} else {
log.InitLog("error")
}
-
- utils.MakeDir(exportDir)
browserDir, key, err := utils.PickBrowser(browser)
if err != nil {
log.Fatal(err, " Available browsers: "+strings.Join(utils.ListBrowser(), "|"))
@@ -65,7 +62,7 @@ func Execute() {
dst := filepath.Base(v)
err := utils.CopyDB(v, dst)
if err != nil {
- log.Println(err)
+ log.Debug(err)
continue
}
core.ParseResult(dst)
@@ -74,16 +71,16 @@ func Execute() {
fileList := utils.GetDBPath(browserDir, utils.FirefoxLoginData, utils.FirefoxKey4DB, utils.FirefoxCookie, utils.FirefoxData)
for _, v := range fileList {
dst := filepath.Base(v)
- fmt.Println(dst)
err := utils.CopyDB(v, dst)
if err != nil {
- log.Println(err)
+ log.Debug(err)
continue
}
core.ParseResult(dst)
}
}
core.FullData.Sorted()
+ utils.MakeDir(exportDir)
if outputFormat == "json" {
err := core.FullData.OutPutJson(exportDir, browser, outputFormat)
if err != nil {
diff --git a/core/common.go b/core/common.go
index c12835f..dc33409 100644
--- a/core/common.go
+++ b/core/common.go
@@ -2,17 +2,13 @@ package core
import (
"bytes"
- "crypto/cipher"
- "crypto/des"
"crypto/hmac"
"crypto/sha1"
"database/sql"
- "encoding/asn1"
"encoding/base64"
"encoding/hex"
"hack-browser-data/log"
"hack-browser-data/utils"
- "io"
"io/ioutil"
"os"
"time"
@@ -106,7 +102,7 @@ func parseBookmarks() {
bookmarks, err := utils.ReadFile(utils.Bookmarks)
defer os.Remove(utils.Bookmarks)
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
r := gjson.Parse(bookmarks)
if r.Exists() {
@@ -128,17 +124,17 @@ func parseLogin() {
defer os.Remove(utils.LoginData)
defer func() {
if err := loginDB.Close(); err != nil {
- log.Println(err)
+ log.Debug(err)
}
}()
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
err = loginDB.Ping()
rows, err := loginDB.Query(queryLogin)
defer func() {
if err := rows.Close(); err != nil {
- log.Println(err)
+ log.Debug(err)
}
}()
for rows.Next() {
@@ -166,7 +162,7 @@ func parseLogin() {
login.Password = password
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
loginItemList = append(loginItemList, login)
}
@@ -182,17 +178,17 @@ func parseCookie() {
defer os.Remove(utils.Cookies)
defer func() {
if err := cookieDB.Close(); err != nil {
- log.Println(err)
+ log.Debug(err)
}
}()
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
err = cookieDB.Ping()
rows, err := cookieDB.Query(queryCookie)
defer func() {
if err := rows.Close(); err != nil {
- log.Println(err)
+ log.Debug(err)
}
}()
for rows.Next() {
@@ -241,17 +237,17 @@ func parseHistory() {
defer os.Remove(utils.History)
defer func() {
if err := historyDB.Close(); err != nil {
- log.Println(err)
+ log.Debug(err)
}
}()
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
err = historyDB.Ping()
rows, err := historyDB.Query(queryHistory)
defer func() {
if err := rows.Close(); err != nil {
- log.Println(err)
+ log.Debug(err)
}
}()
for rows.Next() {
@@ -268,7 +264,7 @@ func parseHistory() {
LastVisitTime: utils.TimeEpochFormat(lastVisitTime),
}
if err != nil {
- log.Println(err)
+ log.Debug(err)
continue
}
historyList = append(historyList, h)
@@ -375,179 +371,128 @@ func parseFirefoxData() {
}
var queryPassword = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
+var queryNssPrivate = `SELECT a11, a102 from nssPrivate`
-func checkKey1() (b [][]byte) {
+func GetDecryptKey() (b [][]byte) {
var (
- err error
- keyDB *sql.DB
- rows *sql.Rows
+ err error
+ keyDB *sql.DB
+ pwdRows *sql.Rows
+ nssRows *sql.Rows
)
-
+ defer func() {
+ if err := os.Remove(utils.FirefoxKey4DB); err != nil {
+ log.Error(err)
+ }
+ }()
keyDB, err = sql.Open("sqlite3", utils.FirefoxKey4DB)
defer func() {
if err := keyDB.Close(); err != nil {
log.Error(err)
}
}()
-
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
err = keyDB.Ping()
- rows, err = keyDB.Query(queryPassword)
+ pwdRows, err = keyDB.Query(queryPassword)
defer func() {
- if err := rows.Close(); err != nil {
- log.Println(err)
+ if err := pwdRows.Close(); err != nil {
+ log.Debug(err)
}
}()
- for rows.Next() {
+ for pwdRows.Next() {
var (
item1, item2 []byte
)
- if err := rows.Scan(&item1, &item2); err != nil {
+ if err := pwdRows.Scan(&item1, &item2); err != nil {
log.Error(err)
continue
}
b = append(b, item1, item2)
}
- return b
-}
-
-var queryNssPrivate = `SELECT a11, a102 from nssPrivate;`
-
-func checkA102() (b [][]byte) {
- keyDB, err := sql.Open("sqlite3", utils.FirefoxKey4DB)
-
- defer func() {
- if err := os.Remove(utils.FirefoxKey4DB); err != nil {
- log.Error(err)
- }
- }()
- defer func(closer io.Closer) {
- if err := keyDB.Close(); err != nil {
- log.Error(err)
- }
- }(keyDB)
-
if err != nil {
log.Error(err)
}
- rows, err := keyDB.Query(queryNssPrivate)
+ nssRows, err = keyDB.Query(queryNssPrivate)
defer func() {
- if err := rows.Close(); err != nil {
- log.Println(err)
+ if err := nssRows.Close(); err != nil {
+ log.Debug(err)
}
}()
- for rows.Next() {
+ for nssRows.Next() {
var (
a11, a102 []byte
)
- if err := rows.Scan(&a11, &a102); err != nil {
- log.Println(err)
+ if err := nssRows.Scan(&a11, &a102); err != nil {
+ log.Debug(err)
}
b = append(b, a11, a102)
}
return b
}
-/*
-ASN1 PBE Structures
-SEQUENCE (2 elem)
- SEQUENCE (2 elem)
- OBJECT IDENTIFIER
- SEQUENCE (2 elem)
- OCTET STRING (20 byte)
- INTEGER 1
- OCTET STRING (16 byte)
-*/
-
-type PBEAlgorithms struct {
- SequenceA
- CipherText []byte
-}
-
-type SequenceA struct {
- DecryptMethod asn1.ObjectIdentifier
- SequenceB
-}
-
-type SequenceB struct {
- EntrySalt []byte
- Len int
-}
-
-/*
-SEQUENCE (3 elem)
- OCTET STRING (16 byte)
- SEQUENCE (2 elem)
- OBJECT IDENTIFIER 1.2.840.113549.3.7 des-EDE3-CBC (RSADSI encryptionAlgorithm)
- OCTET STRING (8 byte)
- OCTET STRING (16 byte)
-*/
-type PasswordAsn1 struct {
- CipherText []byte
- SequenceIV
- Encrypted []byte
-}
-
-type SequenceIV struct {
- asn1.ObjectIdentifier
- Iv []byte
-}
-
-func decryptPBE(decodeItem []byte) (pbe PBEAlgorithms) {
- _, err := asn1.Unmarshal(decodeItem, &pbe)
- if err != nil {
- log.Error(err)
- }
- return
-}
-
func parseFirefoxKey4() {
- h1 := checkKey1()
+ h1 := GetDecryptKey()
globalSalt := h1[0]
decodedItem := h1[1]
+ a11 := h1[2]
+ a102 := h1[3]
keyLin := []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
- pbe := decryptPBE(decodedItem)
- m := checkPassword(globalSalt, pbe.EntrySalt, pbe.CipherText)
- var finallyKey []byte
+ pbe, err := utils.DecodeMeta(decodedItem)
+ if err != nil {
+ log.Error("decrypt meta data failed", err)
+ return
+ }
+ var masterPwd []byte
+ m, err := checkPassword(globalSalt, masterPwd, pbe)
+ if err != nil {
+ log.Error("decrypt firefox failed", err)
+ return
+ }
if bytes.Contains(m, []byte("password-check")) {
- log.Println("password-check success")
- h2 := checkA102()
- a11 := h2[0]
- a102 := h2[1]
+ log.Debugf("password-check success")
m := bytes.Compare(a102, keyLin)
if m == 0 {
- pbe2 := decryptPBE(a11)
+ pbe2, err := utils.DecodeMeta(a11)
+ if err != nil {
+ log.Error(err)
+ return
+ }
log.Debugf("decrypt asn1 pbe success")
- finallyKey = checkPassword(globalSalt, pbe2.EntrySalt, pbe2.CipherText)[:24]
+ finallyKey, err := checkPassword(globalSalt, masterPwd, pbe2)
+ finallyKey = finallyKey[:24]
+ if err != nil {
+ log.Error(err)
+ return
+ }
log.Debugf("finally key", finallyKey, hex.EncodeToString(finallyKey))
+ allLogins := GetLoginData()
+ for _, v := range allLogins {
+ log.Debug(hex.EncodeToString(v.encryptUser))
+ user, _ := utils.DecodeLogin(v.encryptUser)
+ log.Debug(hex.EncodeToString(v.encryptPass))
+ pwd, _ := utils.DecodeLogin(v.encryptPass)
+ log.Debug(user, user.CipherText, user.Encrypted, user.Iv)
+ u, err := utils.Des3Decrypt(finallyKey, user.Iv, user.Encrypted)
+ if err != nil {
+ log.Error(err)
+ return
+ }
+ p, err := utils.Des3Decrypt(finallyKey, pwd.Iv, pwd.Encrypted)
+ if err != nil {
+ log.Error(err)
+ return
+ }
+ FullData.LoginDataSlice = append(FullData.LoginDataSlice, loginData{
+ LoginUrl: v.LoginUrl,
+ UserName: string(u),
+ Password: string(p),
+ CreateDate: v.CreateDate,
+ })
+ }
}
}
- allLogins := GetLoginData()
- for _, v := range allLogins {
- log.Warn(hex.EncodeToString(v.encryptUser))
- s1 := decryptLogin(v.encryptUser)
- log.Warn(hex.EncodeToString(v.encryptPass))
- s2 := decryptLogin(v.encryptPass)
- log.Println(s1, s1.CipherText, s1.Encrypted, s1.Iv)
- block, err := des.NewTripleDESCipher(finallyKey)
- if err != nil {
- log.Println(err)
- }
- blockMode := cipher.NewCBCDecrypter(block, s1.Iv)
- sq := make([]byte, len(s1.Encrypted))
- blockMode.CryptBlocks(sq, s1.Encrypted)
- blockMode2 := cipher.NewCBCDecrypter(block, s2.Iv)
- sq2 := make([]byte, len(s2.Encrypted))
- blockMode2.CryptBlocks(sq2, s2.Encrypted)
- FullData.LoginDataSlice = append(FullData.LoginDataSlice, loginData{
- LoginUrl: v.LoginUrl,
- UserName: string(utils.PKCS5UnPadding(sq)),
- Password: string(utils.PKCS5UnPadding(sq2)),
- CreateDate: v.CreateDate,
- })
- }
}
var queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies`
@@ -559,17 +504,17 @@ func parseFirefoxCookie() {
defer os.Remove(utils.FirefoxCookie)
defer func() {
if err := cookieDB.Close(); err != nil {
- log.Println(err)
+ log.Debug(err)
}
}()
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
err = cookieDB.Ping()
rows, err := cookieDB.Query(queryFirefoxCookie)
defer func() {
if err := rows.Close(); err != nil {
- log.Println(err)
+ log.Debug(err)
}
}()
for rows.Next() {
@@ -600,7 +545,7 @@ func parseFirefoxCookie() {
}
-func checkPassword(globalSalt, entrySalt, encryptText []byte) []byte {
+func checkPassword(globalSalt, masterPwd []byte, pbe utils.MetaPBE) ([]byte, error) {
//byte[] GLMP; // GlobalSalt + MasterPassword
//byte[] HP; // SHA1(GLMP)
//byte[] HPES; // HP + EntrySalt
@@ -611,51 +556,24 @@ func checkPassword(globalSalt, entrySalt, encryptText []byte) []byte {
//byte[] tk;
//byte[] k2;
//byte[] k; // final value conytaining key and iv
- sha1.New()
- hp := sha1.Sum(globalSalt)
- log.Warn(hex.EncodeToString(hp[:]))
- log.Println(len(entrySalt))
- s := append(hp[:], entrySalt...)
- log.Warn(hex.EncodeToString(s))
+ glmp := append(globalSalt, masterPwd...)
+ hp := sha1.Sum(glmp)
+ s := append(hp[:], pbe.EntrySalt...)
chp := sha1.Sum(s)
- log.Warn(hex.EncodeToString(chp[:]))
- pes := paddingZero(entrySalt, 20)
+ pes := utils.PaddingZero(pbe.EntrySalt, 20)
tk := hmac.New(sha1.New, chp[:])
tk.Write(pes)
- pes = append(pes, entrySalt...)
- log.Warn(hex.EncodeToString(pes))
+ pes = append(pes, pbe.EntrySalt...)
k1 := hmac.New(sha1.New, chp[:])
k1.Write(pes)
- log.Warn(hex.EncodeToString(k1.Sum(nil)))
- log.Warn(hex.EncodeToString(tk.Sum(nil)))
- tkPlus := append(tk.Sum(nil), entrySalt...)
+ tkPlus := append(tk.Sum(nil), pbe.EntrySalt...)
k2 := hmac.New(sha1.New, chp[:])
k2.Write(tkPlus)
- log.Warn(hex.EncodeToString(k2.Sum(nil)))
k := append(k1.Sum(nil), k2.Sum(nil)...)
iv := k[len(k)-8:]
key := k[:24]
log.Warn("key=", hex.EncodeToString(key), "iv=", hex.EncodeToString(iv))
- block, err := des.NewTripleDESCipher(key)
- if err != nil {
- log.Println(err)
- }
- blockMode := cipher.NewCBCDecrypter(block, iv)
- sq := make([]byte, len(encryptText))
- blockMode.CryptBlocks(sq, encryptText)
- return sq
-}
-
-func paddingZero(s []byte, l int) []byte {
- h := l - len(s)
- if h <= 0 {
- return s
- } else {
- for i := len(s); i < l; i++ {
- s = append(s, 0)
- }
- return s
- }
+ return utils.Des3Decrypt(key, iv, pbe.Encrypted)
}
func GetLoginData() (l []loginData) {
@@ -676,7 +594,7 @@ func GetLoginData() (l []loginData) {
u, err = base64.StdEncoding.DecodeString(v.Get("encryptedUsername").String())
m.encryptUser = u
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
p, err = base64.StdEncoding.DecodeString(v.Get("encryptedPassword").String())
m.encryptPass = p
@@ -686,11 +604,3 @@ func GetLoginData() (l []loginData) {
}
return
}
-
-func decryptLogin(s []byte) (pbe PasswordAsn1) {
- _, err := asn1.Unmarshal(s, &pbe)
- if err != nil {
- log.Println(err)
- }
- return pbe
-}
diff --git a/core/output.go b/core/output.go
index b727c71..150d88b 100644
--- a/core/output.go
+++ b/core/output.go
@@ -101,7 +101,7 @@ func (b BrowserData) OutPutJson(dir, browser, format string) error {
enc.SetIndent("", "\t")
err = enc.Encode(b.CookieMap)
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
file.Write(w.Bytes())
fmt.Printf("%s Get %d cookies, filename is %s \n", log.Prefix, len(b.CookieMap), filename)
@@ -119,7 +119,7 @@ func (b BrowserData) OutPutJson(dir, browser, format string) error {
enc.SetIndent("", "\t")
err = enc.Encode(b.HistorySlice)
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
file.Write(w.Bytes())
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(b.HistorySlice), filename)
@@ -137,7 +137,7 @@ func (b BrowserData) OutPutJson(dir, browser, format string) error {
enc.SetIndent("", "\t")
err = enc.Encode(b.LoginDataSlice)
if err != nil {
- log.Println(err)
+ log.Debug(err)
}
file.Write(w.Bytes())
fmt.Printf("%s Get %d login data, filename is %s \n", log.Prefix, len(b.LoginDataSlice), filename)
diff --git a/utils/utils.go b/utils/utils.go
index 61dd3ee..5b1be48 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -1,6 +1,9 @@
package utils
import (
+ "crypto/cipher"
+ "crypto/des"
+ "encoding/asn1"
"errors"
"fmt"
"hack-browser-data/log"
@@ -84,11 +87,11 @@ func CopyDB(src, dst string) error {
}
sourceFile, err := ioutil.ReadFile(src)
if err != nil {
- log.Println(err.Error())
+ log.Debug(err.Error())
}
err = ioutil.WriteFile(dst, sourceFile, 0777)
if err != nil {
- log.Println(err.Error())
+ log.Debug(err.Error())
}
return err
}
@@ -164,7 +167,7 @@ func MakeDir(dirName string) {
}
}
-func paddingZero(s []byte, l int) []byte {
+func PaddingZero(s []byte, l int) []byte {
h := l - len(s)
if h <= 0 {
return s
@@ -180,4 +183,44 @@ func PKCS5UnPadding(src []byte) []byte {
length := len(src)
unpadding := int(src[length-1])
return src[:(length - unpadding)]
-}
\ No newline at end of file
+}
+
+func Des3Decrypt(key, iv []byte, src []byte) ([]byte, error) {
+ block, err := des.NewTripleDESCipher(key)
+ if err != nil {
+ log.Error(err)
+ return nil, err
+ }
+ blockMode := cipher.NewCBCDecrypter(block, iv)
+ sq := make([]byte, len(src))
+ blockMode.CryptBlocks(sq, src)
+ return sq, nil
+}
+
+/*
+SEQUENCE (3 elem)
+ OCTET STRING (16 byte)
+ SEQUENCE (2 elem)
+ OBJECT IDENTIFIER 1.2.840.113549.3.7 des-EDE3-CBC (RSADSI encryptionAlgorithm)
+ OCTET STRING (8 byte)
+ OCTET STRING (16 byte)
+*/
+type LoginPBE struct {
+ CipherText []byte
+ SequenceLogin
+ Encrypted []byte
+}
+
+type SequenceLogin struct {
+ asn1.ObjectIdentifier
+ Iv []byte
+}
+
+func DecodeLogin(decodeItem []byte) (pbe LoginPBE, err error) {
+ _, err = asn1.Unmarshal(decodeItem, &pbe)
+ if err != nil {
+ log.Error(err)
+ return
+ }
+ return pbe, nil
+}
diff --git a/utils/utils_darwin.go b/utils/utils_darwin.go
index 9755e97..8dba0fc 100644
--- a/utils/utils_darwin.go
+++ b/utils/utils_darwin.go
@@ -5,6 +5,7 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/sha1"
+ "encoding/asn1"
"errors"
"hack-browser-data/log"
"os/exec"
@@ -68,12 +69,12 @@ func InitKey(key string) error {
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
- log.Println(err)
+ log.Error(err)
return err
}
if stderr.Len() > 0 {
err = errors.New(stderr.String())
- log.Println(err)
+ log.Error(err)
}
temp := stdout.Bytes()
chromePass := temp[:len(temp)-1]
@@ -110,5 +111,36 @@ func aes128CBCDecrypt(encryptPass []byte) (string, error) {
return string(dst), nil
}
+/*
+SEQUENCE (2 elem)
+ SEQUENCE (2 elem)
+ OBJECT IDENTIFIER
+ SEQUENCE (2 elem)
+ OCTET STRING (20 byte)
+ INTEGER 1
+ OCTET STRING (16 byte)
+*/
+type MetaPBE struct {
+ SequenceA
+ Encrypted []byte
+}
+
+type SequenceA struct {
+ DecryptMethod asn1.ObjectIdentifier
+ SequenceB
+}
+
+type SequenceB struct {
+ EntrySalt []byte
+ Len int
+}
+func DecodeMeta(decodeItem []byte) (pbe MetaPBE, err error) {
+ _, err = asn1.Unmarshal(decodeItem, &pbe)
+ if err != nil {
+ log.Error(err)
+ return
+ }
+ return
+}