
Sfrutta CVE-2026-43499 sui dispositivi Android GKI 6.12: lettura/scrittura arbitraria del kernel deterministica, bypass di KASLR e root completo tramite payload LD_PRELOAD.
Un exploit del kernel Linux per la linea Android GKI 6.12 (dispositivi Samsung e Pixel) che mira a CVE-2026-43499: un rollback difettoso di remove_waiter() in rt_mutex_start_proxy_lock() che usa current invece di waiter::task, lasciando il pi_blocked_on di un waiter che punta al suo rt_mutex_waiter nello stack del kernel (poi rimosso dallo stack). Combinato con una sovrascrittura dello stack del fd_set tramite pselect() e una falsa camminata dell'albero rb_tree guidata da sched_setattr da parte di un consumer, produce una primitiva deterministica di scrittura nel kernel e root completo.
L'exploit viene eseguito come libreria condivisa LD_PRELOAD (preload.so) e installa un demone su di root e uno sfondo come artefatti post-root.
Stato: sviluppo attivo. Gli offset di profondità dello stack specifici per target (spostamenti di word
PSELECT_*) e il comportamento dello slide variano da dispositivo a dispositivo e sono ancora in fase di verifica su dispositivo. Il target m1q (ZF1) è l'attuale obiettivo di bring-up.
Quando FUTEX_CMP_REQUEUE_PI riaccoda un waiter e la camminata della catena di rilevamento deadlock del requeue restituisce -EDEADLK, __rt_mutex_start_proxy_lock() esegue il rollback tramite remove_waiter() (rtmutex.c:1535). Il rollback rimuove correttamente il waiter dall'albero di attesa, ma azzera il pi_blocked_on del chiamante del requeue invece di waiter->task->pi_blocked_on. Il pi_blocked_on del WAITER rimane pendente sul suo rt_mutex_waiter nello stack, che viene rimosso quando il futex va in timeout.
Dopo il timeout, la regione dello stack del kernel del waiter viene riusata: core_sys_select() copia i tre fd_set in quel buffer di stack (il percorso stack nfds < 344 su ZF1). Un array di word fd_set appositamente costruito re-materializza un falso rt_mutex_waiter / falso task / falso rt_mutex (con una radice rb_tree i cui puntatori sono controllati). Un thread consumer chiama quindi sched_setattr_tid(waiter) → rt_mutex_adjust_pi() → rb_erase_cached, il che produce una scrittura di un valore controllato a un indirizzo arbitrario.
L'intera primitiva richiede il ciclo della catena PI: l'owner detiene f_pi_target e inoltre è bloccato su f_pi_chain (detenuto dal waiter), quindi la camminata della catena incontra owner → chain → waiter → target → owner e fallisce con -EDEADLK. Rimuovendo il ciclo, il requeue restituisce successo senza alcun effetto sul kernel.
sched_blocked_reason (principale su m1q): il PC di ritorno salvato di un kworker bloccato (stack_trace_save_tsk) viene letto dal ring buffer e confrontato con l'offset worker_thread compilato nel kernel.SLIDE_LOGGERS_0_1 nei dati sysctl di boot_id tramite un alias della mappa lineare; il valore leakato ricostruisce stext.mm_struct e usa una collisione dell'hash dei futex per individuare un mm_struct nell'heap, leakando l'indirizzo di una pagina dell'heap del kernel usato come base per lo spray degli oggetti fittizi.configfs_bin_write_iter nello slot write_iter (+0x30) delle file_operations statiche di ashmem in .data. L'apertura di ashmem produce quindi lettura/scrittura arbitraria del kernel attraverso il percorso binary-fs di configfs.Fase opzionale STAGE3=1 che crea un figlio bridge tramite fork, scambia il suo mm->pgd con una tabella di pagine fittizia predisposta (3 livelli: PGD→L1→L2, foglia RX loop + foglia stack RW) e lascia che il figlio esegua un blob assembly solo-registri che applica la patch alle proprie cred attraverso una finestra di scansione fisica di 2MB — R/W fisico su tutta la RAM senza le primitive configfs/pipe. Attualmente collegato solo al target m1q e non è stato eseguito su dispositivo.
41 target in src/targets/<codename>-<build>/, ciascuno dei quali richiede almeno un target.h (offset dei simboli del kernel, KIMAGE_TEXT_BASE, base della direct map, offset delle struct). I target Pixel (comet, tokay, tegu, caiman, komodo, frankel, mustang, rango, stallion, blazer) sovrascrivono le sorgenti condivise; i target Samsung (m1q-*) aggiungono logica specifica del dispositivo.
make list-projects # full list
I layout delle struct del kernel differiscono da dispositivo a dispositivo: gli offset devono essere verificati contro il binario effettivo del kernel / kallsyms per ciascun target.
Output: build/<PROJECT>/bin/preload.so (libreria condivisa caricata tramite LD_PRELOAD).
# Default project
CC=clang make
# Specific device target (default: blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006
# Without CC=clang: uses NDK if found ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT), otherwise host clang + Android sysroot
make PROJECT=m1q-BP4A.251205.006
# Show build configuration
make info
# Clean
make clean
La compilazione incorpora il binario PIE su_daemon (src/su_daemon.c, compilato in build/embed/su_daemon_aarch64_pie) e assets/wallpaper.webp in preload.so tramite src/su_blob.S / src/wallpaper_blob.S.
Carica gli output della compilazione su /data/local/tmp, quindi esegui l'exploit con LD_PRELOAD. Esegui SENZA tee su un target live in bring-up — tee bufferizza il proprio stdio e perde la coda del log in caso di kernel panic (lo stdout dell'exploit è unbuffered, quindi reindirizzare direttamente su un file preserva ogni riga):
adb push build/m1q-BP4A.251205.006/bin/preload.so /data/local/tmp/preload.so
adb push build/embed/su_daemon_aarch64_pie /data/local/tmp/su_daemon_aarch64_pie
adb shell "chmod 755 /data/local/tmp/preload.so /data/local/tmp/su_daemon_aarch64_pie"
adb shell "LD_PRELOAD=/data/local/tmp/preload.so \
/data/local/tmp/su_daemon_aarch64_pie \
> /data/local/tmp/output.log 2>&1"
Al successo il demone rimane in ascolto su /data/local/tmp/temp_su.sock e su viene installato in /apex/com.android.virt/bin.
exploit/
Makefile
assets/ # wallpaper.webp (embedded post-root artifact)
src/
main.c # run_exploit() entry point (shared)
preload.c # LD_PRELOAD constructor, su/wallpaper install
util.c # addr translation, page prep, configfs helpers
pipe.c # pipe-based physical read/write
root.c # cred patching / task walk
slide.c # KASLR slide (tracefs + boot_id routes)
fops.c # pselect corruption + fops manipulation
faketables.c # stage-3 fake page-table builders (3-level)
stage3.c # pgd-swap bridge child (no-op unless configured)
stage3_loop.S, stage3_poll.S # bridge blob + enter trampoline
su_daemon.c # root daemon (compiled as separate PIE)
su_blob.S, wallpaper_blob.S # embedded binaries
offset.h # pulls per-target target.h via -DTARGET_CONFIG_H
kernelsnitch/ # mm_struct futex-hash leak library
targets/<codename>-<build>/ # per-device target.h + optional overrides
poc/poc.c # standalone POC (separate from the exploit)
I target sovrascrivono le sorgenti condivise tramite il meccanismo pick_src del Makefile: se src/targets/<PROJECT>/<file>.c esiste, viene usato al posto della sorgente condivisa src/<file>.c; altrimenti viene usata la versione condivisa.
-EDEADLK (errno 35). Un ciclo mancante si manifesta come success=1 senza alcun effetto sul kernel.PSELECT_WAITER_WORD_SHIFT deve essere 0 (profondità dello stack ZF1 verificate dall'ELF). Gli altri target hanno ancora default 1 — verifica per dispositivo prima di fidartene.p0_data_alias() usa DIRECT_MAP_BASE (la mappa lineare), che su ZF1 differisce da P0_PAGE_OFFSET. Gli alias dei dati per gli oggetti del kernel dereferenziati devono usare la mappa lineare.pr_error() è fatale (exit(-1)), a differenza delle macro warning/info.SLIDE_LOGGERS_0_1, non SLIDE_NFULNL_LOGGER; la word è un alias della mappa lineare non randomizzato, quindi il percorso boot_id è indipendente dallo slide (usalo solo a slide=0 — esegui prima il leak tracefs).Questo è codice di exploit per dispositivi Android ed è destinato alla ricerca sulla sicurezza, al test di vulnerabilità autorizzato e al lavoro difensivo su hardware di tua proprietà o per cui hai il permesso esplicito di test. L'uso improprio su dispositivi che non possiedi è illegale. L'autore non è responsabile per dispositivi brickati, kernel brickati o garanzie annullate.
preload.c installa il demone su incorporato (montato via tmpfs in /apex/com.android.virt/bin, oltre a varianti nello namespace di adbd e locali) e sostituisce lo sfondo.| Variabile | Default | Scopo |
|---|
PSELECT_ROUTE_SHIFT | compile-time | Shift di word A/B del fd_set per il percorso principale (m1q) |
SLIDE_SHIFT | compile-time | Shift di word A/B del fd_set per il percorso slide |
PSELECT_ROUTE_DELAY_USEC | 50000 | Ritardo del consumer prima di sched_setattr (deve essere > 0) |
SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE | — | Timing/pinning del percorso slide |
SKIP_SLIDE | 0 | Usa il fallback della direct map (slide = 0) |
SLIDE_ONLY | 0 | Esegue solo lo slide KASLR ed esce |
SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF | — | Override degli offset p0-alias / boot_id dello slide |
KSNITCH_COLLISIONS | 4 | Numero di collisioni di KernelSnitch |
STAGE3 | 0 | Abilita la fase del figlio bridge pgd-swap (m1q) |
STAGE3_DRYRUN | 0 | Prepara/verifica le tabelle e poi interrompe prima dello swap |
PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT | — | Regolazioni del layout dello spray/fd-set |
TMP_PAGE_* / TMP_UNAME_* | — | Percorso sperimentale tmp_page (m1q) |