> standard_output

Research

What's our LG Smart TV up to? Part 2

Last time we watched our TV's encrypted traffic from the outside. This time we sat in the middle with a certificate we made up. LG's own servers refused it. The server collecting what we watch waved us through.

Sam Lazarus · October 2026

Summary

  • We put a proxy between the TV and the internet, holding certificates from an authority we’d created minutes earlier.
  • LG’s ad server, LG’s platform server and YouTube refused them and hung up, exactly as they should.
  • The content recognition server accepted one, so for the first time we could read what the TV says about us.
  • It sends audio fingerprints: twelve uploads in 23 minutes of a cartoon played from a laptop, each one acknowledged.
  • It also reports which HDMI socket is in use, and what’s plugged into it by make and model.

Where we left off

In our last post we recorded every packet our 2025 LG TV sent over a day. On default settings it contacted Alphonso’s automatic content recognition (ACR) server, tkacr394.alphonso.tv, every minute it was on, and sent three times as much data while a film played from a laptop over HDMI. One setting, Live Plus, stopped it.

Almost everything the TV sends is wrapped in TLS, so we could see the shape of that traffic but not its contents. The bursts looked like fingerprints of what was on screen. We couldn’t prove it.

What a certificate is for

Certificates are the part of HTTPS that answers “am I really talking to who I think I am?”. Anyone can make one claiming to be any website. The signature is what stops that working. A device that checks properly rejects a certificate from an authority it doesn’t trust.

So there’s a clear pass mark. We generated our own authority on the Raspberry Pi, had it mint certificates for whatever the TV asked for, and never installed it on the TV. A TV that checks refuses. A TV that doesn’t hands us everything.

Smart TVRaspberry PiThe real serversasks for a serverWi-Fiproxy + our owncertificate authorityEthernetLG, Alphonso, Google
The TV thinks it is talking to LG and Alphonso. It is talking to a Raspberry Pi holding certificates it made up.

Only the TV’s encrypted web traffic was redirected. One detail mattered: content recognition uses port 4433, not the usual 443. Catching only 443 would have intercepted everything except the one thing worth looking at.

Which connections were accepted, and which refused

Within a minute the answer arrived, and it wasn’t the clean yes or no we expected. In the hostnames below, <cc> replaces a country code.

ServerWhat it’s forOur certificate
<cc>.ad.lgsmartad.comLG advertisingRefused
<cc>.nextlgsdp.comLG platformRefused
www.youtube.comGoogleRefused
discovery.meethue.comSmart-home lookupAccepted
tkacr394.alphonso.tv:4433Content recognitionAccepted

The refusals were textbook: the TV saw an authority it had never heard of, sent the TLS alert that means exactly that, and hung up. Two came from LG’s own servers.

That rules out the boring explanation. Same TV, same proxy, same invented authority, same minute. The TV is perfectly capable of checking certificates. On the connection carrying what we watch, it doesn’t.

What it was actually sending

With the connection open to us, the channel had three parts.

RequestSizeWhat it does
POST /v5/audio/fingerprint1.1 to 23 kBthe fingerprints themselves
POST /v5/user/appsource365 Bwhat is playing and where from
POST /v5/user/lookups193 Bholds a connection open, waiting

The first settles what the last post left open. The endpoint is named /v5/audio/fingerprint and the server answers each upload with 202 Accepted. During 23 minutes of a cartoon from a laptop the TV sent twelve, from 1.1 kB to 23 kB, in three clusters about six minutes apart. In the 46 idle minutes beforehand, none. That is the burst pattern we measured from outside last time.

Note the word audio. We had assumed the TV was sampling the picture. It is listening. That explains what seemed odd last time, when the TV reported just as diligently on a film arriving over HDMI from a laptop. It needs only to hear the soundtrack.

It also knows what you plugged in

The second request was the surprise. Once a minute, alongside the fingerprints, the TV sends a status report:

{
  "app_name": "",
  "device_input": "HDMI2",
  "hdmi_spd_json_string": "{\"description\":\"<graphics-card-model>\",
                            \"manufacturer\":\"<vendor>\",
                            \"type\":\"PC general\"}",
  "hdmi_cec_json_string": "{\"osdname\":\"\",\"type\":\"\"}",
  "channel_name": "", "channel_number": "",
  "livetv_metadata_json_string": "",
  "vod_metadata_json_string": ""
}

Every HDMI device announces itself to the screen it’s plugged into, in a block called the Source Product Description: who made it, what it is, what class of device it is. Ours forwards that block to Alphonso once a minute, with the socket it arrived on.

So the report isn’t only about what we watched: it named the graphics card inside the laptop, by vendor and model. The fields come through blank on an empty home screen, so this starts only once something is connected.

That is different from content recognition, and worth separating. Fingerprinting tells an advertiser what you watched. This tells them what hardware you own.

What we couldn’t confirm

The third request, /v5/user/lookups, is how a recognition result would come back: it holds a connection open and waits for the server to push an answer. We recorded 68, and not one returned anything we could read.

Three explanations fit and our data cannot separate them. Alphonso may hold no fingerprint for a decades-old cartoon. A match may take longer than our 23-minute run. Or our interception broke the mechanism, since a connection held open is exactly the kind that doesn’t survive being proxied.

So we have proof the TV uploads audio fingerprints and that the server accepts them. We have no proof of what came back, and we won’t claim otherwise.

Why this is worse than it sounds

The traffic is readable by anyone on the path. Not just us in a lab built on purpose: a compromised router, a hostile public network, anyone between the TV and the internet. TLS should make that position useless. For this one stream it doesn’t.

It’s writable too. A TV that accepts our certificate will accept our responses. We didn’t tamper with the replies, but nothing stopped us. Last time we rewrote the TV’s unencrypted home-screen images and it showed our joke posters without warning.

It carries identifiers. Every request puts a persistent user identifier and a device identifier in the URL, alongside an API key. We’ve replaced them with placeholders here. Anyone watching the traffic would not have to.

None of this requires the fingerprinting to be sinister. You can think LG and Alphonso are entitled to run content recognition on a TV that disclosed it, and still think the channel carrying it should check who is on the other end.

What you can do

  • Turn off Live Plus. Still the single setting that stops all of this: no fingerprints, no HDMI reports, nothing to intercept. Settings → All Settings → General → System → Additional Settings.
  • Don’t assume the padlock means much. That a device checks its own encrypted connections does not hold uniformly, even within one TV from a major manufacturer.
  • Keep the TV on its own network. A guest or IoT network limits what an attacker on the path can reach next.
  • Consider blocking the domain. Sinkholing *.alphonso.tv at your router or with a Pi-hole stops the connection entirely. We still haven’t tested what else that breaks.

Previously in this series

What's our LG Smart TV up to?

We recorded every packet a 2025 LG TV sent over a day. On default settings it checked in with LG's ad-tech arm every minute, and sent three times as much while a laptop played a film over HDMI. Turning off one setting, "Live Plus", stopped it within the minute.

Read the post →

Appendix: reproduce it yourself

Same Raspberry Pi access point as last time. The only additions are a proxy and two firewall rules.

# mitmproxy in a venv on the Pi, generating its own CA on first run
python3 -m venv ~/mitm && ~/mitm/bin/pip install mitmproxy
~/mitm/bin/mitmdump --mode transparent --showhost --listen-port 8080 \
  --set flow_detail=1 -w p9-flows.mitm

# send only the TV's TLS traffic to it. 4433 is the content recognition port.
table ip tvmitm {
  chain prerouting {
    type nat hook prerouting priority -100;
    iifname "wlan0" ip saddr <tv-ip> tcp dport { 443, 4433 } redirect to :8080
  }
  # QUIC would bypass the proxy entirely, so force a fallback to TCP
  chain forward {
    type filter hook forward priority -10; policy accept;
    iifname "wlan0" ip saddr <tv-ip> udp dport 443 drop
  }
}

Two things caught us out. Existing connections keep flowing on their old path, so nothing is intercepted until they re-establish. Content recognition also holds its connections open for minutes, which makes it the last to reconnect.

References

All testing was done on a device we own, on our own network, against servers our own device chose to contact. Identifiers have been replaced with placeholders throughout.

Ready to test your defences?

Tell us about your environment and what you need tested. We'll scope an engagement that fits your organisation.

Get in touchsam@standardoutput.com.au