CVE-2024-11696
Overview
Background
- `XPIDatabase.sys.mjs`
- The Firefox add-on manager module that persists installed extension metadata and performs runtime bookkeeping such as signature verification.
- `loadManifestFromFile`
- An
XPIInstallmethod that parses an extension’smanifest.jsonfrom its_sourceBundleon disk, returning add-on details or throwing when the manifest is invalid or unsupported. - `signedState`
- A per-add-on field (compared against
SIGNEDSTATE_SIGNED) recording the outcome of signature validation, which the manager uses to decide whether to keep an extension enabled. - `adminInstallOnly`
- A manifest-derived flag, gated behind
Services.policies, indicating an add-on that may only be installed by an administrator via enterprise policy.
Root Cause Analysis
During signature re-verification, the vulnerable code path in XPIDatabase unconditionally awaited XPIExports.XPIInstall.loadManifestFromFile to refresh the adminInstallOnly flag for a signed, policy-controlled add-on. Because an already-installed extension’s manifest.json can become invalid or unsupported over time — for example after backward-incompatible changes between Firefox versions — this call could throw, and the exception was never caught. The unhandled rejection aborted the enclosing verification loop before it finished, violating the invariant that every add-on’s signature is checked and its disabled state updated on each pass.
As a result, signature enforcement for unrelated add-ons queued after the failing one was skipped, so tampering by third-party applications could go undetected.
The fix wraps the loadManifestFromFile call and the subsequent adminInstallOnly assignment in a try/catch, logging a warning via logger.warn and allowing the loop to continue verifying signatures and updating disabled states for the remaining add-ons.
try/catch so a bad manifest degrades to a logged warning instead of derailing signature enforcement.Attack Path
- Plant a signed add-on with a broken manifest
A third-party application on the machine installs or tampers with a legitimately signed extension whose
manifest.jsonis invalid or unsupported, so thatloadManifestFromFilewillthrowwhen parsed. - Trigger verification
On the next signature re-verification pass,
XPIDatabaseiterates add-ons and reaches the vulnerable block for this signed, policy-eligible add-on. - Force an unhandled exception
The unguarded
await XPIExports.XPIInstall.loadManifestFromFile(...)rejects, and with notry/catchthe rejection propagates out of the loop. - Skip remaining checks The verification routine terminates early, so signature validation and disabled-state updates never run for the add-ons that would have been processed afterward.
- Retain a tampered extension An unrelated add-on that was silently modified by third-party software keeps running because its signature was never re-checked.
Impact Assessment
Files Changed
toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs
Audit Directions
- Unguarded awaits in verification loopsSearch security-critical iteration (signature, permission, or policy checks) for
awaited calls lackingtry/catch, where a single throwing element can abort processing of the rest. - Throwable manifest/parser reloadsAudit other callers of
loadManifestFromFileand similar disk-backed parsers for assumptions that stored, already-installed data is still valid, since format drift across versions can make previously good manifests throw. - Fail-open error handlingReview paths where an exception silently short-circuits an enforcement pass rather than isolating the failing item, ensuring failures degrade to a logged warning instead of skipping unrelated security checks.
Patch
diff --git a/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs b/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs
index 17219a88b42..b277e418618 100644
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs
@@ -2228,12 +2228,22 @@ export const XPIDatabase = {
addon.signedState === lazy.AddonManager.SIGNEDSTATE_SIGNED &&
Services.policies
) {
- const addonDetailsFromFile =
- await XPIExports.XPIInstall.loadManifestFromFile(
- addon._sourceBundle,
- addon.location
- );
- addon.adminInstallOnly = addonDetailsFromFile.adminInstallOnly;
+ // Manifest file for an installed extension can still become
+ // invalid (e.g. due to backward incompatible changes between
+ // Firefox versions).
+ try {
+ const addonDetailsFromFile =
+ await XPIExports.XPIInstall.loadManifestFromFile(
+ addon._sourceBundle,
+ addon.location
+ );
+ addon.adminInstallOnly = addonDetailsFromFile.adminInstallOnly;
+ } catch (err) {
+ // Simply log the error as a warning to be able to check
+ // the signature and potentially update the disabled state
+ // accordingly.
+ logger.warn(`XPI_verifySignature Warning on '${addon.id}': ${err}`);
+ }
}
if (