CVE-2026-87595
Overview
Background
- SSRF (server-side request forgery)
- a class of bug where an application is coerced into issuing network requests to attacker-chosen destinations rather than intended ones.
- `ImageDataFetcher`
- a Chrome
image_fetchercomponent that downloads raw image bytes for a given URL through aSharedURLLoaderFactory. - Omnibox favicon fetching
- the iOS omnibox popup asynchronously downloads favicon/entity images whose URLs are supplied by autocomplete suggestions, via
OmniboxImageFetcher. - `GURL::SchemeIsHTTPOrHTTPS`
- a
GURLpredicate that returns true only when the URL’s scheme ishttporhttps.
Root Cause Analysis
The iOS omnibox rendered suggestion images by handing a suggestion-supplied imageURL straight to -[OmniboxImageFetcher fetchImage:completion:], which passed it to an ImageDataFetcher with no scheme restriction and no download-size ceiling. Because the image URL originates from autocomplete/suggestion data rather than a trusted constant, an attacker who could influence a suggestion’s image URL could point the fetch at a non-web scheme or an unbounded resource, causing the client to emit a request it should never make — the SSRF condition, plus an unbounded-download resource risk. The invariant violated is that a favicon fetch should only ever traverse http/https and should be size-limited.
The fix restores this by
- capping the
ImageDataFetcheratkMaxFaviconDownloadBytes(5 MiB) in the popup coordinator and - short-circuiting the fetch’s completion for any URL that is not
SchemeIsHTTPOrHTTPS(). Note that the added scheme guard callscompletion(nil)without a followingreturn, so control still falls through to the cache/fetch statements; the practical mitigation is that the scheme is now checked at all and the size is bounded, but the early exit is not a hard abort of the method.
http/https allowlist check and a 5 MiB download cap so non-web and oversized fetches are refused.Attack Path
- Influence a suggestion image URL An attacker gets a crafted autocomplete/suggestion entry whose associated favicon/image URL uses a non-HTTP(S) scheme or an unbounded resource to reach the omnibox popup.
- Trigger the fetch
Displaying the suggestion causes
-[OmniboxImageFetcher fetchImage:completion:]to be invoked with the attacker-controlledimageURL. - Unrestricted request emitted
Pre-patch, the URL is forwarded to
ImageDataFetcherwith no scheme check, so the client issues a request to the attacker-chosen destination (SSRF), and with no size limit it may download an arbitrarily large payload. - Consequence
The attacker gains an out-of-context request initiated by the app and/or resource consumption; any escalation beyond the emitted request and download is
(inferred)and not demonstrated by the patch.
Impact Assessment
(inferred) rather than established by the diff.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifios/chrome/browser/omnibox/model/omnibox_image_fetcher.mm |
modified |
Files Changed
ios/chrome/browser/omnibox/model/omnibox_image_fetcher.mmios/chrome/browser/omnibox/coordinator/popup/omnibox_popup_coordinator.mm
Audit Directions
- Suggestion/UI-supplied URLs handed to fetchersAudit other omnibox and suggestion surfaces that pass externally-derived URLs to
ImageDataFetcher/ImageFetcherwithout validatingSchemeIsHTTPOrHTTPS()or setting a download limit. - Guards that signal but do not abortLook for early-exit checks that call a completion/callback but omit
return, allowing the guarded code to still execute the sink it was meant to prevent. - Unbounded network downloadsReview image/asset fetchers that omit
SetImageDownloadLimitor an equivalent size cap, since these enable resource-exhaustion and large-response abuse.
Patch
From c1b361f469d29daac4b4062daeb3ee01ba3aa81c Mon Sep 17 00:00:00 2001
From: Stepan Khapugin <stkhapugin@google.com>
Date: Tue, 28 Jul 2026 04:30:19 -0700
Subject: [PATCH] [iOS][omnibox] Limit favicon downloads to HTTP(s) and <5Mb.
Fixed: 513726466
Change-Id: Idcca8a9717368bbc1b52ce13b8f0ef76776d8856
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8073746
Commit-Queue: Stepan Khapugin <stkhapugin@chromium.org>
Reviewed-by: Radu Nitescu <radunitescu@google.com>
Cr-Commit-Position: refs/heads/main@{#1669358}
---
diff --git a/ios/chrome/browser/omnibox/coordinator/popup/omnibox_popup_coordinator.mm b/ios/chrome/browser/omnibox/coordinator/popup/omnibox_popup_coordinator.mm
index aa31021e..7493f817 100644
--- a/ios/chrome/browser/omnibox/coordinator/popup/omnibox_popup_coordinator.mm
+++ b/ios/chrome/browser/omnibox/coordinator/popup/omnibox_popup_coordinator.mm
@@ -50,6 +50,12 @@
#import "services/network/public/cpp/shared_url_loader_factory.h"
#import "ui/base/device_form_factor.h"
+namespace {
+
+const int64_t kMaxFaviconDownloadBytes = 1024 * 1024 * 5;
+
+} // namespace
+
@interface OmniboxPopupCoordinator () <OmniboxPopupMediatorProtocolProvider,
OmniboxPopupMediatorSharingDelegate>
@@ -100,6 +106,7 @@
std::unique_ptr<image_fetcher::ImageDataFetcher> imageFetcher =
std::make_unique<image_fetcher::ImageDataFetcher>(
self.profile->GetSharedURLLoaderFactory());
+ imageFetcher->SetImageDownloadLimit(kMaxFaviconDownloadBytes);
_omniboxImageFetcher = [[OmniboxImageFetcher alloc]
initWithFaviconLoader:IOSChromeFaviconLoaderFactory::GetForProfile(
diff --git a/ios/chrome/browser/omnibox/model/omnibox_image_fetcher.mm b/ios/chrome/browser/omnibox/model/omnibox_image_fetcher.mm
index 133d961d..099a642 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_image_fetcher.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_image_fetcher.mm
@@ -45,6 +45,11 @@
}
- (void)fetchImage:(GURL)imageURL completion:(void (^)(UIImage*))completion {
+ // As a precaution, don't fetch images from nonstandard schemes.
+ if (!imageURL.SchemeIsHTTPOrHTTPS()) {
+ completion(nil);
+ }
+
NSString* URL = [NSString cr_fromString:imageURL.spec()];
UIImage* cachedImage = [_cachedImages objectForKey:URL];
if (cachedImage) {