fix: retrieve correct ABE master key when browser is running (#577)

* fix(windows): retrieve correct ABE master key when browser is running
This commit is contained in:
Roger
2026-04-19 20:07:51 +08:00
committed by GitHub
parent ae1ec66ccb
commit e50c623db0
7 changed files with 105 additions and 57 deletions
+8 -1
View File
@@ -26,12 +26,19 @@ var errNoABEKey = errors.New("abe: Local State has no app_bound_encrypted_key")
type ABERetriever struct{}
func (r *ABERetriever) RetrieveKey(storage, localStatePath string) ([]byte, error) {
// Non-ABE Chromium forks (Opera/Vivaldi/Yandex/...) call this with an empty storage key; pre-v20
// Chrome profiles have no app_bound_encrypted_key in Local State. Both are "ABE not applicable" —
// return (nil, nil) so ChainRetriever falls through to DPAPI silently instead of emitting a Warnf
// for every non-ABE browser.
browserKey := strings.TrimSpace(storage)
if browserKey == "" {
return nil, fmt.Errorf("abe: empty browser key in storage parameter")
return nil, nil
}
encKey, err := loadEncryptedKey(localStatePath)
if errors.Is(err, errNoABEKey) {
return nil, nil
}
if err != nil {
return nil, err
}
+17 -15
View File
@@ -1,26 +1,27 @@
// Package keyretriever owns the master-key acquisition chain shared by all
// Chromium variants (Chrome, Edge, Brave, Arc, Opera, Vivaldi, Yandex, …).
// The chain is built once per process and reused for every profile.
// Package keyretriever owns the master-key acquisition chain shared by all Chromium variants (Chrome,
// Edge, Brave, Arc, Opera, Vivaldi, Yandex, …). The chain is built once per process and reused for
// every profile.
//
// Firefox and Safari do not route through this package — Firefox derives
// its own keys from key4.db via NSS PBE, and Safari reads InternetPassword
// records directly from login.keychain-db. Each browser package owns its
// own credential-acquisition strategy; see rfcs/006-key-retrieval-mechanisms.md
// §7 for the rationale.
// Firefox and Safari do not route through this package — Firefox derives its own keys from key4.db via
// NSS PBE, and Safari reads InternetPassword records directly from login.keychain-db. Each browser
// package owns its own credential-acquisition strategy; see rfcs/006-key-retrieval-mechanisms.md §7 for
// the rationale.
package keyretriever
import (
"errors"
"fmt"
"github.com/moond4rk/hackbrowserdata/log"
)
// errStorageNotFound is returned when the requested browser storage
// account is not found in the credential store (keychain, keyring, etc.).
// Only used on darwin and linux; Windows uses DPAPI which has no storage lookup.
// errStorageNotFound is returned when the requested browser storage account is not found in the
// credential store (keychain, keyring, etc.). Only used on darwin and linux; Windows uses DPAPI which
// has no storage lookup.
var errStorageNotFound = errors.New("not found in credential store") //nolint:unused // only used on darwin and linux
// KeyRetriever retrieves the master encryption key for a Chromium-based browser.
// Each platform has different implementations:
// KeyRetriever retrieves the master encryption key for a Chromium-based browser. Each platform has
// different implementations:
// - macOS: Keychain access (security command) or gcoredump exploit
// - Windows: DPAPI decryption of Local State file
// - Linux: D-Bus Secret Service or fallback to "peanuts" password
@@ -28,8 +29,8 @@ type KeyRetriever interface {
RetrieveKey(storage, localStatePath string) ([]byte, error)
}
// ChainRetriever tries multiple retrievers in order, returning the first success.
// Used on macOS (gcoredump → password → security) and Linux (D-Bus → peanuts).
// ChainRetriever tries multiple retrievers in order, returning the first success. Used on macOS
// (gcoredump → password → security) and Linux (D-Bus → peanuts).
type ChainRetriever struct {
retrievers []KeyRetriever
}
@@ -47,6 +48,7 @@ func (c *ChainRetriever) RetrieveKey(storage, localStatePath string) ([]byte, er
return key, nil
}
if err != nil {
log.Warnf("keyretriever %T failed: %v", r, err)
errs = append(errs, fmt.Errorf("%T: %w", r, err))
}
}