CVE-2026-3925
Overview
Background
- Ligatures (`liga`/`clig`)
- OpenType font features that draw certain character sequences as one combined glyph (standard and contextual ligatures), so what is displayed can differ from the underlying characters.
- `setFontFeatureSettings`
- the Android
TextViewAPI that turns OpenType features on or off; the patch passesNO_LIGATURES("\"liga\" 0, \"clig\" 0") to turn both ligature features off. - `PermissionDialogCoordinator`
- the Android Java class that builds and shows modal site-permission dialogs, which include text identifying the requesting site.
- Lookalike / incorrect security UI
- a class of bug where security-relevant text, such as the origin in a permission prompt, can be made to look like a different, trusted value.
Root Cause Analysis
Android permission dialogs rendered their text with the font’s default ligatures turned on.
As a result, certain character sequences in dialog text, including the requesting origin (inferred), could be drawn as glyphs that read differently from the actual characters, which makes a lookalike origin more convincing. The commit says a matching mitigation was earlier applied to the omnibox (crrev.com/c/7199504), so permission dialogs had not received the same rendering hardening as the URL bar.
The fix adds UiUtils.disableLigaturesForSecurity(View), which walks a view tree recursively and calls setFontFeatureSettings(NO_LIGATURES) on every TextView. PermissionDialogCoordinator calls it on the dialog’s root view (rootView = dialogView) once, and again in the onGlobalLayout callback so that views added or re-laid-out later are covered. The diff does not show which specific ligature sequences produce a useful spoof.
liga and clig on every TextView in the dialog, matching the earlier omnibox mitigation.Attack Path
- Register a crafted domain the attacker uses a hostname whose characters form ligatures in the system font that make it look like a different, trusted name (inferred: the diff does not name any sequence or font).
- Request a permission a page on that origin calls a permission-gated web API, so Chrome on Android shows a modal permission dialog that includes the site’s identity (inferred: the request entry point is not in the diff).
- Misleading rendering
before the patch, the dialog’s
TextViews rendered with default font features, so the ligatures changed how the origin looked. - User grants the permission the user, who sees what looks like a trusted origin, grants the attacker’s site access to the requested capability (inferred).
Impact Assessment
components/permissions/android and ui/android), a user who visits the attacker’s site, and a user decision to grant the permission. It does not involve memory corruption, and the advisory text supplied here does not report in-the-wild exploitation.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/android/java/src/org/chromium/ui/UiUtils.java |
modified |
Files Changed
components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogCoordinator.javaui/android/java/src/org/chromium/ui/UiUtils.java
Audit Directions
- Other surfaces that display originscheck that every Android (and desktop) UI showing a host or origin, such as page info, download prompts, external-protocol dialogs, notification and PWA install prompts, gets the same
disableLigaturesForSecuritytreatment as the omnibox and permission dialogs. - Early returns that skip the mitigationverify that the hardening is applied on every display path, for example the branch that returns after
recordOutOfScreenNegativeButton, and that views inflated or replaced later are re-covered. - Other rendering transformsreview security-UI text for other font or shaping features (contextual alternates, bidi reordering, combining marks) that can make displayed text differ from the underlying characters.
Patch
From 19ca206a62d08ed290bcda7f5974b1cdb6a765c1 Mon Sep 17 00:00:00 2001 From: Mustafa Emre Acer <[email protected]> Date: Mon, 02 Feb 2026 14:19:58 -0800 Subject: [PATCH] Disable ligatures in permission dialogs on Android This change disables rendering of ligatures in permission dialogs for security and readability. A similar change was previously made for the omnibox in crrev.com/c/7199504. Bug: 418214610 Change-Id: I4f0e61d87ea50304ef0909f939908393a81d9e78 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7536052 Reviewed-by: Sinan Sahin <[email protected]> Commit-Queue: Mustafa Emre Acer <[email protected]> Reviewed-by: Elias Klim <[email protected]> Cr-Commit-Position: refs/heads/main@{#1578393} --- diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogCoordinator.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogCoordinator.java index bd4876d..08329d0b 100644 --- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogCoordinator.java +++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionDialogCoordinator.java @@ -23,6 +23,7 @@ import org.chromium.components.browser_ui.util.DimensionCompat; import org.chromium.components.content_settings.ContentSetting; import org.chromium.ui.LayoutInflaterUtils; +import org.chromium.ui.UiUtils; import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogManagerObserver; import org.chromium.ui.modaldialog.ModalDialogProperties; @@ -76,12 +77,16 @@ recordOutOfScreenNegativeButton(lastButton); return; } + final View rootView = dialogView; + UiUtils.disableLigaturesForSecurity(rootView); + lastButton .getViewTreeObserver() .addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { + UiUtils.disableLigaturesForSecurity(rootView); if (!lastButton.isLaidOut()) { return; } diff --git a/ui/android/java/src/org/chromium/ui/UiUtils.java b/ui/android/java/src/org/chromium/ui/UiUtils.java index c27408c..17795258 100644 --- a/ui/android/java/src/org/chromium/ui/UiUtils.java +++ b/ui/android/java/src/org/chromium/ui/UiUtils.java @@ -77,10 +77,30 @@ // this long after the prompt is displayed. public static long PROMPT_INPUT_PROTECTION_SHORT_DELAY_MS = 600; + // Font feature setting to disable ligature rendering. + private static final String NO_LIGATURES = "\"liga\" 0, \"clig\" 0"; + /** Guards this class from being instantiated. */ private UiUtils() {} /** + * Recursively walks the view tree and disables ligatures on all TextViews + * + * @param view The root view{@link View} + */ + public static void disableLigaturesForSecurity(View view) { + if (view instanceof TextView) { + ((TextView) view).setFontFeatureSettings(NO_LIGATURES); + } + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + for (int i = 0; i < group.getChildCount(); i++) { + disableLigaturesForSecurity(group.getChildAt(i)); + } + } + } + + /** * Gets the set of locales supported by the current enabled Input Methods. * * @param context A {@link Context} instance.
Original Bug Report
Security: Permission prompt spoofs with Google Sans font ligatures (similar to issue 391788835)
SUMMARY
Similar to issue 391788835, the Google Sans font ligatures can be used to spoof origin in various permission prompts.
VULNERABILITY DETAILS
While the fix for issue 391788835 (https://crrev.com/c/6227546) shows an interstitial for top-level navigations if the domain contains a blocked ligature, iframes are allowed to navigate to URLs with domains containing these ligatures.
An iframe at https://googlelogoligature.com can open a new window to about:blank which will still be in the origin with ligatures. The opener iframe can then request permissions using the origin with ligatures. All the permission UIs we tested don’t seem to sanitize these ligatures.
Affected permission prompts we’ve tested:
- Camera
- Microphone
- Location
- Notification
- Read clipboard
- Bluetooth
- USB
- Contacts
Probably all other permission prompts are affected.
VERSION
Verified repro on these versions:
Chrome version: 136.0.7103.87 Stable, 137.0.7151.23 Beta, 138.0.7178.0 Dev, 138.0.7180.0 Canary
Operating System: Android 14, Android 15
REPRODUCTION CASE
Setup:
- Make your Android device resolve
googlelogoligature.comto your malicious server that hosts a downloadable file. In my case, my router lets me override DNS entries so it’s easy to test on physical device. For emulated devices, not sure if host’s DNS resolution would affect the emulated devices. - Navigate once to https://googlelogoligature.com and accept the HTTPS warning (but NOT the fake site warning). Note that an attacker can get a valid cert for the ligature domains, so this is only needed for PoC.
Permissions scenarios
- Navigate to https://alesandroortiz.com/security/chromium/ligatures-perm.html
- Click anywhere.
- If indicated by instructions, click anywhere again. (This is for perm prompts that require user interaction.)
Repeat steps 1-3 for each variation using the in-page links.
Observed: Permission prompts shows spoofed origin (with font ligature).
Expected: Permission prompts show actual origin.
Credit Information
Reporter credit: NDevTK https://ndevtk.github.io/writeups/ and Alesandro Ortiz https://AlesandroOrtiz.com