From b806827f6c1e4642711e1a4f720e20761e5ebd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=B4=8D=E1=B4=8F=E1=B4=8F=C9=B4D4=CA=80=E1=B4=8B?= Date: Sat, 25 Jul 2020 22:17:50 +0800 Subject: [PATCH] feat: decrypt chrome for linux password with dbus Close #4 --- README.md | 4 +- README_ZH.md | 2 +- cmd/cmd.go | 3 +- core/browser.go | 1 + core/browser_darwin.go | 7 ++- core/browser_linux.go | 77 +++++++++++++++++++++++---------- core/common/parse.go | 17 +------- core/decrypt/decrypt.go | 7 +-- core/decrypt/decrypt_darwin.go | 2 +- core/decrypt/decrypt_linux.go | 10 ++--- core/decrypt/decrypt_windows.go | 8 ++-- go.mod | 2 + go.sum | 4 ++ 13 files changed, 86 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 3740940..bdaf863 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [中文文档](https://github.com/moonD4rk/HackBrowserData/blob/master/README_ZH.md) -hack-browser-data is an open-source tool that could help you export data from browser. It supports the most popular browsers on the market and runs on Windows, macOS and Linux. +hack-browser-data is an open-source tool that could help you decrypt data[passwords|bookmarks|cookies|history] from the browser. It supports the most popular browsers on the market and runs on Windows, macOS and Linux. ### Supported Browser @@ -58,7 +58,7 @@ go build ```shell PS C:\hack> .\hack.exe -h NAME: - hack-browser-data - Export passwords/cookies/history/bookmarks from browser + hack-browser-data - Decrypt passwords/cookies/history/bookmarks from browser USAGE: [hack-browser-data -b chrome -f json -dir results -e all] diff --git a/README_ZH.md b/README_ZH.md index 19a14b2..33f003c 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -2,7 +2,7 @@ [中文文档](https://github.com/moonD4rk/HackBrowserData/blob/master/README_ZH.md) -hack-browser-data 是一个浏览器数据(密码|历史记录|Cookies|书签)导出工具,支持全平台主流浏览器。 +hack-browser-data 是一个解密浏览器数据(密码|历史记录|Cookies|书签)的导出工具,支持全平台主流浏览器。 ### 各平台浏览器支持情况 diff --git a/cmd/cmd.go b/cmd/cmd.go index e10ae2d..49b2619 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -23,7 +23,7 @@ func Execute() { Name: "hack-browser-data", Usage: "Export passwords/cookies/history/bookmarks from browser", UsageText: "[hack-browser-data -b chrome -f json -dir results -e all]\n Get all data(password/cookie/history/bookmark) from chrome", - Version: "0.1.7", + Version: "0.1.8", Flags: []cli.Flag{ &cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "Verbose"}, &cli.StringFlag{Name: "browser", Aliases: []string{"b"}, Destination: &browser, Value: "all", Usage: "Available browsers: all|" + strings.Join(core.ListBrowser(), "|")}, @@ -63,6 +63,5 @@ func Execute() { err := app.Run(os.Args) if err != nil { panic(err) - return } } diff --git a/core/browser.go b/core/browser.go index e9b76f0..5db693d 100644 --- a/core/browser.go +++ b/core/browser.go @@ -59,6 +59,7 @@ const ( var ( ErrDataNotSupported = errors.New(`not supported, default is "all", choose from history|password|bookmark|cookie`) ErrBrowserNotSupported = errors.New("browser not supported") + ErrChromeSecretIsEmpty = errors.New("chrome secret is empty") chromiumParseList = map[string]FileList{ cookie: { name: cookie, diff --git a/core/browser_darwin.go b/core/browser_darwin.go index 88c7f24..6273eef 100644 --- a/core/browser_darwin.go +++ b/core/browser_darwin.go @@ -60,10 +60,13 @@ func (c *chromium) InitSecretKey() error { log.Error(err) } temp := stdout.Bytes() - chromePass := temp[:len(temp)-1] + chromeSecret := temp[:len(temp)-1] + if chromeSecret == nil { + return ErrChromeSecretIsEmpty + } var chromeSalt = []byte("saltysalt") // @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_mac.mm;l=157 - key := pbkdf2.Key(chromePass, chromeSalt, 1003, 16, sha1.New) + key := pbkdf2.Key(chromeSecret, chromeSalt, 1003, 16, sha1.New) c.SecretKey = key return err } diff --git a/core/browser_linux.go b/core/browser_linux.go index 9453d04..38990a1 100644 --- a/core/browser_linux.go +++ b/core/browser_linux.go @@ -1,18 +1,17 @@ package core import ( - "bytes" "crypto/sha1" - "errors" + "github.com/godbus/dbus/v5" + keyring "github.com/ppacher/go-dbus-keyring" "hack-browser-data/log" - "os/exec" "golang.org/x/crypto/pbkdf2" ) const ( fireFoxProfilePath = "/home/*/.mozilla/firefox/*.default-release/" - fireFoxCommand = "" + chromeProfilePath = "/home/*/.config/google-chrome/*/" ) var ( @@ -24,35 +23,67 @@ var ( }{ "firefox": { ProfilePath: fireFoxProfilePath, - Name: fireFoxCommand, + Name: firefoxName, New: decryptFirefox, }, + "chrome": { + ProfilePath: chromeProfilePath, + Name: chromeName, + New: decryptChromium, + }, } ) func (c *chromium) InitSecretKey() error { - var ( - cmd *exec.Cmd - stdout, stderr bytes.Buffer - ) - //➜ security find-generic-password -wa 'Chrome' - cmd = exec.Command("security", "find-generic-password", "-wa", c.Name) - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() + //what is d-bus @https://dbus.freedesktop.org/ + var chromeSecret []byte + conn, err := dbus.SessionBus() + if err != nil { + return err + } + svc, err := keyring.GetSecretService(conn) if err != nil { - log.Error(err) return err } - if stderr.Len() > 0 { - err = errors.New(stderr.String()) - log.Error(err) + session, err := svc.OpenSession() + if err != nil { + return err + } + defer func() { + if err = session.Close(); err != nil { + log.Error(err) + } + }() + collections, err := svc.GetAllCollections() + if err != nil { + return err + } + for _, col := range collections { + items, err := col.GetAllItems() + if err != nil { + return err + } + for _, item := range items { + i, err := item.GetLabel() + if err != nil { + log.Error(err) + continue + } + if i == "Chrome Safe Storage" { + se, err := item.GetSecret(session.Path()) + if err != nil { + return err + } + chromeSecret = se.Value + } + } } - temp := stdout.Bytes() - chromePass := temp[:len(temp)-1] var chromeSalt = []byte("saltysalt") - // @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_mac.mm;l=157 - key := pbkdf2.Key(chromePass, chromeSalt, 1003, 16, sha1.New) + if chromeSecret == nil { + return ErrChromeSecretIsEmpty + } + // @https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_linux.cc + key := pbkdf2.Key(chromeSecret, chromeSalt, 1, 16, sha1.New) c.SecretKey = key - return err + return nil } diff --git a/core/common/parse.go b/core/common/parse.go index 2c89874..ea8e266 100644 --- a/core/common/parse.go +++ b/core/common/parse.go @@ -136,7 +136,6 @@ func (l *Logins) ChromeParse(key []byte) error { log.Debug(err) } }() - err = loginDB.Ping() rows, err := loginDB.Query(queryChromiumLogin) defer func() { if err := rows.Close(); err != nil { @@ -206,7 +205,6 @@ func (h *History) ChromeParse(key []byte) error { log.Error(err) } }() - err = historyDB.Ping() rows, err := historyDB.Query(queryChromiumHistory) defer func() { if err := rows.Close(); err != nil { @@ -247,7 +245,6 @@ func (c *Cookies) ChromeParse(secretKey []byte) error { log.Debug(err) } }() - err = cookieDB.Ping() rows, err := cookieDB.Query(queryChromiumCookie) defer func() { if err := rows.Close(); err != nil { @@ -282,11 +279,7 @@ func (c *Cookies) ChromeParse(secretKey []byte) error { } cookie.Value = string(value) - if _, ok := c.cookies[host]; ok { - c.cookies[host] = append(c.cookies[host], cookie) - } else { - c.cookies[host] = []cookies{cookie} - } + c.cookies[host] = append(c.cookies[host], cookie) } return nil } @@ -412,7 +405,6 @@ func (c *Cookies) FirefoxParse() error { log.Debug(err) } }() - err = cookieDB.Ping() rows, err := cookieDB.Query(queryFirefoxCookie) if err != nil { log.Error(err) @@ -441,11 +433,7 @@ func (c *Cookies) FirefoxParse() error { } cookie.Value = value - if _, ok := c.cookies[host]; ok { - c.cookies[host] = append(c.cookies[host], cookie) - } else { - c.cookies[host] = []cookies{cookie} - } + c.cookies[host] = append(c.cookies[host], cookie) } return nil } @@ -533,7 +521,6 @@ func getDecryptKey() (item1, item2, a11, a102 []byte, err error) { } }() - err = keyDB.Ping() pwdRows, err = keyDB.Query(queryMetaData) defer func() { if err := pwdRows.Close(); err != nil { diff --git a/core/decrypt/decrypt.go b/core/decrypt/decrypt.go index 7994cdd..ced96b3 100644 --- a/core/decrypt/decrypt.go +++ b/core/decrypt/decrypt.go @@ -10,9 +10,10 @@ import ( ) var ( - errKeyIsEmpty = errors.New("input [security find-generic-password -wa 'Chrome'] in terminal") - errPasswordIsEmpty = errors.New("password is empty") - errDecryptFailed = errors.New("decrypt failed, password is empty") + errSecurityKeyIsEmpty = errors.New("input [security find-generic-password -wa 'Chrome'] in terminal") + errPasswordIsEmpty = errors.New("password is empty") + errDecryptFailed = errors.New("decrypt failed, password is empty") + errDbusSecretIsEmpty = errors.New("dbus secret key is empty") ) func aes128CBCDecrypt(key, iv, encryptPass []byte) ([]byte, error) { diff --git a/core/decrypt/decrypt_darwin.go b/core/decrypt/decrypt_darwin.go index 26d8b11..1a6751b 100644 --- a/core/decrypt/decrypt_darwin.go +++ b/core/decrypt/decrypt_darwin.go @@ -14,7 +14,7 @@ var ( func ChromePass(key, encryptPass []byte) ([]byte, error) { if len(encryptPass) > 3 { if len(key) == 0 { - return nil, errKeyIsEmpty + return nil, errSecurityKeyIsEmpty } m, err := aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) return m, err diff --git a/core/decrypt/decrypt_linux.go b/core/decrypt/decrypt_linux.go index 24dee6c..24ebd83 100644 --- a/core/decrypt/decrypt_linux.go +++ b/core/decrypt/decrypt_linux.go @@ -18,7 +18,7 @@ var ( func ChromePass(key, encryptPass []byte) ([]byte, error) { if len(encryptPass) > 3 { if len(key) == 0 { - return nil, errKeyIsEmpty + return nil, errSecurityKeyIsEmpty } m, err := aes128CBCDecrypt(key, chromeIV, encryptPass[3:]) return m, err @@ -151,18 +151,18 @@ func decryptMeta(globalSalt, masterPwd, entrySalt, encrypted []byte) ([]byte, er func decryptNss(globalSalt, masterPwd, nssIv, entrySalt, encrypted []byte, iter, keySize int) ([]byte, error) { k := sha1.Sum(globalSalt) - log.Println(hex.EncodeToString(k[:])) + log.Debug(hex.EncodeToString(k[:])) key := pbkdf2.Key(k[:], entrySalt, iter, keySize, sha256.New) - log.Println(hex.EncodeToString(key)) + log.Debug(hex.EncodeToString(key)) i, err := hex.DecodeString("040e") if err != nil { - log.Println(err) + log.Debug(err) } // @https://hg.mozilla.org/projects/nss/rev/fc636973ad06392d11597620b602779b4af312f6#l6.49 iv := append(i, nssIv...) dst, err := aes128CBCDecrypt(key, iv, encrypted) if err != nil { - log.Println(err) + log.Debug(err) } return dst, err } diff --git a/core/decrypt/decrypt_windows.go b/core/decrypt/decrypt_windows.go index e0aabf1..db4d309 100644 --- a/core/decrypt/decrypt_windows.go +++ b/core/decrypt/decrypt_windows.go @@ -158,18 +158,18 @@ func Nss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) { func decryptMeta(globalSalt, masterPwd, nssIv, entrySalt, encrypted []byte, iter, keySize int) ([]byte, error) { k := sha1.Sum(globalSalt) - log.Println(hex.EncodeToString(k[:])) + log.Debug(hex.EncodeToString(k[:])) key := pbkdf2.Key(k[:], entrySalt, iter, keySize, sha256.New) - log.Println(hex.EncodeToString(key)) + log.Debug(hex.EncodeToString(key)) i, err := hex.DecodeString("040e") if err != nil { - log.Println(err) + log.Debug(err) } // @https://hg.mozilla.org/projects/nss/rev/fc636973ad06392d11597620b602779b4af312f6#l6.49 iv := append(i, nssIv...) dst, err := aes128CBCDecrypt(key, iv, encrypted) if err != nil { - log.Println(err) + log.Debug(err) } return dst, err } diff --git a/go.mod b/go.mod index 8a0f509..b8aa917 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,10 @@ module hack-browser-data go 1.14 require ( + github.com/godbus/dbus/v5 v5.0.3 github.com/jszwec/csvutil v1.3.0 github.com/mattn/go-sqlite3 v1.14.0 + github.com/ppacher/go-dbus-keyring v1.0.1 github.com/stretchr/testify v1.6.1 // indirect github.com/tidwall/gjson v1.6.0 github.com/urfave/cli/v2 v2.2.0 diff --git a/go.sum b/go.sum index 6c2b8dc..e34291b 100644 --- a/go.sum +++ b/go.sum @@ -6,12 +6,16 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/jszwec/csvutil v1.3.0 h1:d0zzXKQYvc22b4La5Wcp97CDgQ7JDLGJLm2NWqJGEYg= github.com/jszwec/csvutil v1.3.0/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg= github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/ppacher/go-dbus-keyring v1.0.1 h1:dM4dMfP5w9MxY+foFHCQiN7izEGpFdKr3tZeMGmvqD0= +github.com/ppacher/go-dbus-keyring v1.0.1/go.mod h1:JEmkRwBVPBFkOHedAsoZALWmhNJxR/R/ykkFpbEHtGE= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=