Unofficial security research on a 2024 Samsung Neo QLED running Tizen 9. No sdb, no developer mode, no firmware flashing: the TV is rooted from its own browser, and the root is made persistent through a stock on-boot service app.
| Test unit | 2024 Neo QLED, QNX1D series (QTV T09 platform) |
| Firmware | T-PTMxxxx (01400) |
| Kernel | Linux 5.4.249, ARM32 (VMSPLIT_3G + HIGHMEM, no LPAE), PAGE_OFFSET 0xC0000000, PHYS_OFFSET 0x20000000 |
| Browser | Chromium 120.0.6099.5 (SamsungBrowser/7.0) |
| Root | uid=0, CapEff=0x3fffffffff |
| Persistent payload | repo v41 (current md5/size in the AGENTS.md state row — regenerated from the artifact); the test unit runs the same build (deployed 2026-10-27, beacons carry tok=); payload_backup.dll keeps the previous build |
| Root channel | TCP 31337 on the TV, line protocol, replies end with __END__ |
pwsh -File .\root.ps1 -TvIp <tv-ip> # root the TV
pwsh -File .\uninstall.ps1 -TvIp <tv-ip> # restore stockroot.ps1 runs on the PC and drives everything: it starts the port-80 server,
roots the browser stub, installs the boot payload, and waits for the root channel
after the reboot. Two steps stay on the TV remote, and it prompts for both:
- open the printed
http://<pc-ip>/index.htmland wait forRUNNING; - power-cycle the TV once the deploy is verified.
Then:
python tools\root_shell.py "id" <tv-ip> # talk to the TV's root channelEvery option, the exact prompts, expected output, the host-IP caveat and the
manual fallback: docs/USAGE.md.
- Browser RCE — CVE-2024-2887 (WasmGC type confusion) in Chromium 120 gives arbitrary read/write and ARM shellcode execution inside the renderer.
- Physmap — the V4L2 codec nodes (
/dev/video40/30/60) accept an attacker-chosenvm_pgoffinmmap()and pass it toremap_pfn_range()unvalidated: arbitrary physical memory read/write from an unprivileged process (uid 5001, groupvideo). - Root — find the kernel Image and
init_taskin physical memory, locate our own task through a forked child'sreal_parent, and zero the credentials. - Persistence — replace the IL assembly of a stock
app_onbootservice with a .NET payload that repeats the physmap/cred patch at every boot, closes its node and serves a root command channel on TCP 31337.
No GPU CVE, no kernel exploit, no signed module. The physmap is the interesting
part; the stage-by-stage writeup with the measured constants is
docs/EXPLOIT.md.
| Document | Contents |
|---|---|
docs/USAGE.md |
running it: options, prompts, expected output, uninstall detail, recovery |
docs/EXPLOIT.md |
the chain stage by stage, with the firmware-specific constants |
docs/ROOT-METHODS.md |
every root/persistence method evaluated and the one that won |
docs/FAILED.md |
what did not work and why — reads like a list of dead ends, saves weeks |
docs/BOOT-REGRESSION-SMACK.md |
the subtlest bug in the project: a wrong SMACK label made the app die before Main |
docs/PORTABILITY.md |
another Tizen 9 TV: the four gates, what to re-measure, a step-by-step port |
docs/SERVER-HOSTING.md |
the PC-side server: how it must run, the silent-death trap, firewall, logs |
docs/SCRIPT-REVIEW.md |
production-readiness audit of the scripts |
docs/TESTING.md |
the test layers, offline and on-TV, and what each catches |
docs/LOGGUIDE.md |
every beacon and tool error decoded, with next steps |
docs/REFERENCE-MATERIALS.md |
where to download firmware / GPL sources / sim tooling for your own model |
docs/REVIEW-HISTORY.md |
closed review findings and their evidence (archive) |
docs/OPEN-ISSUES.md |
current state, pending work, and the closed review/pass history |
AGENTS.md |
hand-off: tools, procedure, verification, traps, recovery |
CHANGELOG.md |
dated history of what changed and why |
SECURITY.md |
the unauthenticated 31337 backdoor, scope, reporting, legal |
NOTICE.md |
third-party attribution; which files are not relicensed |
LICENSE |
MIT for the original work (third-party files excluded) |
root.ps1, uninstall.ps1 PC entry points (PowerShell)
payload/ boot payload source (.NET 6, Program.cs)
server/ port-80 server, exploit page, TV-side deploy scripts
tools/ physmap client, kernel helpers, build scripts
docs/ the documents above
AGENTS.md hand-off notes
LICENSE, NOTICE.md license + third-party attribution
SECURITY.md backdoor/scope/reporting
The whole toolkit is committed, so a clone runs without any other working tree.
server/payload.dll is included on purpose (byte-identical to the build output)
so the toolkit works without a .NET SDK. Runtime logs, build output and the
per-host generated deploy script are git-ignored and never served over HTTP.
- The same TV family and firmware generation — see
docs/PORTABILITY.mdbefore trying another model. - A PC on the same LAN, able to receive TCP/80 from the TV and reach TCP/31337
on it. No address is committed for the normal flow:
server/deploy.shis a template whoseH=/WANT=/SIZE_WANT=/TOKEN=markersserver.pyrenders per request from the address the TV used and the payload actually served, the page rewrites the shellcode's callback (IP and port), and the payload readsshare/rootchan.host. The compiled-inHostis a TEST-NET placeholder. - Python 3 on the PC. On Windows the server must run under an interpreter path the
firewall allows (see
docs/SERVER-HOSTING.md). - .NET SDK only if you rebuild the boot payload —
payload/global.jsonpins 10.0.x (rollForward: latestPatch); the project targets net6.0, which the pinned SDK still builds (an EOL warning is expected).
Reference material (firmware, GPL kernel sources, QEMU tooling) is not bundled; docs/REFERENCE-MATERIALS.md lists where to download each piece for your own model.
The method is portable; this build is not. payload.dll hardcodes this kernel's
PHYS_OFFSET, init_task and four task_struct offsets, and the browser chain
is specific to Chromium 120. The short version:
- Browser gate — serve
server/probe.html(safe; it executes nothing) and open it on the TV. You need Chromium ~120 with WasmGC; otherwise this CVE does not apply. - Physmap gate — get code execution as
owner(browser chain), then prove an arbitrary-pfn mmap on one of the TV's/dev/video*nodes withtools/physmap.py/tools/physrpc.S. If the offset is validated, stop. - Constants — measure
PHYS_OFFSET, the Image base,init_task, and thetasks/comm/cred/real_parentoffsets. Edit the constants inpayload/Program.cs; re-check the read-reliability limit below. - Carrier — find a
dotnet-inhouse,app_onbootapp on writable/optwhose files you can rewrite under its own SMACK label. Set its app id/paths inserver/deploy.sh,server/uninstall.sh, andserver/recover.sh. - Build, deploy, verify —
dotnet build -c Release -o out, copy the artifact overserver/payload.dll(the deploy gate is rendered from it, so there is noWANT=to edit), deploy viaroot.ps1, then cold boot. Attempt 2 is the expected floor.
Read-reliability limit found on the test unit: the video40 physmap returns
garbage for reads at/above 0xE0000000 even when the VA/PA alias is correct.
The linear map itself is bigger (zoneinfo: high_memory = VA 0xF0400000),
so 0xE0000000 is inside it — the prime suspect is the V4L2 node's DMA
window, not the CPU map. Either way: test a known low page and a known high
page on the new model before trusting Kptr; all successful cred patches here
were below 0xE0000000, and it affects how you interpret "not found".
Full gates, measurement recipes, and a file-by-file porting checklist:
docs/PORTABILITY.md.
- Use only on hardware you own. Rooting voids the warranty, and a bad write to the wrong physical page can crash or brick the set.
- The root channel on 31337 is an unauthenticated LAN root shell (the allowlist
is the only filter). Never expose it to the internet or a network you do not
control. See
SECURITY.md. - The boot payload takes a physmap node (
/dev/video40; exclusivity is per instance). It retries video40 onEBUSYand only moves tovideo30/video60when a node reportsENOENT/ENODEV— switching on busy would break the boot, because only video40 serves the payload's reads. It closes the node after rooting so the browser path keeps working. - Keep the stock
mcs_orig.dll/mcs_ni_orig.dllbackups and leave TV auto-update off — an OTA can replace the app and undo persistence. - No Samsung firmware, proprietary images or dumps are distributed here.
- Provided as-is, without warranty, for security research and interoperability on
devices you own. Original code is MIT licensed (
LICENSE); the third-party browser-chain files are attributed inNOTICE.mdand are not relicensed by that file.
Standing on public work by:
- hdbreaker — the
samsung-smarttv-tizen-9-chromium-120-v8-rcebrowser chain (CVE-2024-2887 + WasmGC) that gets code execution on Tizen 9. - levkropp — the boot-agent shape (on-boot app + V4L2 physmap).
- califio / MADBugs — the
/dev/ntksysphysmap-to-cred technique on older Samsung TVs, the direct ancestor of this approach. - DemonKingSwarn, chris-ritsen, invaliddayta — Tizen root/debloat research and tooling.
- theubusu (unixtract), gtors (vdfs-unpack) — firmware decryption and VDFS4 extraction used during the research.
Samsung and Tizen are trademarks of Samsung Electronics. This project is unofficial and not affiliated with or endorsed by Samsung.