Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2026-43499-pmg110-root — Android-LPE-Exploit für CVE-2026-43499 gegen das OPPO PMG110 (Kernel 6.6). Nutzt futex PI UAF aus, um Root-Rechte zu erlangen und über LD_PRELOAD einen su-Daemon zu installieren. | Kitploit
Tools/GitHubGitHub/soralis0912/cve-2026-43499-pmg110-root
Android-SicherheitPrivilege EscalationSchwachstellenanalyseExploitationPost-ExploitationPenetrationstestsMobile SicherheitRed TeamingPayload-Entwicklung

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
Binary-Exploitation
GitHubsoralis0912/cve-2026-43499-pmg110-root

CVE-2026-43499-pmg110-root

Android-LPE-Exploit für CVE-2026-43499 gegen das OPPO PMG110 (Kernel 6.6). Nutzt futex PI UAF aus, um Root-Rechte zu erlangen und über LD_PRELOAD einen su-Daemon zu installieren.

Repository anzeigen
214vor 1 MonatNoch nicht geprüft

pmg110-root

CVE-2026-43499 (futex PI rt_mutex_waiter use-after-free) lokale Privilegieneskalation, portiert auf das OPPO PMG110 / K15 Pro+ — MediaTek MT6991, ColorOS 16.

Eine Datei übertragen, ausgeführt über LD_PRELOAD:

root@kitploit:~
adb push out/preload-pmg110-16.0.9.400.so /data/local/tmp/preload.so
adb shell chmod 644 /data/local/tmp/preload.so
adb shell LD_PRELOAD=/data/local/tmp/preload.so /system/bin/true

Bei Erfolg bleibt ein persistentes su zurück:

root@kitploit:~
adb shell /data/local/tmp/su -c id      # uid=0(root)

Auf dem Gerät verifiziert (2026-07-27): uid=0 in etwa 35 Sekunden bei einem reinen Lauf ohne Umgebungsüberschreibungen, und su antwortet anschließend aus einer gewöhnlichen unprivilegierten adb shell:

root@kitploit:~
$ adb shell "/data/local/tmp/su -c 'echo 0 > /proc/sys/kernel/kptr_restrict'"
$ adb shell "/data/local/tmp/su -c 'grep -w init_task /proc/kallsyms'"
ffffffe89033e780 D init_task

Der Exploit und die su-Installation sind beide auf diesem Gerät verifiziert. Was diese gerootete Shell anschließend zurücklas, bestätigt außerdem P0_KERNEL_PHYS_LOAD, die Symbol-Offsets und KS_MTE_TAGGED=0 unabhängig vom Exploit — siehe targets/pmg110-16.0.9.400/NOTES.md.

Was es tut und was es nicht tut

Es führt Write 1 (SELinux permissive) und Write 2 (cred → init_cred) aus, bringt einen Kindprozess auf uid=0 und installiert von dort den eingebetteten su-Daemon.

  • weiterhin nur eine Datei übertragen. su ist kein zweites Artefakt: su_daemon.c wird als eigenständiges aarch64-PIE gebaut und per .incbin in das .rodata der Bibliothek eingebettet, sodass es in preload.so mitfährt und zur Laufzeit wieder herausgeschrieben wird. Der Weg von warhol-root, unverändert.
  • kein Root-Skript, kein ksud, kein KernelSU
  • der aufrufende Prozess bleibt unprivilegiert — er erhält Root, indem er den Daemon fragt; das ist dasselbe, was du anschließend aus der Shell tun wirst
  • SELinux bleibt permissive, so wie es warhol-root hinterlässt: Der Daemon muss unprivilegierte Clients über seinen Socket bedienen. Neustart, um enforcing wiederherzustellen.

su wird an drei Orten installiert, weil einer davon derjenige ist, den du tatsächlich erreichst:

Der Daemon lauscht auf /data/local/tmp/temp_su.sock und protokolliert nach /data/local/tmp/su_daemon.log. Root bleibt über einen Neustart hinweg nicht bestehen — führe die LD_PRELOAD-Zeile nach jedem Boot erneut aus.

Für die KernelSU-Installation verwende stattdessen den /data/local/tmp/a/e-Weg in ghostlock-oneplus.

Beziehung zu warhol-root

Alles außer dem Exploit-Kern stammt von warhol-root, übernommen statt neu erfunden:

  • das Layout — gerätespezifische Header unter targets/<device>/, zur Build-Zeit nach source/src/ gestagt, sodass ein Wechsel von DEVICE niemals die Header des vorherigen Geräts zurücklassen kann
  • der Build — die Toolchain-Auswahl des source/Makefile (NDK, falls vorhanden, sonst Host-clang gegen den NDK-Sysroot) sowie die zweistufige Embed-Regel, die vor dem Linken der .so das build/embed/su_daemon_aarch64_pie erzeugt
  • der su-Weg — su_daemon.c und su_blob.S sind byte-identisch mit denen von warhol-root, und su_install.c ist dessen preload.c-Installer

Der Exploit-Kern stammt nicht von warhol-root. warhol-root ist popsicle, das an GKI 6.12 / android16 gebunden ist und dessen generate_target.py jedes andere Banner ablehnt. PMG110 läuft auf 6.6 / android15, daher ist der Kern hier der ghostlock-6.6-Baum — selbst ein Abkömmling desselben Codes (kernelsnitch/utils.h und timeutils.h sind zwischen den beiden Repos byte-identisch), weiterentwickelt.

Jede Zeile des eigentlichen Exploits — Write 1, Write 2, KernelSnitch, der pselect-Weg — ist in beiden Bäumen derselbe Code.

Woher die su-Installation aufgerufen wird

Das ist der eine strukturelle Unterschied, und er ist dadurch erzwungen, dass die beiden Bäume auf unterschiedliche Weise Root erlangen.

warhol-root rootet den Exploit-Prozess selbst und ruft install_embedded_su() daher direkt aus run_direct_root() auf. Hier tauscht Write 2 den cred-Zeiger eines geforkten Kindes, und der Parent bleibt der unprivilegierte Aufrufer, sodass das Kind in child_main() der einzige Kontext ist, der die Installation durchführen kann — dort läuft sie.

Beide Bäume tragen denselben schwachen install_embedded_su()-Stub in util.c, der ENOSYS zurückgibt; die starke Definition bereitzustellen, ist das, was den Weg aktiviert. Das ist wissenswert, denn ein Build, der su_install.c irgendwie weglässt, linkt und läuft weiterhin — er meldet nur su=0/38 und installiert nichts.

Build

root@kitploit:~
make                      # = make preload -> out/preload-<DEVICE>.so
make DEVICE=<name>        # use targets/<name>/
make devices              # list available DEVICE values
make info                 # show the selected target and the resolved toolchain

Die Toolchain wird von selbst gefunden: zuerst ANDROID_NDK_HOME / ANDROID_NDK_ROOT, dann die üblichen NDK-Installationsorte für Linux und macOS, und falls all das fehlschlägt, Host-clang gegen den NDK-Sysroot. Setze ANDROID_NDK_HOME nur, um die Suche zu überschreiben. make info gibt aus, was gewählt wurde.

Der Build besteht aus zwei Stufen, was der Teil ist, den man kennen sollte:

  1. su_daemon.c → build/embed/su_daemon_aarch64_pie, ein eigenständiges aarch64-PIE
  2. su_blob.S bettet diese Binärdatei per .incbin in .rodata ein, und das Ganze wird zu der einen preload.so gelinkt

Ein make clean und ein Rebuild sind also der einzige Weg, das eingebettete su zu ändern — su_daemon.c allein zu bearbeiten genügt, die Abhängigkeit ist deklariert, aber der Blob ist ein Build-Artefakt und wird nicht versioniert.

targets/<device>/{target.h,device_offsets.h} werden bei jedem Build erneut nach source/src/ gestagt, sodass ein veralteter Header eines anderen Geräts nicht stillschweigend übernommen werden kann.

out/*.so wird nicht versioniert (gleiche Konvention wie warhol-root) — klonen und make.

Die .so wird mit -fvisibility=hidden gebaut und exportiert null Symbole. Eine LD_PRELOAD-Bibliothek hat bei der Symbolauflösung für den gesamten Prozess Vorrang, sodass alles, was sie exportiert, ein gleichnamiges Symbol in der Host-Binary oder in libc überdecken könnte. Dieses Flag steuert nur die C-Codegenerierung, daher markiert su_blob.S seine beiden Symbole von Hand als .hidden — ohne diese Zeilen wären die Blob-Grenzen das Einzige, was die Bibliothek noch exportierte.

Umgebungsvariablen

Keine davon war beim verifizierten Lauf nötig.

Das Log lesen

root@kitploit:~
[*] futex_hashsize 2048 (8 possible CPUs)
[*] ks collisions=3/3 baseline=8 threshold=10x (80) accepted=[1244..1597] slowest_rejected=N
[+] child uid = 0
[+] embedded su wrote 15304 bytes to /apex/com.android.virt/bin/su
[+] embedded su daemon ready pid=NNNN socket=/data/local/tmp/temp_su.sock daemon=/apex/com.android.virt/bin/su
[+] embedded su install ok=1 errno=0 daemon=NNNN
[+] su ready: /data/local/tmp/su and /apex/com.android.virt/bin/su
[+] ghostlock preload verdict: EXPLOIT OK

child uid = 0 bedeutet, dass der Exploit erfolgreich war; alles danach ist die Installation. Beides wird absichtlich getrennt gemeldet, ebenso das Urteil:

Die mittlere ist die Unterscheidung, die sich lohnt: Sie besagt, dass die Offsets in target.h für diesen Build stimmen und das Problem irgendwo in der Installation liegt, was eine völlig andere Sache zum Debuggen ist. su=0/38 (ENOSYS) darin bedeutet konkret, dass der schwache Stub gelinkt wurde.

mm_struct leak failed, gefolgt von prepare_kernel_page retry N/24, ist kein Fehler. Es ist Schleifenfortschritt, und der erfolgreiche Lauf zeigt es ebenfalls. Nichts ist fehlgeschlagen, bis 24 Versuche erschöpft sind und prepare_kernel_page timeout erscheint. Ebenso ist probing cfi ... expected=9 mit child uid = 2000 eine verfehlte Runde von zehn.

Beurteile einen Lauf nicht anhand eines abgeschnittenen Logs — dieser Fehler kostete hier eine ganze Runde Fehldiagnose.

Auch ein komplett fehlgeschlagener Lauf ist normal. Die pselect-Race ist nicht zu 100 %: Ein Lauf kann sie fünfmal hintereinander verlieren und in Write 1 failed enden, und der nächste Lauf gewinnt sie auf Anhieb mit ret=9. Auf diesem Gerät beobachtet. ret=4 expected=9 sieht so aus, wenn man die Race verliert, nicht wie ein falsches target.h — ein Fehlschlag ist kein Grund, die Offsets neu herzuleiten. Führe es erneut aus.

Dateien

Lizenz

Nur für autorisierte Sicherheitsforschung und zu Bildungszwecken.

Tool herunterladen
GerätOPPO PMG110 / K15 Pro+ / OP61E5L1
SoCMediaTek MT6991 (Dimensity 9500s)
Kernel6.6.118-android15-8-g93e223c276e7-abogki500782043-4k (GKI, 4K-Seiten)
BuildColorOS 16 / PMG110_16.0.9.400(CN01) — gleiche Kernel-Bytes wie 16.0.8.300
BugCVE-2026-43499, nicht gepatcht in diesem Image (per Disassemblierung belegt, nicht durch die Version)
PfadWarum
/apex/com.android.virt/bin/suauf einem tmpfs, das über dieses Verzeichnis gemountet ist; auf dem PATH einer Root-Shell
/data/local/tmp/suaus einer einfachen adb shell ohne PATH-Spielereien erreichbar
/apex/com.android.virt/bin/su im Mount-Namespace von adbdüber setns installiert, sodass eine neue adb shell es sieht
DateiBeziehung
util.c slide.c fops.c pipe.c root.c miniadb.c common.h offset.h kernelsnitch/*von ghostlock, byte-identisch
su_daemon.c su_blob.Svon warhol-root, byte-identisch (su_blob.S fügt zwei .hidden-Zeilen hinzu — siehe Build)
su_install.cwarhol-roots preload.c-Installer, in eine eigene Datei verschoben, weil das preload.c dieses Baums bereits eine andere Aufgabe hat
main.cvon ghostlock, plus der su-Aufruf im gerooteten Kind und die Ergebnisberichterstattung
preload.cnur hier — der Konstruktor und das Dual-Sink-Log
offsets.hnur die Struct-Definition; der Eintrag wird aus targets/<device>/device_offsets.h gestagt
VariableEffekt
GHOSTLOCK_LOGLog-Ziel (Standard /data/local/tmp/.ghostlock.log); die Ausgabe geht an stdout und in die Datei
GHOSTLOCK_KS_VERBOSE=1die Kollisionsadressen von KernelSnitch und die Sweep-Bereiche ausgeben
GHOSTLOCK_KS_THRESHOLD=<n>den Kollisions-Schwellenmultiplikator überschreiben
GHOSTLOCK_MTE=1auch Kernel-Pointer-Tags durchsuchen (15x langsamer)
GHOSTLOCK_PHYS_LOAD=0x...die physische Ladeadresse des Kernels überschreiben
PSELECT_SHIFT=<n>den Stack-Overlay-Shift überschreiben (ersetzt, addiert nicht)
UrteilBedeutung
EXPLOIT OKRoot, und su antwortet
EXPLOIT OK, SU INSTALL FAILEDWrite 1 und Write 2 sind gelandet; nur die Installation ist schiefgelaufen
EXPLOIT FAILEDdie Writes sind nicht gelandet
ABORTEDder Lauf starb, bevor er melden konnte — lies die letzte [!]-Zeile
PfadInhalt
source/src/preload.cKonstruktor: führt den Exploit aus, meldet, stoppt
source/src/main.cder Exploit selbst (Write 1 / Write 2)
source/src/su_daemon.cdie su-Binärdatei — eigenständig als aarch64-PIE gebaut, nicht in die .so gelinkt
source/src/su_blob.Sbettet dieses PIE per .incbin in das .rodata der .so ein
source/src/su_install.cschreibt den Blob zurück, startet den Daemon, prüft ihn
source/src/target.hStaging-Ziel (gitignoriert)
targets/<device>/target.hKompilierzeit-Layout: Struct-Offsets, Physmap-Konstanten, Slab- und Futex-Formen
targets/<device>/device_offsets.hglobale Symbol-Offsets aus kallsyms
tools/extract_device.pyboot.img → Offsets, BTF-Struct-Felder, pselect-Overlay-Ergebnis
tools/preloader_memlayout.pyMediaTek-Preloader → P0_KERNEL_PHYS_LOAD
tools/qemu_verify.pybootet den Kernel unter QEMU: misst das Stack-Overlay, prüft die Stabilität der Linearmap
tools/device_probe.shPre-Flight-Check aus einer unprivilegierten adb-Shell