mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-05-19 18:58:03 +02:00
feat: refactor firefox get master key retrieval and decryption functions. (#300)
* refactor: Simplify Firefox master key retrieval and decryption functions. - Simplify variable names and functions in browsingdata/password/password.go - Modify Decrypt function for each PBE type to have only one parameter named globalSalt in crypto/crypto.go - Implement functions to retrieve master key from Firefox's key4.db file and query metadata and private NSS data in browser/firefox/firefox.go * chore: Add dependencies and tests to Firefox package. - Add go-sqlmock and github.com/kisielk/sqlstruct dependencies - Add tests for Firefox package metadata and nssPrivate query - Add test for Firefox's processMasterKey function (currently commented out) * refactor: Refactor Firefox test functions, remove unused code - Remove unused test function in firefox_test.go file - Clean up code by removing unnecessary changes - Simplify file structure for easier maintenance and readability
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
package firefox
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
_ "modernc.org/sqlite" // sqlite3 driver TODO: replace with chooseable driver
|
||||
|
||||
"github.com/moond4rk/hackbrowserdata/browsingdata"
|
||||
"github.com/moond4rk/hackbrowserdata/crypto"
|
||||
"github.com/moond4rk/hackbrowserdata/item"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/fileutil"
|
||||
"github.com/moond4rk/hackbrowserdata/utils/typeutil"
|
||||
@@ -68,8 +74,82 @@ func firefoxWalkFunc(items []item.Item, multiItemPaths map[string]map[item.Item]
|
||||
}
|
||||
}
|
||||
|
||||
// GetMasterKey returns master key of Firefox. from key4.db
|
||||
func (f *Firefox) GetMasterKey() ([]byte, error) {
|
||||
return f.masterKey, nil
|
||||
tempFilename := item.FirefoxKey4.TempFilename()
|
||||
|
||||
// Open and defer close of the database.
|
||||
keyDB, err := sql.Open("sqlite", tempFilename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open key4.db error: %w", err)
|
||||
}
|
||||
defer os.Remove(tempFilename)
|
||||
defer keyDB.Close()
|
||||
|
||||
globalSalt, metaBytes, err := queryMetaData(keyDB)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query metadata error: %w", err)
|
||||
}
|
||||
|
||||
nssA11, nssA102, err := queryNssPrivate(keyDB)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query NSS private error: %w", err)
|
||||
}
|
||||
|
||||
return processMasterKey(globalSalt, metaBytes, nssA11, nssA102)
|
||||
}
|
||||
|
||||
func queryMetaData(db *sql.DB) ([]byte, []byte, error) {
|
||||
const query = `SELECT item1, item2 FROM metaData WHERE id = 'password'`
|
||||
var globalSalt, metaBytes []byte
|
||||
if err := db.QueryRow(query).Scan(&globalSalt, &metaBytes); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return globalSalt, metaBytes, nil
|
||||
}
|
||||
|
||||
func queryNssPrivate(db *sql.DB) ([]byte, []byte, error) {
|
||||
const query = `SELECT a11, a102 from nssPrivate`
|
||||
var nssA11, nssA102 []byte
|
||||
if err := db.QueryRow(query).Scan(&nssA11, &nssA102); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nssA11, nssA102, nil
|
||||
}
|
||||
|
||||
// processMasterKey process master key of Firefox.
|
||||
// Process the metaBytes and nssA11 with the corresponding cryptographic operations.
|
||||
func processMasterKey(globalSalt, metaBytes, nssA11, nssA102 []byte) ([]byte, error) {
|
||||
metaPBE, err := crypto.NewASN1PBE(metaBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k, err := metaPBE.Decrypt(globalSalt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !bytes.Contains(k, []byte("password-check")) {
|
||||
return nil, errors.New("password-check not found")
|
||||
}
|
||||
keyLin := []byte{248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
||||
if !bytes.Equal(nssA102, keyLin) {
|
||||
return nil, errors.New("nssA102 not equal keyLin")
|
||||
}
|
||||
nssPBE, err := crypto.NewASN1PBE(nssA11)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
finallyKey, err := nssPBE.Decrypt(globalSalt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(finallyKey) < 24 {
|
||||
return nil, errors.New("finallyKey length less than 24")
|
||||
}
|
||||
finallyKey = finallyKey[:24]
|
||||
return finallyKey, nil
|
||||
}
|
||||
|
||||
func (f *Firefox) Name() string {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package firefox
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestQueryMetaData(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
assert.NoError(t, err)
|
||||
defer db.Close()
|
||||
|
||||
rows := sqlmock.NewRows([]string{"item1", "item2"}).
|
||||
AddRow([]byte("globalSalt"), []byte("metaBytes"))
|
||||
mock.ExpectQuery("SELECT item1, item2 FROM metaData WHERE id = 'password'").WillReturnRows(rows)
|
||||
|
||||
globalSalt, metaBytes, err := queryMetaData(db)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("globalSalt"), globalSalt)
|
||||
assert.Equal(t, []byte("metaBytes"), metaBytes)
|
||||
}
|
||||
|
||||
func TestQueryNssPrivate(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
assert.NoError(t, err)
|
||||
defer db.Close()
|
||||
|
||||
rows := sqlmock.NewRows([]string{"a11", "a102"}).
|
||||
AddRow([]byte("nssA11"), []byte("nssA102"))
|
||||
mock.ExpectQuery("SELECT a11, a102 from nssPrivate").WillReturnRows(rows)
|
||||
|
||||
nssA11, nssA102, err := queryNssPrivate(db)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("nssA11"), nssA11)
|
||||
assert.Equal(t, []byte("nssA102"), nssA102)
|
||||
}
|
||||
Reference in New Issue
Block a user