
Exploit de preuve de concept pour CVE-2023-20938, une use-after-free dans le pilote binder Android. Permet d'obtenir un shell root avec SELinux permissive sur les builds Cuttlefish AVD vulnérables.
Preuve de concept d'escalade de privilèges locale pour CVE-2023-20938, un use-after-free dans le pilote Android binder. Depuis un shell non privilégié, elle obtient uid 0 et abaisse SELinux en mode permissif, puis lance un shell root.
Cible pour cette compilation : Android Cuttlefish / Goldfish AVD, x86_64, noyau 5.10.107.
⚠️ Utilisation réservée à la recherche / éducation uniquement. Exécutez ceci uniquement sur vos propres appareils de test et émulateurs. Le bug est corrigé dans Android actuel ; ce PoC cible une ancienne version de test délibérément vulnérable. Aucune garantie.

Enregistré avec asciinema et converti en GIF avec
agg:
drivers/android/binder.c, binder_transaction_buffer_release()binder_transaction_buffer_release() se fie à un offsets_size fourni par l'attaquant.
Envoyer une transaction dont offsets_size est un octet trop court par rapport à une
entrée complète (sizeof(offset) - 1 == 7) amène le chemin de libération à mal analyser
le flat_binder_object final et à décrémenter excessivement le refcount d'un
binder_node, le libérant alors qu'une référence pendante est toujours active.
L'exploit enchaîne une fuite cross-cache en une lecture arbitraire et une écriture
contrôlée, puis réécrit les credentials. Étape par étape (voir main() dans poc_cf.c):
B. Cross-cache leak
free a wave of binder_nodes (kmalloc-128) -> page returns to the buddy
allocator -> reclaim the page with eventpoll epitems (eventpoll_epi cache,
0x80-byte objects). A dangling binder transaction now reads kernel
pointers out of an epitem, leaking a struct file and its epitem address.
C. Arbitrary read
re-trigger the UAF and use a sprayed fake binder_node to turn binder's
hlist_del unlink into a controlled *(where) = what write. Two writes
redirect a victim file's f_inode at a fake inode overlaid on the epitem
and mark it S_PRIVATE. FIGETBSZ on the file then returns
*(epitem.event.data + 24): a steerable 4-byte read.
D/E/F. Privilege escalation
defeat KASLR (leak eventfd_fops via f_op), walk init_task's task list to
the launcher's cred, zero its uid/gid set and seccomp.mode, then zero
selinux_state to drop SELinux to permissive.
G. A pre-forked, eventfd-free launcher execs a root shell.
Deux processus non privilégiés doivent partager un nœud binder pour déclencher le UAF ;
via le ITokenManager de hwservicemanager (createToken / get-par-token).
clang -static -Wall -Wextra -o poc_cf poc_cf.c -lpthread
binder.h doit se trouver à côté de poc_cf.c. La compilation est volontairement
statique pour que le binaire unique puisse s'exécuter sur la cible sans chaîne d'outils.
Depuis le répertoire du paquet hôte Cuttlefish, lancez l'AVD avec le noyau et l'initramfs correspondants :
HOME=$PWD ./bin/launch_cvd \
--kernel_path=bzImage \
--initramfs_path=initramfs.img
adb push poc_cf /data/local/tmp/
adb shell /data/local/tmp/poc_cf
Une exécution réussie parcourt les étapes et se termine par un shell root :
L'exploit est probabiliste. Chaque étape réessaie en interne.
Que vous voyiez un échec propre ou un panic de l'invité, réexécutez simplement
(le /data de l'AVD est effacé au redémarrage, donc repoussez le binaire après un panic).
Si le taux de récupération est faible sur une cible plus lente ou plus chargée,
élargissez les temps d'attente ou augmentez le nombre de sprays (les #define en haut
de poc_cf.c) :
Les décalages de structures et la base de symboles non randomisée (*_NOKASLR) sont
spécifiques à cette compilation exacte 5.10.107 et ne fonctionneront pas ailleurs.
Pour porter sur un noyau différent, mettez à jour le bloc de décalages et le bloc de
symboles près du haut de poc_cf.c avec les valeurs du vmlinux correspondant. Je
l'ai essayé sur Pixel 7 après avoir extrait les décalages.
| Macro | Défaut | Signification |
|---|
SETTLE_US | 5000 | pause après la libération du nœud, avant le spray |
FAKE_SPRAY_COUNT | 8 | sendmsg()s bloqués en compétition pour récupérer l'emplacement |
FAKE_WAIT_US | 10000 | pause après la libération des sprays, avant le déliage |
PRESKIP_US | 1000 | pause après le premier sendmsg sacrificiel |
KW_RETRY_COUNT | 8 | tentatives par écriture de déliage unique |
STAGE_C_TRIES | 8 | tentatives pour mettre en ligne la lecture arbitraire |