- CVE-2023-6241 est un bug logique dans l’unité de gestion mémoire du GPU Arm Mali, qui permet à une application Android malveillante d’obtenir l’exécution arbitraire de code noyau et les privilèges root, même sur un Pixel 8 avec le MTE du noyau activé
- Les appareils affectés sont les récents appareils à GPU Arm Mali utilisant le Command Stream Frontend (CSF), dont les Google Pixel 7 et Pixel 8
- La vulnérabilité exploite une courte fenêtre où un verrou est relâché pendant l’extension de mémoire JIT afin de créer une incohérence entre les mappings GPU et le tableau des pages de backing
- L’exploit utilise un mapping GPU résiduel vers des pages de backing libérées, force leur réutilisation comme PGD du contexte GPU, puis mappe la mémoire noyau et le code noyau
- Le MTE détecte les incohérences entre pointeurs et tags mémoire, mais ce flux d’attaque se déroule hors du périmètre de protection du MTE, car le GPU accède directement aux adresses physiques
Portée de la vulnérabilité et état du correctif
- CVE-2023-6241 est une vulnérabilité du GPU Arm Mali qui permet à une application Android malveillante d’obtenir l’exécution arbitraire de code noyau et les privilèges root sur l’appareil
- Elle a été signalée à Arm le 15 novembre 2023 et corrigée dans le driver Arm Mali r47p0, publié le 14 décembre 2023
- Le correctif Android a été inclus dans la mise à jour de sécurité de mars 2024
- Les appareils touchés sont les récents appareils à GPU Arm Mali utilisant la fonction CSF (Command Stream Frontend), notamment les Google Pixel 7 et Pixel 8
- Le fonctionnement de l’exploit a été confirmé sur un Pixel 8 avec le MTE du noyau activé
Modèle de défense de MTE sur Arm64
- MTE (Memory Tagging Extension) est une fonctionnalité matérielle des processeurs Arm récents qui compare les tags des pointeurs et des blocs mémoire afin de détecter les corruptions mémoire
- Les pointeurs Arm64 font 64 bits, mais l’espace d’adressage applicatif réel est généralement de 52 bits ou moins, ce qui permet d’utiliser une partie des bits de poids fort pour stocker les tags
- Lors d’un overflow linéaire, le tag d’un bloc mémoire adjacent peut différer de celui du pointeur, et dans un use-after-free, des changements de tag pendant la libération et la réallocation peuvent provoquer une incohérence
- Contrairement à des mesures d’atténuation de stade plus avancé comme kCFI, MTE est une mesure d’atténuation précoce conçue pour intercepter la corruption mémoire au moment où elle se produit
- Le nombre de bits de tag étant limité, les collisions sont inévitables, mais même avec seulement 4 bits de tag, la probabilité de succès aléatoire tombe à 1/16
- Des attaques par canal auxiliaire comme Spectre peuvent divulguer les valeurs des pointeurs et des blocs mémoire, permettant de deviner le bon tag et de contourner MTE, mais ce type de fuite est surtout accessible à un attaquant local
- À l’heure actuelle, seul le Google Pixel 8 autorise l’activation de MTE dans les options développeur, et MTE est désactivé par défaut
- L’activation de MTE dans le noyau nécessite des étapes supplémentaires
La race condition dans la mémoire JIT de Mali
- Une application utilisateur qui utilise le driver GPU Mali ouvre le fichier du driver et crée puis initialise un objet noyau
kbase_contextvia des appelsioctl kbase_contextgère plusieurs types de mémoire partagés entre l’appareil GPU et l’application en espace utilisateur- Les régions mémoire du GPU Mali sont représentées par
kbase_va_region;nr_pagesindique la taille virtuelle etgpu_alloc->nentsle nombre réel de pages de backing - La mémoire JIT est une mémoire native dont le cycle de vie est géré par le driver noyau ; l’application peut l’allouer ou la libérer via des commandes GPU
- Sur les GPU CSF, les commandes logicielles et matérielles sont placées dans des files distinctes
KBASE_IOCTL_KCPU_QUEUE_CREATEpermet de créer unekbase_kcpu_command_queueKBASE_IOCTL_KCPU_QUEUE_ENQUEUEpermet de mettre des commandes en fileBASE_KCPU_COMMAND_TYPE_JIT_ALLOCetBASE_KCPU_COMMAND_TYPE_JIT_FREEservent aux allocations et libérations JIT
kbase_jit_allocaterecherche une région réutilisable dans le pool de mémoire JIT libérée et, si la taille physique est insuffisante, augmente les pages de backing viakbase_jit_growkbase_jit_growpeut relâcher temporairementkctx->reg_locketkctx->mem_partials_locklors de l’appel àkbase_mem_pool_growkctx->reg_lockprotège l’accès concurrent aux régions mémoire ; le moment où ce verrou est relâché devient donc la fenêtre de race
Flux de déclenchement de CVE-2023-6241
- Si le GPU accède à l’adresse d’une région mémoire qui n’est pas soutenue par des pages physiques de backing, un fault d’accès mémoire GPU se produit
kbase_mmu_page_fault_workervérifie alors si la région peut être étendue et peut allouer puis mapper immédiatement les pages de backing nécessaires- Lors de leur création, les régions JIT satisfont la condition GROWABLE_FLAGS_REQUIRED, qui inclut
KBASE_REG_PF_GROWetKBASE_REG_GPU_WR - Le flag
KBASE_REG_DONT_NEED, ajouté lors de la libération d’une région JIT, est supprimé au début dekbase_jit_growparkbase_mem_evictable_unmake - En conséquence, si un fault de page GPU est déclenché sur la même région JIT pendant la fenêtre de race de
kbase_mem_pool_grow, le gestionnaire de fault peut étendre cette région - Si le gestionnaire de fault modifie
reg->gpu_alloc->nents, les valeursold_sizeetdeltaprécédemment sauvegardées parkbase_jit_growne correspondent plus à l’état réel - Ensuite,
kbase_alloc_phy_pages_helper_lockedetkbase_mem_grow_gpu_mappingeffectuent les allocations de pages de backing et les mappings GPU à partir de valeurs obsolètes, créant une incohérence entre les mappings GPU et le tableaupages - Cette race est facile à gagner, car
kbase_mem_pool_growinclut de grosses allocations mémoire
Changement de méthode d’attaque après le correctif de GHSL-2023-005
- Dans la précédente vulnérabilité GHSL-2023-005, un autre thread pouvait réduire une région JIT avec
KBASE_IOCTL_MEM_COMMIT, ce qui invalidaitold_sizeetdelta - Après le correctif de GHSL-2023-005, il n’est plus possible de modifier la taille de la mémoire JIT via l’
ioctl KBASE_IOCTL_MEM_COMMIT - Dans CVE-2023-6241, il n’est donc plus possible de réduire la région dans la fenêtre de race, mais seulement de l’agrandir
- En cas de simple agrandissement, seules quelques pages de backing finales ne sont pas mappées au GPU ; le mapping continu depuis le début de la région reste intact, ce qui ne pose pas immédiatement problème
- L’exploit crée alors un nouveau mapping après le gap non mappé via un fault GPU supplémentaire, puis exploite une libération JIT pour faire coïncider le point de shrink avec l’intérieur de ce gap et obtenir un état exploitable
Hypothèse fragile lors du démontage des mappings GPU
kbase_mmu_teardown_pgd_pagesparcourt les tables de pages GPU et marque les entrées comme invalides afin de supprimer les mappings d’adresses GPU- Cette fonction part du principe que si une PTE de niveau supérieur est invalide, toute la grande plage d’adresses couverte par cette entrée est déjà unmapped et peut être ignorée
- Une seule PTE de niveau 2 couvre une plage de 512 pages
- Dans un
kbase_va_regionnormal, les adresses virtuelles mappées sont toujours continues depuis le début de la région, sans gap intermédiaire, ce qui rend ce comportement sûr - L’exploit de CVE-2023-6241 crée au contraire un gap non mappé entre des mappings, puis place le point de départ du shrink à l’intérieur de ce gap
kbase_mmu_teardown_pgd_pagesrencontre une PTE invalide de niveau 2 et saute 512 pages, alors qu’une partie des adresses situées après peut en réalité être encore mappée- Les adresses GPU sautées à tort conservent l’accès aux pages physiques même après la libération des pages de backing
Comment cela mène à l’exécution de code noyau
- Lorsqu’une région JIT est libérée, les pages de backing sont rendues, mais les mappings GPU laissés par erreur peuvent continuer à accéder à ces pages libérées
- Ces pages de backing libérées peuvent ensuite être réutilisées comme d’autres pages noyau
- L’une des techniques utilisées consiste à faire réutiliser une page de backing libérée comme PGD (page table global directory) du
kbase_contextGPU - L’allocation des pages de backing dans le driver Mali suit une hiérarchie
- le driver prend d’abord des pages dans le
kbase_mem_pooldukbase_contextcourant - si cela ne suffit pas, il utilise
pool->next_pool - si cela ne suffit toujours pas, il alloue directement des pages via le buddy allocator du noyau
- le driver prend d’abord des pages dans le
pool->next_poolest un pool mémoire géré par le driver Mali et partagé par tous leskbase_context; il est aussi utilisé pour allouer les PGD des contextes GPU- Si une page libérée est réutilisée comme PGD, l’adresse GPU résiduelle permet au GPU de réécrire ce PGD
- En réécrivant le PGD, il devient possible de mapper en GPU de la mémoire noyau arbitraire et du code noyau
- À partir de là, on peut réécrire du code noyau pour obtenir l’exécution arbitraire de code noyau, et lire ou écrire des données noyau pour modifier les credentials d’un processus et désactiver SELinux
- L’exploit pour Pixel 8 et les notes de configuration sont publiés dans le dépôt GitHub Security Lab
Pourquoi le contournement de MTE est possible
- Ce flux d’exploit n’a pas besoin d’une étape dédiée au contournement de MTE
- MTE sert à vérifier si le tag du bloc mémoire visé correspond au tag du pointeur afin de détecter les déréférencements invalides
- Au moment du déclenchement de CVE-2023-6241, une incohérence apparaît entre le tableau
pageset les mappings GPU, mais pris séparément, aucun des deux ne contient d’entrée invalide - Lorsque
kbase_mmu_teardown_pgd_pagessaute à tort la suppression d’un mapping GPU, l’adresse physique d’une page mémoire libérée reste présente dans la table de pages GPU - Quand le GPU accède à cette page libérée, il accède directement à son adresse physique sans passer par une vérification de déréférencement de pointeur
- L’effet exact de MTE sur les accès mémoire du GPU n’est d’ailleurs pas clairement établi
- En pratique, ce bug contourne donc la protection MTE en exploitant le chemin par lequel le GPU, en tant que coprocesseur, accède directement à la mémoire physique
Surface d’attaque restante après MTE
- CVE-2023-6241 montre que même avec le MTE du noyau activé sur un Pixel 8, un seul bug peut suffire à atteindre l’exécution arbitraire de code noyau
- MTE constitue une avancée importante dans l’atténuation des corruptions mémoire et peut rendre de nombreuses vulnérabilités de ce type inexploitées, mais ce n’est pas une défense universelle
- Dans ce cas, le GPU contourne MTE en accédant directement à la mémoire physique
- À mesure que les atténuations matérielles et logicielles côté CPU se multiplient, les coprocesseurs et leurs drivers noyau peuvent continuer à représenter une surface d’attaque puissante
1 commentaires
Avis sur Hacker News
Le point essentiel ici, c’est que les GPU sont depuis longtemps un casse-tête sur Android
Les GPU disposent de droits d’accès très puissants à l’AP, ce qui leur permet en pratique de contourner les mesures d’atténuation placées en amont. Un bug dans le code de mapping du pilote peut déboucher sur une primitive d’attaque puissante, et cela a été exploité à plusieurs reprises dans des exploits observés dans la nature. Au final, il semble difficile que la situation change vraiment tant que l’architecture n’est pas repensée
Ce qui est intéressant dans cette vulnérabilité, c’est qu’il s’agit d’un bug logique dans l’unité de gestion mémoire du GPU Arm Mali, et qu’il permet de contourner Memory Tagging Extension
Mais le reste de l’article semble expliquer que la cause réelle est une condition de concurrence, et que l’use-after-free n’en est que la conséquence
Les installations de GrapheneOS antérieures à la mise à jour de mars étaient-elles aussi affectées ?
Il arrive parfois que GrapheneOS adopte le niveau de correctifs de sécurité AOSP avant sa publication, ou rétroporte des correctifs de sécurité d’AOSP ou du noyau qui ne sont pas encore publics
Cette mise à jour concernait la pile Bluetooth, après tout Édit : vous pouvez ignorer ça. Je l’avais confondu avec un billet récent du blog GrapheneOS intitulé « MTE a permis de trouver un problème qui affecte aussi toutes les applications système ». GrapheneOS a intégré le « niveau complet de correctifs de sécurité 2024-03-05 » dans la version 2024030600, donc ce correctif semble également inclus
La sûreté mémoire probabiliste d’Arm MTE constitue un tremplin vers le matériel CHERI déterministe, https://saaramar.github.io/memory_safety_blogpost_2022/ et https://news.ycombinator.com/item?id=39668053
La bonne stratégie d’atténuation doit viser les primitives d’attaque de premier ordre, c’est-à-dire la cause racine du bug. Parmi les solutions matérielles, on trouve CHERI (Morello, CheriIoT) et MTE ; côté atténuations logicielles, il y a kalloc_type+dataPAC, AUTOSLAB, Firebloom, GuardedMemcpy, CastGuard, la réduction de la surface d’attaque ; et parmi les langages de programmation sûrs, Rust et Swift. MTE et CHERI se complètent bien et aident à éliminer ce type de bugs à la racine. MSR, MSRC et Azure Silicon ont poussé à miniaturiser CHERI jusqu’à la spécification du plus petit cœur RISC-V, le RISC-V32E
Microsoft Research a publié en open source une pile matérielle/logicielle CHERI pour les appareils IoT, https://msrc.microsoft.com/blog/2023/02/first-steps-in-cheri...
Les microcontrôleurs basés sur CHERI visent à obtenir de très fortes garanties de sécurité en co-concevant l’architecture du jeu d’instructions (ISA), l’interface binaire d’application (ABI), le modèle d’isolation et le cœur de la pile logicielle. Ces microcontrôleurs réalisent une atténuation déterministe de la sûreté spatiale via les capacités CHERI-ISA, une atténuation déterministe de la sûreté temporelle du tas et des piles inter-compartiments via des barrières de chargement, la mise à zéro, la récupération et un contrôle de flux d’information sur 1 bit, ainsi qu’une compartimentation fine grâce à des fonctionnalités CHERI-ISA supplémentaires et un petit moniteur
David Chisnall, Université de Cambridge, https://lobste.rs/s/gnjx2n/c_can_be_memory_safe#c_9ohzku via https://eclypsium.com/blog/a-faster-path-to-memory-safety-ch...
« Il y a environ 13 milliards de lignes de code C/C++ open source dans diverses bases informatiques de confiance, et davantage encore si l’on inclut le code propriétaire. Même si tout le monde arrêtait dès maintenant d’écrire du C/C++ et que tous les ingénieurs logiciels se concentraient sur la réécriture du code existant dans des langages sûrs, il faudrait quand même 5 à 10 ans pour tout remplacer, et il est très probable que cela introduirait aussi beaucoup de bugs logiques, car on remplacerait du code éprouvé de longue date par du nouveau code nécessitant d’autres algorithmes et structures de données pour correspondre aux idiomes autorisés des langages sûrs. »
« Si l’on se contente d’arrêter d’écrire du code C/C++ sans faire de réécriture, alors, au rythme habituel de remplacement du code, il faudra environ 50 ans pour que la base informatique de confiance devienne entièrement sûre. Si tout le monde ne se met pas d’accord pour abandonner le C/C++, ce sera au minimum 100 ans. »
« En revanche, si les principaux fabricants de CPU lancent des CPU CHERI dans les 5 prochaines années, alors, sans même que les programmeurs aient à changer de comportement, la plupart des machines — en particulier celles à forte valeur — disposeront de la sûreté mémoire d’ici 15 ans à partir d’aujourd’hui. »
La première est le système Sonata : https://github.com/lowRISC/sonata-system. Il se compose d’un PCB dédié avec FPGA, plusieurs périphériques et des connecteurs. La conception du PCB est terminée et il devrait être disponible via Mouser ; comme même le layout de la carte est open source, il sera aussi possible de l’assembler soi-même. En ce moment, ils travaillent sur le RTL pour le FPGA. Une fois terminé, cela donnera un système de type microcontrôleur basé sur CHERIoT, avec documentation et outils
Ils développent aussi en plus le système Symphony, qui combine Sonata avec la racine de confiance OpenTitan Earl Grey : https://github.com/lowRISC/symphony-system
En plus, la dernière fois que j’ai regardé, CHERI n’était pas sound. On pouvait encore écrire des bugs mémoire par-dessus ; est-ce que cela a été corrigé depuis ?
Rien que les débats de type bike-shedding pourraient prendre autant de temps
Cet exploit semble pouvoir être écrit dans la plupart des langages, y compris Rust
Le matériel est à ce point mauvais ? Mon dieu...
Cette approche est possible parce que les GPU n’autorisent pas un accès matériel relativement direct comme les CPU
Excellente recherche et excellent billet ; c’est un peu surprenant, mais aussi agréable, de le voir publié sur le blog GitHub.
Quelqu’un sait quelle est la « raison business » de GitHub pour mener ce type de recherche ? Je ne dis pas qu’il faut absolument une raison business, mais le voir là m’étonne un peu.
Cette capacité de recherche a ensuite donné naissance au GitHub Security Lab. Semmle a créé CodeQL, désormais proposé par GitHub (https://docs.github.com/en/code-security/code-scanning/intro...). GitHub et Microsoft veulent associer CodeQL à des « analyses de sécurité approfondies » (https://www.microsoft.com/en-us/security/blog/2023/11/02/ann...)
Ils continuent donc de financer ce type de recherche en sécurité novatrice, et les professionnels de la sécurité du secteur leur en sont reconnaissants.
Il existe aussi des applis GitHub, et leur sécurité ne sort pas du périmètre de GitHub. Si un attaquant peut installer sur un téléphone une appli cachée, il peut faire toutes sortes de choses en se faisant passer pour l’utilisateur. Si la victime est une personne influente dans l’écosystème GitHub, l’impact peut être important ; trouver ce genre de vulnérabilités est donc aussi dans l’intérêt de GitHub.
Ils peuvent donc avoir intérêt à examiner et valider les fonctions de sécurité du matériel Arm pour le sandboxing à l’aide de MTE.
À première vue, un produit comme GitHub n’a pas forcément besoin d’experts en sécurité Android. Mais à long terme, il peut y avoir des bénéfices potentiels.
[0]: https://en.wikipedia.org/wiki/Basic_research
Il est surprenant qu’on n’ait encore jamais vu de CPU et de téléphones avec très peu, voire pas du tout, de GPU, vendus comme des business phones.
Les avantages en matière de sécurité, de coût et de consommation électrique semblent évidents.