Low watch CVSS 4.3 chrome Policy Bypass 🔧 Commit mapped

Overview

Low
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in NFC
DescriptionInsufficient policy enforcement in NFC
ComponentNFC
Bug ClassPolicy Bypass
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker497337759
Fix commit9491c00b2c19 (chromium/src) +21/-2
CWECWE-346 (Origin validation error)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-03-29 (per vendor advisory)
Fix landed2026-06-10 (public git)
Disclosed2026-07-29
Patch gap49 days — fix public in source before the advisory

Background

Web NFC blocklist
A NfcBlocklist allowlist-denial layer that refuses WebNFC access to security-sensitive tags (e.g. YubiKeys / FIDO tokens) so a malicious page cannot silently interact with them.
`IsoDep.getHistoricalBytes()`
Android NFC API returning the ISO 14443 Type A historical bytes used to fingerprint a tag; returns null/empty for NFC-B (Type B) tags.
`IsoDep.getHiLayerResponse()`
Android NFC API returning the ISO 14443 Type B higher-layer response (ATTRIB) bytes, the correct identifier source for NFC-B tags.
NFC-B (ISO 14443 Type B)
A contactless card protocol variant that carries no historical bytes, so identity must be read from the hi-layer response instead.

Root Cause Analysis

NfcBlocklist.isTagBlocked fingerprinted every IsoDep tag solely via getHistoricalBytes() and passed the result to areHistoricalBytesBlocked(). This assumed all IsoDep tags expose historical bytes, but NFC-B (Type B) tags return null/empty there and instead carry their identifier in getHiLayerResponse(). A blocked device presenting over the NFC-B protocol therefore yielded null historical bytes, matched nothing in the blocklist, and was treated as an ordinary tag. The blocklist enforcement silently fell through to “allow,” defeating the security control for an entire class of tags.

The fix falls back to getHiLayerResponse() whenever historical bytes are null or empty, so NFC-B blocked tags are matched before access is granted.

Key insight
The identification method and the security decision were coupled to a single data source that does not exist for a whole tag class, so absence of an identifier was implicitly interpreted as “not blocked” rather than “unidentified.” A deny-list keyed on one protocol’s fingerprint silently fails open for any protocol variant it does not read.

Attack Path

  1. Present blocked device as NFC-B An attacker (or a lured victim tapping a token) exposes a blocklisted secure tag, e.g. a FIDO/YubiKey, over the ISO 14443 Type B protocol.
  2. Null historical bytes getHistoricalBytes() returns null/empty for the NFC-B tag, so areHistoricalBytesBlocked() finds no match.
  3. Blocklist fails open isTagBlocked returns not-blocked, so WebNFC treats the sensitive tag as permitted.
  4. Malicious page interacts A hostile web page reads from / writes to the security-sensitive tag via WebNFC, bypassing the protection intended to isolate it.

Impact Assessment

Insufficient policy enforcement: WebNFC’s blocklist could be bypassed for any security-sensitive tag reachable over the NFC-B protocol, exposing FIDO/YubiKey-class devices to interaction from untrusted web content. Exploitation requires physical proximity of the tag to the device and an NFC-B-capable target, which bounds practical reach. Within that constraint the impact is a full defeat of the blocklist for the affected tag class, potentially enabling probing or misuse of authentication tokens.

Changed Functions

FunctionChangeNotes
if
services/device/nfc/android/java/src/org/chromium/device/nfc/NfcBlocklist.java
modified

Files Changed

  • services/device/nfc/android/java/src/org/chromium/device/nfc/NfcBlocklist.java
  • services/device/nfc/android/junit/src/org/chromium/device/nfc/NfcBlocklistTest.java

Audit Directions

  • Identifier source assumptions
    Audit every security check keyed on one protocol-specific fingerprint (getHistoricalBytes, ATR, ATQA/SAK, UID) for tag/card classes where that source is absent, and confirm a missing identifier is treated as “unidentified,” not “safe.”
  • Fail-open deny-lists
    Review blocklist/allowlist enforcement where a null or empty lookup key short-circuits to permit; ensure unmatched or unreadable inputs default to the restrictive outcome.
  • NFC-B / protocol-variant coverage
    Check other NFC and hardware-token code paths that branch on Type A vs Type B (IsoDep, NfcA, NfcB) to verify both variants are fingerprinted and gated consistently.
From 9491c00b2c1910c2cafbbc25733ddbbc6507f62d Mon Sep 17 00:00:00 2001
From: Alvin Ji <[email protected]>
Date: Wed, 10 Jun 2026 11:44:07 -0700
Subject: [PATCH] nfc: Fix Web NFC Blocklist bypass for NFC-B tags

Consult getHiLayerResponse() if getHistoricalBytes() returns null or
empty, which is the case for NFC-B tags.

BUG=497337759

Change-Id: I1aa00301707679f1cf401ecd3b9568c5ab858ab9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7913540
Reviewed-by: Fr <[email protected]>
Commit-Queue: Alvin Ji <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1644747}
---

diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcBlocklist.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcBlocklist.java
index 165b21dc..f13e54ff 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcBlocklist.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcBlocklist.java
@@ -130,8 +130,11 @@
 
         IsoDep iso = IsoDep.get(tag);
         if (iso != null) {
-            byte[] historicalBytes = iso.getHistoricalBytes();
-            if (areHistoricalBytesBlocked(historicalBytes)) {
+            byte[] identifierBytes = iso.getHistoricalBytes();
+            if (identifierBytes == null || identifierBytes.length == 0) {
+                identifierBytes = iso.getHiLayerResponse();
+            }
+            if (areHistoricalBytesBlocked(identifierBytes)) {
                 Log.w(TAG, "Access to NFC tag is blocked.");
                 return true;
             }
diff --git a/services/device/nfc/android/junit/src/org/chromium/device/nfc/NfcBlocklistTest.java b/services/device/nfc/android/junit/src/org/chromium/device/nfc/NfcBlocklistTest.java
index 181f614..aeb7bb2 100644
--- a/services/device/nfc/android/junit/src/org/chromium/device/nfc/NfcBlocklistTest.java
+++ b/services/device/nfc/android/junit/src/org/chromium/device/nfc/NfcBlocklistTest.java
@@ -101,4 +101,20 @@
         assertFalse(areHistoricalBytesBlocked(new byte[] {}));
         assertFalse(areHistoricalBytesBlocked(new byte[] {0x01, 0x02, 0x03}));
     }
+
+    /**
+     * Verifies that null historical bytes are not blocked.
+     *
+     * <p>null is returned by IsoDep.getHistoricalBytes() for NFC-B tags, which are instead
+     * identified by their hi-layer response in NfcBlocklist.isTagBlocked().
+     */
+    @Test
+    @Feature({"NfcBlocklistTest"})
+    public void testNullHistoricalBytesAreNotBlocked() {
+        // Even with a server-pushed entry, null (the value every NFC-B ISO-DEP
+        // tag yields from getHistoricalBytes()) is never blocked.
+        NfcBlocklist.overrideNfcBlocklistForTests("8073c021c057597562694b6579");
+        assertFalse(areHistoricalBytesBlocked(null));
+        assertTrue(areHistoricalBytesBlocked(YUBIKEY_5_SERIES_HISTORICAL_BYTES));
+    }
 }
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.