
Lokaler Root-Exploit für CVE-2025-21479 (Adreno KGSL) auf iQOO Neo8 (SM8475) – physischer Speicher Lese-/Schreibzugriff, deaktiviert SELinux, startet Root-Shell
Lokaler Privilege-Escalation-Proof-of-Concept-Code für CVE-2025-21479, der auf die Qualcomm-Adreno-GPU (KGSL) des vivo V2301A (iQOO Neo8 / PD2301, Snapdragon 8+ Gen 1 / SM8475, Adreno A730, Android 14, GKI-Kernel 5.10) abzielt.
Im Prinzip treibt der Exploit über den KGSL-Befehl CP_SMMU_TABLE_UPDATE die SMMU der GPU an, erlangt beliebigen physischen Speicherzugriff (Lesen/Schreiben), deaktiviert daraufhin SELinux, lokalisiert Kernel-Symbole und erlangt eine Root-Shell – ganz ohne Flashen und ohne entsperrten Bootloader.
Nur für Sicherheitsforschung an Geräten, die dir selbst gehören oder für die du autorisiert bist.
and $12, 0x3) der Adreno-A730-Mikrocode (eureka_panther) ist fehlerhaft und klassifiziert Self-Draw-State (SDS)-Pakete fälschlich als RingBuffer (RB). Indem ein gefälschter CP_SMMU_TABLE_UPDATE im SDS-Block aus dem User-Mode übergeben wird, kann TTBR0 der GPU überschrieben werden, sodass die GPU-Seitentabelle auf beliebige physische Seiten zeigt – die GPU kann danach beliebigen physischen Speicher lesen und beschreiben.stabilize_rw wird das Mapping zu einer 2-MB-Self-Mapping-Region gehärtet, die schnelle Massen-Lesevorgänge unterstützt (fast_pread_bytes usw.).selinux_state.enforcing auf Null gesetzt. Danach wird das Kernel-Image von der festen physischen Basisadresse 0xA8000000 aus gedumpt (Standard-Obergrenze 43 MB) – SM8475 hat kein physisches KASLR – und die kallsyms-Strukturen werden direkt aus dem rohen Dump analysiert (Endianness wird automatisch erkannt), sodass auch virtuelles KASLR keinen Einfluss hat.prepare_kernel_cred(0) → commit_creds(), zusätzlich seccomp deaktivieren) patcht __do_sys_capset. Ein per fork erzeugter Kindprozess ruft capset() auf → wird uid 0 → stellt den überschriebenen Kernel-Code wieder her → der Kindprozess führt im Domänenkontext u:r:shell:s0 den von dir angegebenen Befehl erneut aus (Standard sh). Der Kernel-Code ist am Ende byteweise identisch mit dem Zustand vor der Ausführung.Vollständige Geräteinformationen siehe device_info.txt.
├── source/
│ ├── cheese.c # Exploit-Hauptprogramm (inkludiert die beiden folgenden Dateien)
│ ├── kallsyms_lookup.c # kallsyms-Parser für rohe Kernel-Images
│ └── adrenaline.h # KGSL-ioctl-/Befehlsdefinitionen (Autor hawkes)
├── exploit # Vorkompiliertes arm64-Binary (dynamisch gelinkt, minSdk 21,
│ # NDK r30-beta2)
├── device_info.txt # Zielgeräteinformationen zur Parameterabstimmung
└── README.md
Hinweis: cheese.c verwendet #include für kallsyms_lookup.c und adrenaline.h; beim Kompilieren müssen sich die drei Dateien im selben Verzeichnis befinden.
Jedes neuere Android-NDK (r25+) ist geeignet, Beispiel:
# Statisch linken (eigenständig, läuft auf jeder API-Stufe; größere Datei)
aarch64-linux-android34-clang -o cheese source/cheese.c -static
# Dynamisch linken (kleine Datei, nutzt die geräteeigene libc; API-Stufe muss zum Gerät passen)
aarch64-linux-android34-clang -o exploit_dyn source/cheese.c
# Für Kompatibilität mit API-21+-Geräten bitte aarch64-linux-android21-clang verwenden
adb push exploit /data/local/tmp/
adb shell
chmod +x /data/local/tmp/exploit
/data/local/tmp/exploit
Ausführliches Tutorial hier (enthält ein Ein-Klick-Tool)
Nach Erfolg erhältst du eine Shell mit uid=0, und SELinux ist in dieser Sitzung deaktiviert. Nach Abschluss der Rechteausweitung führt der Prozess argv[1..] erneut aus (Standard sh). Bei jedem Lauf gibt es einen Countdown ("pte wait 3 second") und einen 43-MB-Kernel-Lesefortschritt ("Read X / 43 MB").

$(find /data/app -type f -name "libksud.so" | grep resukisu) late-load --kmi android12-5.10
Gerätebezogene Konstanten befinden sich oben in source/cheese.c:
KERNEL_PHYS_BASE — physische Kernel-Basisadresse (Standard 0xA8000000, SM8475)IDMAP_PG_DIR_OFFSET — idmap_pg_dir-Offset (Standard 0x245a000)selinux_state_offset — Offset zum Auffinden von selinux_state (Standard 0x729030, tatsächliche physische Adresse = physische Adresse von idmap_pg_dir + dieser Offset)gPhyAddrs[] — nacheinander versuchte Kandidaten-BasisadressenDEFAULT_KERNEL_SIZE_MB — Fallback-Wert / Obergrenze für den Kernel-LesevorgangVoraussetzungen: Adreno-A730/A7xx-GPUs (betroffene Firmware), ein Kernel, dessen kallsyms aus dem physischen Dump auflösbar ist, und kein physisches KASLR.
can't get GPU r/w — GPU-Initialisierung fehlgeschlagen; zuerst warten, wenn es weiterhin nicht klappt, CHEESE_ATTEMPT=1..n versuchen oder CHEESE_PHYADDR manuell angeben; für Debugging eines einzelnen Versuchs CHEESE_NO_RETRY=1 hinzufügen.CHEESE_READ_FULL_KERNEL=1 das vollständige Image lesen (zusammen mit CHEESE_KALLSYMS_MAX_MB das Limit erhöhen), danach CHEESE_FORCE_MANUAL_PATCHFINDER=1 in Betracht ziehen.cheese-Exploits, auf dem dieses Tool basiertadrenaline.h von 2020, liefert die ioctl-SchnittstellendefinitionenBasiert auf zhuowei/cheese (Original-Repository ohne Lizenz). Vollständige technische Details siehe Schwachstellen-Meldung.
Dieses Repository dient ausschließlich Bildungs- und Sicherheitsforschungszwecken; bitte verwende es nur auf Geräten, die dir selbst gehören oder für die du autorisiert bist. Der Autor übernimmt keine Verantwortung für Missbrauch. Die Verwendung dieses Tools kann zum Erlöschen der Garantie oder zu Verstößen gegen entsprechende Nutzungsbedingungen führen.
| Modell | vivo V2301A (iQOO Neo8, Codename PD2301, Mainboard taro) |
| SoC | SM8475 (Snapdragon 8+ Gen 1) + Adreno 730 |
| Android | 14 (UP1A.231005.007, SDK 34, qssi-user) |
| Kernel | 5.10.198-gki-gfb2f5774872c |
| SELinux | Enforcing (wird zur Laufzeit von diesem Tool deaktiviert) |
| Variable | Funktion |
|---|
CHEESE_PHYADDR=<phys> | Erzwingt die physische Kernel-Basisadresse (überspringt die eingebaute gPhyAddrs-Erkundungstabelle). |
CHEESE_ATTEMPT=<n> | Verwendet gPhyAddrs[n] statt gPhyAddrs[0]; nach fehlgeschlagener GPU-Initialisierung erhöht maybe_retry den Versuch automatisch. |
CHEESE_SWAPPER_PG_DIR_OFF=<off> | Überspringt den swapper_pg_dir-Scan und verwendet direkt diesen absoluten Offset. |
CHEESE_READ_FULL_KERNEL=1 | Liest das vollständige Kernel-Image (Standard-Leselimit ist DEFAULT_KERNEL_SIZE_MB = 43 MB). |
CHEESE_KALLSYMS_MAX_MB=<MB> | Überschreibt das kallsyms-Leselimit. |
CHEESE_FORCE_MANUAL_PATCHFINDER=1 | Erzwingt die manuelle Patchfinder-Fallback-Option (init_cred, prepare_kernel_cred). |
CHEESE_SKIP_GPU=1 | Überspringt die GPU-Lese-/Schreibphase (nur zum Debuggen). |
CHEESE_NO_RETRY=1 | Beendet sich bei Fehlschlag sofort, ohne mit anderer Nummer erneut zu versuchen. |