moonD4rk/HackBrowserData · error

abe: unexpected key length %d (want 32)

Error message

abe: unexpected key length %d (want 32)

What it means

RetrieveKey in masterkey/abe_windows.go obtains the Chrome app-bound encryption (ABE) master key by reflectively injecting a payload into the browser process. After injection, the payload is expected to return the 32-byte AES-256 master key. This error means the injection channel returned a byte slice whose length is not 32, so the payload output is not a valid key and cannot be used for decryption.

Source

Thrown at masterkey/abe_windows.go:64

		return nil, fmt.Errorf("abe: %w", err)
	}

	exePath, err := winutil.ExecutablePath(browserKey)
	if err != nil {
		return nil, fmt.Errorf("abe: %w", err)
	}

	env := map[string]string{
		envEncKeyB64: base64.StdEncoding.EncodeToString(encKey),
	}

	inj := &injector.Reflective{}
	key, err := inj.Inject(exePath, pl, env)
	if err != nil {
		return nil, fmt.Errorf("abe: inject into %s: %w", exePath, err)
	}
	if len(key) != 32 {
		return nil, fmt.Errorf("abe: unexpected key length %d (want 32)", len(key))
	}
	log.Infof("abe: retrieved %s master key via reflective injection", browserKey)
	return key, nil
}

func loadEncryptedKey(localStatePath string) ([]byte, error) {
	if localStatePath == "" {
		return nil, errNoABEKey
	}
	data, err := os.ReadFile(localStatePath)
	if err != nil {
		return nil, fmt.Errorf("abe: read Local State: %w", err)
	}

	raw := gjson.GetBytes(data, "os_crypt.app_bound_encrypted_key")
	if !raw.Exists() {
		return nil, errNoABEKey
	}

View on GitHub (pinned to 0503d04d7a)

Solutions

  1. Rebuild and re-embed the ABE payload so it matches the current bootstrap layout (make payload && make build-windows; run make gen-layout if bootstrap_layout.h changed).
  2. Log len(key) and a hex dump of the returned bytes to identify what the payload actually returned (error text vs truncated key).
  3. Verify the injected payload completed successfully and that Chrome's version matches the layout the payload was built against.
  4. Check that AV/EDR is not stripping or corrupting the returned buffer and that the payload DLL was injected into the real chrome.exe.

Example fix

// before
key, err := inj.Inject(exePath, pl, env)
if err != nil {
	return nil, fmt.Errorf("abe: inject into %s: %w", exePath, err)
}
// after - surface the payload's raw output for diagnosis
key, err := inj.Inject(exePath, pl, env)
if err != nil {
	return nil, fmt.Errorf("abe: inject into %s: %w", exePath, err)
}
if len(key) != 32 {
	return nil, fmt.Errorf("abe: unexpected key length %d (want 32): got %x", len(key), key)
}
Defensive patterns

Strategy: validation

Validate before calling

if len(key) != 32 {
	return fmt.Errorf("payload returned %d bytes, not a 32-byte key; rebuild payload for this Chrome version", len(key))
}

Try / catch

key, err := RetrieveKey(exePath, localStatePath)
if err != nil {
	var keyLenErr *KeyLengthError
	if errors.As(err, &keyLenErr) {
		log.Warnf("ABE payload mismatch: %v; falling back to DPAPI path", err)
		return fallbackDecrypt()
	}
	return err
}

Prevention

When it happens

Trigger: Calling RetrieveKey when the injected payload returns malformed output: the DLL produced truncated/corrupted data, an error string was captured instead of key bytes, or a payload/Chrome version mismatch caused the bootstrap to hand back something other than the raw 32-byte key.

Common situations: Chrome updated its ABE bootstrap layout so the payload returns a different struct; a stale or mismatched abe payload binary; the target process is not the expected Chrome binary; sandbox/AV interference caused partial copy-back of the key buffer.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of moonD4rk/HackBrowserData@0503d04d7a (2026-09-06). Data as JSON: /api/errors/002a84ea1e45ad9a. Report an issue: GitHub.