Pixel BigWave BIGO timeout race UAF → 2KB kernel write from mediacodec

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Résumé

  • Depuis le contexte SELinux-confined mediacodec, /dev/bigwave (accélérateur matériel Pixel AV1) est accessible. Un backlog de jobs fait que BIGO_IOCX_PROCESS atteint son 16s wait_for_completion_timeout() et retourne alors que le thread worker désenfile simultanément la même structure inline job.
  • Fermer le FD libère immédiatement struct bigo_inst (qui embed struct bigo_job). Le worker reconstruit inst = container_of(job, ...) et utilise ensuite des champs libérés tels que job->regs dans bigo_run_job(), ce qui mène à un Use-After-Free on the inline job/inst.
  • bigo_pull_regs(core, job->regs) effectue memcpy_fromio(regs, core->base, core->regs_size). En récupérant le slab libéré et en écrasant job->regs, un attaquant obtient un écriture arbitraire noyau d’environ ~2144 octets vers une adresse choisie, avec un contrôle partiel des octets en pré-programmant les valeurs des registres avant le timeout.
  • Traqué comme CVE-2025-36934 ; corrigé dans les builds 2026-01-05 Pixel/2025-12-01 ASB.

Attack surface mapping (SELinux → /dev reachability)

  • Utilisez des outils comme DriverCartographer pour énumérer les device nodes accessibles depuis un domaine SELinux donné. Malgré la politique contrainte de mediacodec (les software decoders doivent rester dans un contexte isolé), /dev/bigwave restait accessible, exposant une large surface d’attaque au code post-media-RCE.

Vulnérabilité : BIGO_IOCX_PROCESS timeout vs worker

  • Flux : ioctl copie le buffer de registres utilisateur dans job->regs, queue le job inline, puis appelle wait_for_completion_timeout(..., 16s). En cas de timeout il tente de désenfiler/annuler et retourne en userspace.
  • Pendant ce temps, bigo_worker_thread peut avoir justement désenfilé le même job :
inst = container_of(job, struct bigo_inst, job);
bigo_push_regs(core, job->regs);
...
bigo_pull_regs(core, job->regs);   // memcpy_fromio(regs, core->base, core->regs_size)
*(u32 *)(job->regs + BIGO_REG_STAT) = status;
  • Si userspace ferme le FD après le timeout, inst/job sont libérés tandis que le worker continue de les utiliser → UAF. Aucune synchronisation ne lie la durée de vie du FD au pointeur job du worker thread.

Plan d’exploitation

  1. Backlog + timeout : Mettez en file d’attente suffisamment de jobs pour retarder le worker, puis émettez BIGO_IOCX_PROCESS et laissez l’exécution emprunter le chemin de timeout à 16s.
  2. Free while in use : Dès que l’ioctl revient, appelez close(fd) pour libérer inst/job alors que le worker exécute encore le job retiré de la file.
  3. Reclaim + pointer control : Spray reclaimers (e.g., Unix domain socket message allocations) pour occuper la case du slab libérée et écraser le job inline, en particulier job->regs.
  4. Arbitrary write : Quand bigo_pull_regs() s’exécute, memcpy_fromio() écrit core->regs_size (~2144 bytes) depuis le MMIO vers l’adresse fournie par l’attaquant dans job->regs, produisant un large write-what-where sans KASLR leak.
  5. Data shaping : Parce que les registres sont d’abord programmés depuis des données utilisateur (bigo_push_regs), configurez-les pour que le hardware n’exécute pas, en gardant l’image des registres recopiés proche des octets contrôlés par l’attaquant.

Squelette minimal de PoC (blocking backlog + reclaim)

int fd = open("/dev/bigwave", O_RDWR);
for (int i = 0; i < 64; i++) submit_job(fd, regs_buf);   // fill worker queue
submit_job(fd, regs_buf);                                // victim job
auto t0 = now();
while (now() - t0 < 17000ms) sched_yield();              // hit 16s timeout
close(fd);                                               // free inst/job
spray_uds_msgs(payload_pointing_to_target, spray_count); // reclaim slab
sleep(1);                                                // let worker memcpy_fromio
  • regs_buf doit préconfigurer BigWave en état inactif (p. ex., définir des bits de contrôle pour ignorer l’exécution) afin que l’image des registres recopiée reste déterministe.

Points à retenir pour les réviseurs de pilotes

  • Les structs de job inline par FD, enfilés vers des workers asynchrones, doivent conserver des références qui survivent aux chemins de timeout/annulation ; la fermeture d’un FD doit se synchroniser avec la consommation par les workers.
  • Tout helper de copie MMIO (memcpy_fromio/memcpy_toio) qui utilise des pointeurs de buffer provenant de jobs doit être validé ou dupliqué avant leur mise en file pour éviter des primitives UAF→write.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks