Skip to content

Latest commit

 

History

History
74 lines (46 loc) · 4.62 KB

File metadata and controls

74 lines (46 loc) · 4.62 KB

Ghostwire · root package (e3q)

One-command root on the SM-S928B (e3q) with a locked bootloader, entirely in software, at runtime. Base: CVE-2026-43499 ("GhostLock"), a use-after-free in rtmutex/PI-futex published by NebulaSec, ported from the e2s profile to e3q. The root is soft: it dies on reboot.

./run.sh            # root, with verification
./run.sh --check    # preflight only, does not touch the device
./run.sh --reboot   # reboot first if uptime is high

What decides between a first-try hit and five wasted minutes is heap state. Measured on this bench: at ~3 h uptime under heavy use it failed 3 rounds in a row (wait_requeue_pi errno=110); right after a boot it took on the 1st attempt, 2 for 2. run.sh reads the uptime during preflight and says so; --reboot handles it. A cold boot (power off, power on) beats a reboot.

Exits 0 with root confirmed, 2 on preflight failure, 1 if the exploit did not take.

Supported target

Field Value
Device SM-S928B (e3q, Snapdragon 8 Gen 3)
Kernel 6.1.145-android14-11-33419968-abS928BXXS6DZE1
Build One UI 8.5 / Android 16, patch 2026-05-05

run.sh aborts when the device kernel is not that string. It is not fussiness: the payload carries offsets for this build, and on another one it fails or takes the device down. After a reset followed by an OTA, this is the check that keeps you from hunting a bug that does not exist. To skip it at your own risk: --force.

The chain

  1. tslide reads this boot's KASLR slide through tracefs: a deterministic oracle, re-read every round (the slide changes on every boot).
  2. payload.so enters through LD_PRELOAD on a /system/bin/sh: file_operations hijack → configfs → physical R/W over a pipe → selinux=0 → UMH.
  3. roothelper stays as an su daemon. Confirmation: roothelper -c id → uid=0(root) … context=u:r:kernel:s0.

After a factory reset

The blocker is not the exploit; it is adb. On a freshly reset phone USB debugging is off and the host is not authorised, and that is a physical step, not something that can be automated. run.sh detects it and says exactly what to do:

  1. Settings → About phone → Software information → tap Build number 7×
  2. Settings → Developer options → USB debugging
  3. Accept the Allow USB debugging dialog for this computer
  4. ./run.sh

After that, /data is empty but none of it matters: run.sh re-pushes the three binaries and validates each one's size on the device (a truncated push has happened).

If it does not take

It is almost always a stale heap: run ./run.sh --reboot. run.sh already does 3 rounds with a fresh slide read each time (the slide changes on every boot) and survives a reboot mid-run; the full log lands in last-run.log.

The heap-failure signature is slide wait_requeue_pi ret=-1 errno=110 (ETIMEDOUT): the UAF trigger does not fire. If instead the kernel did not match the supported one, preflight would have aborted before any of this.

Never turn on P0_ASHMEM_PROBE or P0_CFG_DEBUG: they crash (a pread on a fake fops without SET_NAME dereferences NULL). The core runs without probes.

Rebuilding

Only needed for a different firmware. Requires the NDK:

export ANDROID_NDK_HOME=~/Android/Sdk/ndk/28.2.13676358    # macOS: ~/Library/Android/sdk/ndk/...
make && make verify

The Makefile picks the NDK's host toolchain automatically (linux-x86_64 or darwin-x86_64).

The target is fixed (e3q-S928USQS6DZF2) on purpose: that is the target header the payload in use was compiled with, and the embedded BUILD_VARIANT_LABEL proves it. Compiling for another profile produces a payload that fails at runtime, and finding that out is expensive. make verify checks the label before you take it to the device.

src/targets/ holds two profiles: ours (e3q) and e2s, which is the base the port came from and is cited in the fixes inside the e3q target.h.

Inherited vs. ours

From the CVE: the bug, the write primitive (rb-erase) and the architecture of the chain (fops → configfs → physrw → UMH → daemon).

Ours: the tracefs KASLR oracle (a deterministic vector in place of the base exploit's brute force) and the five target corrections that made e3q / One UI 8 work, an unsupported target before. Each one is written up with its failure signature in the main README, and lives next to the code in target.h.

This is a port and adaptation with its own oracle, not a different exploitation of the CVE.


Own device, authorised research.