web-dev-qa-db-fra.com

DMA attaques malgré l'isolement IOMMU

Si vous connaissez déjà le comportement PCI et la gestion par Linux des tampons DMA, passez à la troisième section pour ma question. Sinon, lisez la suite pour un petit résumé de la façon dont les périphériques PCI effectuent les accès à la mémoire, et comment le noyau gère la communication avec les périphériques utilisant DMA. Je l'ai inclus ici à la fois dans l'espoir de fournir aux gens posant la même question des informations utiles, et de donner aux autres la possibilité de me corriger au cas où ma compréhension ne fonctionnerait pas.

(Ma compréhension de) PCI, IOMMU et DMA

Les périphériques PCI et PCIe ont dans leur espace de configuration un octet registre de commandes qui contient un masque de bits pour activer ou désactiver plusieurs fonctionnalités matérielles différentes. Le bit 2 est le bus master enable bit qui, lorsqu'il est défini, permet au périphérique d'initier DMA requêtes. Ce bit, et tout autre bit du registre de commandes , est défini par un logiciel exécuté en mode superviseur (généralement par des pilotes du noyau) et, bien qu'il soit physiquement stocké sur le périphérique PCI, il ne peut pas être modifié par celui-ci (peut-être que le PCH conserve un cliché instantané de l'espace de configuration PCI de chaque périphérique?). sans IOMMU, le périphérique peut demander des lectures et des écritures à n'importe quelle adresse mémoire légale. Ceci est souvent appelé une attaque DMA ou une mauvaise maîtrise du bus, et est un problème sur tout système non protégé avec des périphériques PCI malveillants . L'IOMMU est censé être la solution pour améliorer à la fois la sécurité et les performances. Pour référence, je pose spécifiquement des questions sur la mise en œuvre d'Intel, VT-d (précisément le plus moderne VT-d2).

La plupart des systèmes peuvent être configurés pour Remappage DMA, ou DMAR. Les tables ACPI incluses dans le BIOS ont souvent la table DMAR, qui contient une liste d'adresses vers laquelle divers groupes PCI verront tous leurs accès mémoire routés. Tout cela est décrit dans la section 2.5.1.1 des spécifications VT-d . Un graphique du document:

DMA remapping

Le noyau Linux DMA API

Les tables DMAR sont codées en dur par le BIOS. Un périphérique PCI donné (ou plutôt un donné groupe IOMM ) est autorisé à accéder uniquement à une plage de mémoire prédéterminée. Le noyau est informé de l'emplacement de cette mémoire et est invité à ne pas y allouer de mémoire qu'il ne souhaite pas lire/écrire sur DMA. Les valeurs de remappage sont signalées dans la mémoire tampon du journal du noyau:

 DMAR: définition de la carte d'identité pour le périphérique 0000: 00: 02.0 [0xad000000 - 0xaf1fffff] 
 DMAR: définition de la carte d'identité pour le périphérique 0000: 00: 14.0 [0xa95dc000 - 0xa95e8fff] 
 DMAR : Définition de la carte d'identité pour le périphérique 0000: 00: 1a.0 [0xa95dc000 - 0xa95e8fff] 
DMAR: définition de la carte d'identité pour le périphérique 0000: 00: 1d.0 [0xa95dc000 - 0xa95e8fff]
 DMAR: préparer le mappage d'unité 0-16 Mo pour le LPC 
 DMAR: définition de la carte d'identité pour le périphérique 0000: 00: 1f.0 [0x0 - 0xffffff] 
 DMAR: Intel (R) Technologie de virtualisation pour les E/S dirigées 
 Iommu: Ajout de périphérique 0000: 00: 00.0 au groupe 0 
 Iommu: Ajout de périphérique 0000: 00: 01.0 au groupe 1 
 Iommu: Ajout de périphérique 0000: 00: 02.0 au groupe 2 
 Iommu: Ajout d'un appareil 0000: 00: 14.0 au groupe 3 
 Iommu: Ajout d'un appareil 0000: 00: 16.0 au groupe 4 
 Iommu: Ajout périphérique 0000: 00: 1a.0 au groupe 5 
 iommu: ajout du périphérique 0000: 00: 1b.0 au groupe 6 
 iommu: ajout du périphérique 0000: 00: 1c.0 au groupe 7 
 iommu: Ajout d'un appareil 0000: 00: 1c.2 au groupe 8 
 iommu: Ajout d'un appareil 0000: 00: 1c.3 au groupe 9 
 iommu: Ajout d'un appareil 0000: 00: 1c.4 au groupe 10 
iommu: Ajout d'un périphérique 0000: 00: 1d.0 au groupe 11
 iommu: Ajout de périphérique 0000: 00: 1f.0 au groupe 12 
 iommu: Ajout de périphérique 0000: 00: 1f.2 au groupe 12 
 iommu: Ajout de périphérique 0000: 00 : 1f.3 au groupe 12 
 Iommu: Ajout d'un appareil 0000: 01: 00.0 au groupe 1 
 Iommu: Ajout d'un appareil 0000: 03: 00.0 au groupe 13 
 Iommu: Ajout d'un appareil 0000: 04: 00.0 au groupe 14 
 Iommu: Ajout d'un appareil 0000: 05: 00.0 au groupe 15 

À partir des lignes en gras, nous voyons que le groupe 11 contient (uniquement) le périphérique 0000:00:1d.0, Qui peut accéder librement à 13 pages de mémoire dans la plage de 0xa95dc000 - 0xa95e8fff. Tous les accès pour les périphériques du groupe 11 ne pourront y écrire, ce qui les empêchera de modifier le contenu des autres tampons DMA ou code OS non lié. De cette façon, même si le périphérique a son bus ensemble de bits maître, il n'a pas besoin de garder une trace de l'endroit où il écrit et il ne peut pas (accidentellement ou par malveillance) écrire où il n'est pas censé le faire.

Lorsqu'un pilote de noyau souhaite interagir avec un périphérique via DMA, il alloue de la mémoire spécifiquement à cet effet en utilisant, par exemple, void *addr = kmalloc(len, GFP_KERNEL | GFP_DMA). Cela renverra, dans addr, une adresse de mémoire virtuelle pointant vers une section contiguë de mémoire len octets de taille appropriée pour DMA utilisation. Ceci est tous décrits plus en détail dans la documentation de l'API DMA . Le pilote est alors libre de communiquer avec le périphérique PCI via cette région de mémoire partagée. La série d'événements, simplifiée, peut ressembler à ceci:

  • Le pilote OpenCL alloue de la mémoire, partagée avec le périphérique PCI GPU, pour DMA use.
  • Le pilote écrit des données vectorielles à l'adresse DMA, et part pour faire autre chose.
  • Le GPU lit les données sur le périphérique PCI et commence la lente tâche de traitement.
  • Une fois terminé, le GPU écrit les données finies dans le tampon et déclenche une interruption.
  • Le pilote arrête ce qu'il fait en raison de l'interruption et lit le graphique rendu dans la mémoire.

Le noyau se méfie-t-il DMA des tampons et les gère-t-il en toute sécurité?

Le noyau fait-il implicitement confiance à ces DMA? Un périphérique PCI malveillant ou compromis, n'écrivant nulle part ailleurs que les tampons désignés (l'IOMMU l'empêche de faire autrement), peut-il compromettre le noyau en exploitant les données structures qu'ils partagent? La réponse évidente est peut-être, car tout partage et analyse de structures de données complexes utilisant des langages non sécurisés en mémoire comporte un risque d'exploitation. Mais les développeurs du noyau peuvent supposer que ces tampons sont fiables et ne font aucun effort pour sécuriser le noyau contre toute activité malveillante (contrairement, disons, aux données partagées entre l'espace utilisateur non privilégié et le noyau via copy_from_user() et des fonctions similaires ). Je commence à penser que la réponse à la question de savoir si un périphérique PCI malveillant peut compromettre l'hôte malgré les restrictions de l'IOMMU est probablement.

L'exploitation d'une telle vulnérabilité fonctionnerait comme ceci, où buf est dans l'espace d'adressage contrôlé par le périphérique et accessible en écriture DMA, et dest est ailleurs dans la mémoire du noyau:

  • L'appareil écrit les données sous struct { size_t len; char data[32]; char foo[32]; } buf.
  • Le pilote doit copier dans data dans struct { char data[32]; bool summon_demons; } dest.
  • L'appareil définit malicieusement buf.len = sizeof(buf.data) + 1 et buf.foo[0] = 1.
  • Le pilote copie les données de manière non sécurisée à l'aide de memcpy(dest.data, buf.data, buf.len).
  • Le périphérique PCI prend le contrôle du noyau et de votre âme dans un débordement de tampon classique.

Évidemment, ceci est un exemple artificiel et même si un bug aussi évident ne se produirait probablement pas dans le noyau en premier lieu, il illustre mon point de vue et m'amène à ma question principale:

Existe-t-il des exemples de vulnérabilités dues à une mauvaise manipulation des structures de données partagées sur DMA, ou de pilotes spécifiques traitant l'entrée des périphériques PCI comme fiable?

Limitations de VT-d en tant qu'IOMMU

Je suis conscient de ses limites et je ne veux pas de réponse qui tente d'expliquer comment un périphérique pourrait contourner directement l'IOMMU ou utiliser une autre faille pour prendre le contrôle du système. Je connais:

  • Il ne peut pas protéger adéquatement un système à moins que x2APIC et le remappage d'interruption ne soient pris en charge.
  • Services de traduction d'adresses (ATS) peut contourner l'IOMMU.
  • PCI modifié ROM d'extension peut attaquer le système au redémarrage.
  • Tous les périphériques d'un groupe IOMMU donné ont accès à la même mémoire (en supposant qu'il n'y ait pas d'ACS).
  • Certains BIOS sont livrés avec une table DMAR cassée, entraînant la désactivation de l'IOMMU.
  • Le CSME ("Intel ME") peut être en mesure de désactiver VT-d via PSF et PAVP .
  • Pourtant, des attaques inconnues peuvent être capables de désactiver ou de contourner l'IOMMU.

* DMA signifie accès direct à la mémoire. Il s'agit d'une fonctionnalité matérielle grâce à laquelle certaines interfaces matérielles (comme PCIe) peuvent demander un accès direct à la mémoire système, sans passer par le CPU.

23
forest

Récemment, j'avais lu les articles publiés au NDSSS 2019, et cet article présenté en février, je pense, répond complètement à la question. Au moment où la question a été posée, il semble que la réponse à l'existence de vulnérabilités était oui mais elles ont été corrigées dans le noyau Linux à partir de 5. . Les diapositives appartenant à l'article présenté lors du Network and Distributed System Security Symposium 2019 peuvent être trouvées ici .

Les questions sont toujours d'actualité car les interfaces Thunderbolt disposent également de DMA. Les notes de mise à jour sous Linux peuvent être trouvées ici .

6
LTPCGO