Case 01 — Google Messages

A preview to render,
your IP to the sender.

Google Messages is the texting app built into a very large share of Android phones. It builds link previews on the device — so a single link from a saved contact can quietly reveal the recipient's IP address, device and timing to whoever owns that link. No tap, no notification. You open the chat once; after that, new links can report home on their own. The leak is narrow — an IP and some metadata, honestly Medium — but it ships on hundreds of millions of phones, and it is working as designed.

By Abbas Hoseiny· Independent security research· Vendor: Google

Google Messages is the app behind the chat-bubble icon on most Android phones — the texting app almost nobody picks by name, carrying your SMS and RCS chats. A link preview is a small convenience: send someone a URL and the chat shows a title, a description and a thumbnail instead of a bare address. The question that decides whether it is private is simple — who fetches the page? If a server in the middle fetches it, the link's owner sees the server. If your own phone fetches it, the link's owner sees you: your network, your device, your timing.

A grid of many identical locked phones; some show a link from a saved contact
Fig. 1 — the same app, on countless phones

The same app, the same ordinary chats — on a very large number of phones. A link arrives from a saved contact, in a conversation opened once before; no one has to touch anything.

In Google Messages the preview is built on the device. The app extracts the URL, and — once the usual conditions are met — makes an ordinary HTTPS request to that address from your phone. The request does not pass through a Google proxy. That is the whole mechanism; everything else is about when it fires.

Red dotted lines run from many of the phones to a single server
Fig. 2 — the silent fetch, multiplied

Each phone fetches the link by itself to build the preview. Nothing lights up, nothing asks. Across every conversation, the requests all arrive at one place: the server that owns the link.

Here is the honest part, and it is where the earlier version of this report overreached. The very first handler, when the message lands, often only writes a deferred placeholder and returns without any network request. The actual fetch is driven by an observer that starts when the conversation becomes interactive — when you open the chat. That observer is tied to a background scope with no visible shut-off when you leave, which is why, after a chat has been opened once, later links in it can be fetched while the screen is locked and the app sits in the background.

A server log filling with lines: the same recipient IP, a GoogleMessages user-agent and timestamps minutes apart
Fig. 3 — the log on the other side

On the server that owns the link, the requests simply pile up: the recipient's IP address, the user-agent and timestamps a few minutes apart — lines the recipient never agreed to write.

One line is a coarse data point. The preview is stored per message, not per URL, so the same link sent again in a new message is fetched again. A sender who is a saved contact can therefore send the same link on a schedule and collect a fresh reading each time — a rough trail of which network the recipient is on, and when.

A map with several diffuse red areas — approximate regions, no precise marker — linked from a locked phone
Fig. 4 — a coarse trail, over time

An IP address is not a street address, but over repeated messages it narrows to a city and a network, and it shows when a device comes and goes. That is the privacy cost of a preview that is fetched by the phone instead of a proxy.

This was reported to Google in March 2026. Google triaged and closed the report on 2026-03-17, as recorded in the timeline below. The behaviour described here is still present in the release installed on the test device when it was re-examined on 29 October 2026. This page states what the code and a single device test actually support — and, just as deliberately, what they do not.

Technical write-up

Google Messages — findings & analysis

Summary & severity

Google Messages (com.google.android.apps.messaging) was examined on a Samsung Galaxy A26 (SM-A266B), Android 16, build BP4A.251205.006, running the package messages.android_20260910_03_RC02.phone_samsung_dynamic (version code 323089900). The transport under test was RCS. Analysis is static, against the exact installed APK (base APK SHA-256 9e869a68…ff60b2, signature verified as a genuine Google build), combined with behaviour observed on that one device.

IDFindingCWECVSS 3.1Severity
F-PREVIEWIP/metadata leak via on-device link preview200/3594.3Medium
F-UAPreview request carries a spoofed, remotely configured user-agent (folded into F-PREVIEW)451—Context
F-PREVIEW · CWE-200 / CWE-359

IP/metadata leak via on-device link preview

Medium · CVSS 4.3

Description

When a message containing a URL is handled, Google Messages generates the link preview by making an HTTP(S) GET to that URL from the device, not through a server-side proxy. The server that owns the link therefore receives a request originating from the recipient's current network egress: its IP address, the preview user-agent, Accept-Language and the request time. Follow-up requests for an og:image or a favicon can additionally carry a Dalvik user-agent that exposes the Android version, device model and build number.

How the fetch is actually triggered

This is the point that most needs stating precisely, because it sets the severity. The path is two-stage:

  1. On receipt, request_link_preview work is scheduled after the message database commit (dfvl/bcnl → btlr.b()). For a new link, the first pass can insert a deferred preview row and return before any network request (btln).
  2. When the conversation becomes interactive — i.e. the chat is opened — onMessageListInteractive starts an observer (ahxw → aied) that watches the deferred rows for that conversation and schedules a second, deferred=true job (aieb → btlr.a()), which can proceed to the fetch after the gates below.

The observer runs in a background coroutine scope with no visible unregister when the chat is left. That is the code-level reason a fetch can be observed with the screen locked and the app backgrounded after the chat has been opened once. A fetch with the conversation never opened is not established by the visible code and is not claimed here.

Gates that must pass first

  • Preview setting. The automatic-preview setting must be on (aqck). Google Messages exposes “Show all previews” / “Show only web link previews” / off.
  • Sender trust. The sender must satisfy a trust predicate (btez.m()): a populated contact lookup_key is one accepted condition, with two other participant states. A separate flag path can instead count manual preview loads (APK-default threshold 3, stored per participant). The APK default enable_link_preview_untrusted_senders is false. Opening a chat or marking a message read is not the same as a manual preview load; live remote-config values were not read.
  • URL scheme. The parser accepts only http/https (it rewrites ws:/wss:); it rejects file:// and content://, and the image path runs through that same HTTP-only client.

Impact, bounded to evidence

The link's server learns the recipient's current egress IP, user-agent, language and timing, and — via image/favicon follow-ups — device model, OS version and build. Because the preview is keyed by message ID, not URL, the same link in a new message produces a new fetch, so a trusted sender can take repeated readings over time and build a coarse location/network trail. In the archived March run the device used a VPN, so the address observed was a VPN egress, not a residential IP. No access to other apps, no local-service reachability, no account takeover and no code execution were demonstrated or are claimed.

Who counts as “trusted”?

“Saved contact” is broader than it sounds. It is not only the people you chat with — it is any number your phone already knows: a shop, a delivery service or a bank whose number you once saved. Whether verified business senders — the bulk SMS/RCS services companies use to send you links, which arrive with a business name and no “unknown sender” warning — clear the same bar was not tested, so it stays here as a question for Google. Either way, the set of senders who could trigger an automatic fetch is wider than “your friends”.

CVSS 3.1 breakdown

AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N → Base 4.3 (Medium)

The impact is disclosure of an IP address plus a user-agent and device metadata, which is C:Low — this caps the score at Medium no matter how little interaction each individual fetch needs. UI:R reflects the code: the fetch chain is driven by the conversation having been opened. The earlier “Critical / zero-click” framing is not supportable: it rests on an UI:N, never-opened path that the visible code does not establish, and on a C:High impact that an IP-and-metadata leak does not reach.

What a fix looks like

  • Fetch link previews through a Google-side proxy, so the link server sees Google's infrastructure rather than the recipient's device — the approach already used for other preview surfaces.
  • If on-device fetching is kept, make it strictly interactive (an explicit per-message tap), and do not let a background observer re-drive fetches for a conversation after it is closed.
  • Document the data flow plainly in the privacy help, so “automatic previews” is not read as “previews that cannot reveal your IP”.
Severity

A note on the score

A CVSS base score is driven by the attack vector, the interaction needed and the confidentiality/integrity/availability impact — not by how alarming a finding sounds. A privacy leak that reveals an IP address and some device metadata is C:Low, so it cannot reach High no matter how little the victim has to do. “Zero-click” would only lower the interaction metric, not raise the impact ceiling — and here the code does not even support the zero-click, never-opened variant, so the honest vector is UI:R, Base 4.3.

This finding is real and worth fixing — and honestly Medium. An earlier draft labelled it Critical; that label is inconsistent with its own CVSS and with what a single IP/metadata disclosure actually is. I would rather publish a defensible score than an inflated one.

What this is — and is not

To keep the record clean, these were checked and are not claimed as part of this finding:

Check it yourself

No screenshots are published here; the finding stands on the code path above, and anyone can reproduce it with their own endpoint. The idea in one paragraph:

To prevent these requests, disable automatic web link previews in settings. “Show only web link previews” does not stop these web fetches. Be aware that a saved contact can probe your network with a link.

Disclosure timeline

What would count as fixed

I would rather update this page than defend it. The page will say “fixed” when link previews are fetched through Google's own infrastructure (so the link server no longer sees the recipient's IP), or when on-device fetching is made strictly interactive with no background re-driving after a conversation is closed — in the shipping release, not only an internal build.


All testing was performed on my own device and my own accounts, against endpoints I control. No other person's device, account or data was involved. This write-up contains no malware and no weaponised exploit; the reproduction is an ordinary link sent to an endpoint you own. Identifiers are truncated.