
Exploit de preuve de concept pour CVE-2024-1065, démontrant l'exploitation du cache de pages via un use-after-free dans le pilote du noyau GPU ARM Mali pour exécuter du code arbitraire en tant que root sans modification du disque.
Preuve de concept pour CVE-2024-1065, une utilisation après libération de page physique dans le pilote noyau GPU ARM Mali. L'exploit démontre l'exploitation du cache de pages : faire la course avec une page MIGRATE_MOVABLE libérée pour l'introduire dans l'image mémoire du code d'un binaire SUID, puis la réécrire pour exécuter du code arbitraire en tant que root sans modifier aucun fichier sur le disque.
À des fins de recherche uniquement. Cette preuve de concept cible une vulnérabilité corrigée sur un système de test x86 contrôlé. Le chemin de code vulnérable ne peut pas être déclenché depuis l'espace utilisateur sur les appareils Pixel en production.
Déclenchement de l'UAF — Une page anonyme est importée dans le pilote Mali GPU via KBASE_IOCTL_MEM_IMPORT et mappée deux fois dans l'espace utilisateur. La libération des deux mappages renvoie la page physique à la liste libre des pages MIGRATE_MOVABLE tandis que cpu_mapping2 y fait toujours référence.
Arrosage du cache de pages — L'exploit ouvre /usr/bin/passwd 100 fois et évince à plusieurs reprises la page à PAGE_OFFSET avec posix_fadvise(POSIX_FADV_DONTNEED), puis déclenche un défaut de cache avec pread. Chaque défaut force le noyau à allouer une nouvelle page MIGRATE_MOVABLE, parcourant la liste libre jusqu'à ce que la page UAF soit choisie.
Confirmation du chevauchement — Une lecture d'octet de sonde via cpu_mapping2 à MAIN_OFFSET confirme que la page UAF correspond désormais à l'entrée du cache de pages du binaire. Les valeurs 0x00 et 0x61 indiquent un échec ; toute autre valeur correspond au code binaire réel.
Injection du shellcode — memcpy via cpu_mapping2 écrase main() avec un shellcode qui appelle setuid(0), setgid(0) et execve("/bin/sh").
Exécution — execve("/usr/bin/passwd") est appelé. Le chargeur ELF rend les pages du cache de pages du binaire exécutables. La page corrompue est déjà présente — aucune lecture disque n'a lieu — donc le shellcode s'exécute en tant que root.
gcc -O2 -o exploit exploit.c
./exploit
Sortie attendue :
[*] Opened /usr/bin/passwd x100
[*] MEM_IMPORT: flags=0x... gpu_va=0x... va_pages=0x1
[*] gpu_mapping (VA 1): 0x...
[*] cpu_mapping2 (VA 2): 0x...
[*] UAF triggered — stale mapping alive at 0x...
[*] Spraying page cache (100 attempts)...
[+] Overlap confirmed on attempt N (byte=0xf3) — cpu_mapping2 aliases the page cache!
[*] Shellcode written. Triggering execve...
# id
uid=0(root) gid=0(root) groups=0(root)
Toutes les valeurs propres à la cible sont définies en haut de exploit.c :
#define TARGET_BINARY "/usr/bin/passwd"
#define PAGE_OFFSET 0x4000 /* décalage dans le fichier de la page contenant main() */
#define MAIN_OFFSET 0xbc0 /* décalage intra-page de main() */
Pour recibler un autre binaire, trouvez le décalage de la fonction en désassemblant _start à l'adresse du point d'entrée (readelf -h), puis calculez :
PAGE_OFFSET = sym_va & ~0xfff
MAIN_OFFSET = sym_va & 0xfff
Vérifiez avant d'exécuter :
dd if=<TARGET_BINARY> bs=1 skip=$((PAGE_OFFSET + MAIN_OFFSET)) count=8 2>/dev/null | xxd
| Prérequis | Valeur |
|---|
| Architecture | x86-64 |
| Noyau | 5.15 (testé sur 5.15.0+) |
| Configuration du noyau | CONFIG_MALI_NO_MALI=y, CONFIG_MALI_CSF_SUPPORT=y, r48 |
| Périphérique | /dev/mali0 accessible depuis l'espace utilisateur |
| Binaire cible | /usr/bin/passwd (SUID root) |
| Symptôme | Cause | Correction |
|---|
| L'arrosage échoue après 100 tentatives | La page UAF est consommée avant la fenêtre de concurrence | Pré-évincer la page cible avant de déclencher l'UAF ; augmenter NUM_FDS |
kernel BUG at mm/page_poison.c | La vérification de sonde a réussi sur l'octet d'empoisonnement 0xaa | Ajouter probe != 0xaa à la condition de confirmation |
SYSCHK échoue sur MEM_IMPORT | Pilote Mali non chargé ou version incorrecte | Vérifier CONFIG_MALI_NO_MALI et l'accord de version du pilote |