JoinChang/ghostlock-oneplus

GhostLock (CVE-2026-43499) kernel exploit for Android devices with locked bootloader

339

stars

63

commits

C

primary language

Sep 8, 2026

updated

android
coloros
ghostlock
kernelsu
oneplus
root

README

GhostLock — Locked Bootloader Jailbreak

Kernel exploit for Android devices with locked bootloader. Achieves temporary root + KernelSU installation without unlocking bootloader or modifying boot image. Runtime auto-detection of kernel version with multi-device offset table.

GhostLock running on OnePlus Ace 6T with KernelSU (LKM, Jailbreak mode)

Vulnerability

CVE-2026-43499 — Futex PI (Priority Inheritance) Use-After-Free

Affects Linux kernel 5.7 ~ 7.1. Fixed in stable 6.1.175, 6.6.140, 6.12.86. Most Android devices remain unpatched as of September 2026.

The pselect6 syscall copies fd_set data onto the kernel stack. When combined with the futex PI waiter mechanism, a freed stack frame can be reclaimed as an rt_mutex_waiter structure. The rb-tree rebalance during PI chain walk then writes controlled values to arbitrary kernel addresses.

Supported Devices

Verified Working

DeviceSoCKernelGKI BranchSHIFT
OnePlus Ace 6T (PLR110)SM88456.12.38android16-50
OnePlus 15 (CPH2745/2747/2749)SM88506.12.23android16-50
Xiaomi 17 (pudding)SM88506.12.23 / 6.12.69android16-5 / android16-60
OnePlus 13 (IN2060)SM87506.6.89android15-8-2
OPPO Pad 4 ProSM87506.6.89android15-8-2

Offsets Extracted (pending device test)

DeviceSoCKernelNotes
OnePlus 15T (PLZ110)SM88456.12.38Same kernel as Ace 6T
OPPO Reno10 Pro+ (CPH2521)SM84755.10.2365.10 compact waiter, waiter word=0
Vivo X Fold3 Pro (PD2337)SM86506.1.1246.1 compact waiter, waiter word=3
Vivo T4SM86506.1.1456.1 compact waiter, waiter word=3

Not Feasible

The pselect stack overlay requires the freed rt_mutex_waiter to land within the user-controllable stack_fds region (words 0–14). Where it lands is determined by compiler PGO/LTO profiles, not the kernel version. See Stack Layout.

DeviceSoCKernelRoot Cause
OPPO Find X9 UltraSM87506.12.58PGO eliminates do_futex → waiter word=14
OnePlus 12SM86506.1.141PGO inlines do_futex → waiter word=13/19
OnePlus 13R / Ace 5SM86506.1.xOPLUS 6.1: waiter word=13
realme RMX5070SM66506.1.141OPLUS 6.1: waiter word=13
OPPO Pad 5 (OPD2502)MT68786.1.134OPLUS 6.1: waiter word=13
Motorola Edge 60 FusionMT68786.1.145Non-OPLUS 6.1: same result
iQOO Neo 10 CNSM86506.1.84do_futex frame 0xD0 → waiter word=-11
OPPO PKW1105.15.180do_futex frame 0x140 → waiter word=-29
iQOO Z9 5G5.15.178do_futex frame too large
CPH2763 (OPPO)6.1.115OPLUS 6.1: PGO inlined, waiter word=24

Note on 6.1 feasibility: OPLUS 6.1 kernels are consistently infeasible due to PGO inlining do_futex. However, some non-OPLUS 6.1 kernels (vivo) retain the standard call chain and are feasible (vivo T4, X Fold3 Pro). Feasibility must be checked per-device.

Exploit Flow

Two root paths, selected automatically based on device capabilities:

Path A: UMH Root (preferred, C ashmem devices)

Requires off_ashmem_misc_fops != 0 (C ashmem with static miscdevice in BSS).

PI write (mode=4)  →  redirect miscdevice fops to fake fops
                   →  configfs r/w → pipe physrw (1-byte precise kernel r/w)
                   →  SELinux enforcing = 0 (single byte, no policycap corruption)
                   →  UMH: inject work_struct into system_unbound_wq
                   →  root script → ksud late-load → KSU installed

Available on: OnePlus 13, OPPO Pad 4 Pro, 5.10/6.1 C ashmem devices. Not available on Rust ashmem (6.12 GKI) — heap-allocated miscdevice.

Path B: Direct PI Write (fallback)

Write 1 (mode=1)  →  SELinux enforcing = 0 (8-byte write, corrupts adjacent bytes)
Write 2 (mode=2)  →  task->cred = init_cred (uid=0, all capabilities)
Root shell         →  ksud late-load → KSU → SELinux policy fix

After W1+W2, the exploit patches the SELinux policy binary's config field (|= 0xC0000000 for ANDROID_NETLINK_ROUTE + GETNEIGH) and reloads via /sys/fs/selinux/load to restore network connectivity.

Bootstrap Mode (phone standalone)

App (seccomp)  →  Write 1 → mini-adb TCP → adb shell: full exploit → root

Auto-Boot

The boot-time launcher is provided by the separate GhostLock Anchor app.

Stack Layout Feasibility

With NFDS=320, core_sys_select allocates a 256-byte stack_fds buffer on the kernel stack:

stack_fds:  0    5    10   14 | 15   20   25   29
            ├─in─┤─out─┤─ex──┤ ├res_in┤res_out┤res_ex┤
            ◄── USER CONTROLLED ──►│◄── KERNEL ZEROED ──►

The exploit places fake waiter fields (task, lock) in the fd_set input bitmaps. For 6.12 nested waiter (14 words): max feasible waiter word = 3. For 5.10/6.1 compact waiter (10 words): max feasible waiter word = 7.

The waiter position depends on the call chain depth:

PatternCall ChainFeasible
android16-5 (6.12)sys_futex → do_futex → fwrpi✅ waiter word=2
android15-8 (6.6)sys_futex → do_futex → fwrpi✅ waiter word=2 (SHIFT=-2)
vivo 6.1sys_futex → do_futex → fwrpi✅ waiter word=3
OPLUS 6.1sys_futex → fwrpi (PGO inlined)❌ waiter word=13+
android16-6 (X9 Ultra)sys_futex → fwrpi (PGO inlined)❌ waiter word=14
5.10 OPLUSsys_futex → do_futex → fwrpi✅ waiter word=0

kernel_phys_load

All kernel writes use the linear-map alias. The bootloader picks kernel_phys_load, which varies per SoC:

SoCkernel_phys_load
SM8845 (Ace 6T, 15T)0xa8000000
SM8750 (OnePlus 13, OPPO Pad 4 Pro)0xa8000000
SM8650 (vivo T4, X Fold3 Pro)0xa8000000
SM8850 (OnePlus 15, Xiaomi 17)0xc7800000

A wrong value fails silently. Read it on a rooted unit:

su -c 'grep -i "Kernel code" /proc/iomem'   # c7810000-... → 0xc7800000

Override at runtime: KPHYS=0xc7800000 /data/local/tmp/a/e

PSELECT_SHIFT

/data/local/tmp/a/e                        # Default (shift=0)
PSELECT_SHIFT=-2 /data/local/tmp/a/e       # OnePlus 13 (6.6)

KIMAGE_TEXT_BASE

5.10 kernels use 0xffffffc008000000 (VA_BITS=39, different from 6.x default 0xffffffc080000000). This is handled automatically via the kimage_text_base field in the device offset entry.

Build

make NDK_ROOT=/path/to/android-ndk

Or directly:

NDK=/path/to/android-ndk
"$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android35-clang" \
  -O2 -Wall -Isrc/core -Isrc/devices -DTARGET_CONFIG_H=\"target.h\" \
  src/core/main.c src/core/util.c src/core/slide.c \
  src/core/fops.c src/core/pipe_physrw.c src/core/root.c \
  src/core/miniadb.c src/core/umh_root.c \
  -o ./ghostlock -fPIE -pie -pthread

Prerequisites

ksud (required for KSU installation)

GhostLock provides temporary root. KernelSU installation requires ksud which bundles kernelsu.ko for each KMI version.

MethodSteps
ReSukiSU APK (recommended)Install ReSukiSU or this fork. Bundles libksud.so.
CI releaseDownload from ReSukiSU CI

Setup

adb shell mkdir -p /data/local/tmp/a
adb push ./ghostlock /data/local/tmp/a/e
adb shell chmod 755 /data/local/tmp/a/e
adb shell /data/local/tmp/a/e

Run after boot completes and ADB is ready.

Adding New Devices

Only boot.img is needed — no root, no device access required.

1. Extract kernel and kallsyms

See kallsyms tools guide for the full workflow, or:

python tools/extract_target.py --kallsyms kallsyms.txt
python tools/extract_btf.py kernel

2. Determine feasibility

Check if do_futex is PGO-inlined by searching the kernel binary for all branch instructions targeting futex_wait_requeue_pi. If __arm64_sys_futex calls it directly (bypassing do_futex), the device is likely not feasible.

3. Create device entry

Add src/devices/<name>/offsets.h with the extracted offsets and include it in src/devices/offsets.h. Use the appropriate struct offsets macro:

KernelWaiter LayoutMacro
6.12.xNested (14 words)STRUCT_OFFSETS_6_12
6.6.xNested (14 words)STRUCT_OFFSETS_6_6
6.1.x (vivo)Compact (10 words)STRUCT_OFFSETS_6_1
5.10.xCompact (10 words)STRUCT_OFFSETS_5_10

For 5.10/6.1 compact waiter devices, also set .kimage_text_base=0xffffffc008000000ULL.

Files

FileDescription
src/core/main.cExploit entry, W1/W2, UMH path, bootstrap, root script, SELinux policy fix
src/core/fops.cpselect route, PI write, CFI stage, compact waiter support
src/core/util.cHeap spray, KernelSnitch, slab drain, payload setup
src/core/pipe_physrw.cPipe buffer physical memory r/w
src/core/umh_root.cUMH root via workqueue injection
src/core/miniadb.cMini ADB client (TCP + RSA auth)
src/core/target.hMemory layout, struct field defaults (6.12)
src/core/runtime_struct_offsets.hPer-device struct field override
src/devices/offsets.hDevice offset tables + STRUCT_OFFSETS_* macros
tools/extract_target.pyOffset extraction from kallsyms
tools/extract_btf.pyStruct offset extraction from BTF
tools/kallsyms/End-to-end kallsyms recovery workflow

License

For authorized security research and educational purposes only.

Contributors

JoinChang

56 commits

Dev-iL

5 commits

Dream-Rainy

1 commits

iangcarroll

1 commits

JoinChang/ghostlock-oneplus

GhostLock (CVE-2026-43499) kernel exploit for Android devices with locked bootloader

339

stars

63

commits

C

primary language

Sep 8, 2026

updated

android
coloros
ghostlock
kernelsu
oneplus
root

README

GhostLock — Locked Bootloader Jailbreak

Kernel exploit for Android devices with locked bootloader. Achieves temporary root + KernelSU installation without unlocking bootloader or modifying boot image. Runtime auto-detection of kernel version with multi-device offset table.

GhostLock running on OnePlus Ace 6T with KernelSU (LKM, Jailbreak mode)

Vulnerability

CVE-2026-43499 — Futex PI (Priority Inheritance) Use-After-Free

Affects Linux kernel 5.7 ~ 7.1. Fixed in stable 6.1.175, 6.6.140, 6.12.86. Most Android devices remain unpatched as of September 2026.

The pselect6 syscall copies fd_set data onto the kernel stack. When combined with the futex PI waiter mechanism, a freed stack frame can be reclaimed as an rt_mutex_waiter structure. The rb-tree rebalance during PI chain walk then writes controlled values to arbitrary kernel addresses.

Supported Devices

Verified Working

DeviceSoCKernelGKI BranchSHIFT
OnePlus Ace 6T (PLR110)SM88456.12.38android16-50
OnePlus 15 (CPH2745/2747/2749)SM88506.12.23android16-50
Xiaomi 17 (pudding)SM88506.12.23 / 6.12.69android16-5 / android16-60
OnePlus 13 (IN2060)SM87506.6.89android15-8-2
OPPO Pad 4 ProSM87506.6.89android15-8-2

Offsets Extracted (pending device test)

DeviceSoCKernelNotes
OnePlus 15T (PLZ110)SM88456.12.38Same kernel as Ace 6T
OPPO Reno10 Pro+ (CPH2521)SM84755.10.2365.10 compact waiter, waiter word=0
Vivo X Fold3 Pro (PD2337)SM86506.1.1246.1 compact waiter, waiter word=3
Vivo T4SM86506.1.1456.1 compact waiter, waiter word=3

Not Feasible

The pselect stack overlay requires the freed rt_mutex_waiter to land within the user-controllable stack_fds region (words 0–14). Where it lands is determined by compiler PGO/LTO profiles, not the kernel version. See Stack Layout.

DeviceSoCKernelRoot Cause
OPPO Find X9 UltraSM87506.12.58PGO eliminates do_futex → waiter word=14
OnePlus 12SM86506.1.141PGO inlines do_futex → waiter word=13/19
OnePlus 13R / Ace 5SM86506.1.xOPLUS 6.1: waiter word=13
realme RMX5070SM66506.1.141OPLUS 6.1: waiter word=13
OPPO Pad 5 (OPD2502)MT68786.1.134OPLUS 6.1: waiter word=13
Motorola Edge 60 FusionMT68786.1.145Non-OPLUS 6.1: same result
iQOO Neo 10 CNSM86506.1.84do_futex frame 0xD0 → waiter word=-11
OPPO PKW1105.15.180do_futex frame 0x140 → waiter word=-29
iQOO Z9 5G5.15.178do_futex frame too large
CPH2763 (OPPO)6.1.115OPLUS 6.1: PGO inlined, waiter word=24

Note on 6.1 feasibility: OPLUS 6.1 kernels are consistently infeasible due to PGO inlining do_futex. However, some non-OPLUS 6.1 kernels (vivo) retain the standard call chain and are feasible (vivo T4, X Fold3 Pro). Feasibility must be checked per-device.

Exploit Flow

Two root paths, selected automatically based on device capabilities:

Path A: UMH Root (preferred, C ashmem devices)

Requires off_ashmem_misc_fops != 0 (C ashmem with static miscdevice in BSS).

PI write (mode=4)  →  redirect miscdevice fops to fake fops
                   →  configfs r/w → pipe physrw (1-byte precise kernel r/w)
                   →  SELinux enforcing = 0 (single byte, no policycap corruption)
                   →  UMH: inject work_struct into system_unbound_wq
                   →  root script → ksud late-load → KSU installed

Available on: OnePlus 13, OPPO Pad 4 Pro, 5.10/6.1 C ashmem devices. Not available on Rust ashmem (6.12 GKI) — heap-allocated miscdevice.

Path B: Direct PI Write (fallback)

Write 1 (mode=1)  →  SELinux enforcing = 0 (8-byte write, corrupts adjacent bytes)
Write 2 (mode=2)  →  task->cred = init_cred (uid=0, all capabilities)
Root shell         →  ksud late-load → KSU → SELinux policy fix

After W1+W2, the exploit patches the SELinux policy binary's config field (|= 0xC0000000 for ANDROID_NETLINK_ROUTE + GETNEIGH) and reloads via /sys/fs/selinux/load to restore network connectivity.

Bootstrap Mode (phone standalone)

App (seccomp)  →  Write 1 → mini-adb TCP → adb shell: full exploit → root

Auto-Boot

The boot-time launcher is provided by the separate GhostLock Anchor app.

Stack Layout Feasibility

With NFDS=320, core_sys_select allocates a 256-byte stack_fds buffer on the kernel stack:

stack_fds:  0    5    10   14 | 15   20   25   29
            ├─in─┤─out─┤─ex──┤ ├res_in┤res_out┤res_ex┤
            ◄── USER CONTROLLED ──►│◄── KERNEL ZEROED ──►

The exploit places fake waiter fields (task, lock) in the fd_set input bitmaps. For 6.12 nested waiter (14 words): max feasible waiter word = 3. For 5.10/6.1 compact waiter (10 words): max feasible waiter word = 7.

The waiter position depends on the call chain depth:

PatternCall ChainFeasible
android16-5 (6.12)sys_futex → do_futex → fwrpi✅ waiter word=2
android15-8 (6.6)sys_futex → do_futex → fwrpi✅ waiter word=2 (SHIFT=-2)
vivo 6.1sys_futex → do_futex → fwrpi✅ waiter word=3
OPLUS 6.1sys_futex → fwrpi (PGO inlined)❌ waiter word=13+
android16-6 (X9 Ultra)sys_futex → fwrpi (PGO inlined)❌ waiter word=14
5.10 OPLUSsys_futex → do_futex → fwrpi✅ waiter word=0

kernel_phys_load

All kernel writes use the linear-map alias. The bootloader picks kernel_phys_load, which varies per SoC:

SoCkernel_phys_load
SM8845 (Ace 6T, 15T)0xa8000000
SM8750 (OnePlus 13, OPPO Pad 4 Pro)0xa8000000
SM8650 (vivo T4, X Fold3 Pro)0xa8000000
SM8850 (OnePlus 15, Xiaomi 17)0xc7800000

A wrong value fails silently. Read it on a rooted unit:

su -c 'grep -i "Kernel code" /proc/iomem'   # c7810000-... → 0xc7800000

Override at runtime: KPHYS=0xc7800000 /data/local/tmp/a/e

PSELECT_SHIFT

/data/local/tmp/a/e                        # Default (shift=0)
PSELECT_SHIFT=-2 /data/local/tmp/a/e       # OnePlus 13 (6.6)

KIMAGE_TEXT_BASE

5.10 kernels use 0xffffffc008000000 (VA_BITS=39, different from 6.x default 0xffffffc080000000). This is handled automatically via the kimage_text_base field in the device offset entry.

Build

make NDK_ROOT=/path/to/android-ndk

Or directly:

NDK=/path/to/android-ndk
"$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android35-clang" \
  -O2 -Wall -Isrc/core -Isrc/devices -DTARGET_CONFIG_H=\"target.h\" \
  src/core/main.c src/core/util.c src/core/slide.c \
  src/core/fops.c src/core/pipe_physrw.c src/core/root.c \
  src/core/miniadb.c src/core/umh_root.c \
  -o ./ghostlock -fPIE -pie -pthread

Prerequisites

ksud (required for KSU installation)

GhostLock provides temporary root. KernelSU installation requires ksud which bundles kernelsu.ko for each KMI version.

MethodSteps
ReSukiSU APK (recommended)Install ReSukiSU or this fork. Bundles libksud.so.
CI releaseDownload from ReSukiSU CI

Setup

adb shell mkdir -p /data/local/tmp/a
adb push ./ghostlock /data/local/tmp/a/e
adb shell chmod 755 /data/local/tmp/a/e
adb shell /data/local/tmp/a/e

Run after boot completes and ADB is ready.

Adding New Devices

Only boot.img is needed — no root, no device access required.

1. Extract kernel and kallsyms

See kallsyms tools guide for the full workflow, or:

python tools/extract_target.py --kallsyms kallsyms.txt
python tools/extract_btf.py kernel

2. Determine feasibility

Check if do_futex is PGO-inlined by searching the kernel binary for all branch instructions targeting futex_wait_requeue_pi. If __arm64_sys_futex calls it directly (bypassing do_futex), the device is likely not feasible.

3. Create device entry

Add src/devices/<name>/offsets.h with the extracted offsets and include it in src/devices/offsets.h. Use the appropriate struct offsets macro:

KernelWaiter LayoutMacro
6.12.xNested (14 words)STRUCT_OFFSETS_6_12
6.6.xNested (14 words)STRUCT_OFFSETS_6_6
6.1.x (vivo)Compact (10 words)STRUCT_OFFSETS_6_1
5.10.xCompact (10 words)STRUCT_OFFSETS_5_10

For 5.10/6.1 compact waiter devices, also set .kimage_text_base=0xffffffc008000000ULL.

Files

FileDescription
src/core/main.cExploit entry, W1/W2, UMH path, bootstrap, root script, SELinux policy fix
src/core/fops.cpselect route, PI write, CFI stage, compact waiter support
src/core/util.cHeap spray, KernelSnitch, slab drain, payload setup
src/core/pipe_physrw.cPipe buffer physical memory r/w
src/core/umh_root.cUMH root via workqueue injection
src/core/miniadb.cMini ADB client (TCP + RSA auth)
src/core/target.hMemory layout, struct field defaults (6.12)
src/core/runtime_struct_offsets.hPer-device struct field override
src/devices/offsets.hDevice offset tables + STRUCT_OFFSETS_* macros
tools/extract_target.pyOffset extraction from kallsyms
tools/extract_btf.pyStruct offset extraction from BTF
tools/kallsyms/End-to-end kallsyms recovery workflow

License

For authorized security research and educational purposes only.

Contributors

JoinChang

56 commits

Dev-iL

5 commits

Dream-Rainy

1 commits

iangcarroll

1 commits

Languages

C

73.9%

Python

25.9%