1 points par GN⁺ 2024-01-27 | 1 commentaires | Partager sur WhatsApp
  • Le commit 0226b56 de rhboot/shim corrige CVE-2023-40547, causée par le fait de faire confiance telle quelle à la valeur de taille de l’en-tête HTTP lors de la réception d’un fichier
  • Si un en-tête manipulé indique une taille plus petite que les données réellement reçues, shim peut allouer un espace plus petit que le tampon nécessaire
  • Le code existant utilisait la valeur de l’en-tête pour l’allocation, mais les métadonnées du protocole pour la copie, ce qui pouvait entraîner une écriture hors limites
  • Le correctif ajoute dans httpboot.c, au sein de receive_http_response(), une vérification de *buf_size < rx_message.BodyLength et traite l’échec avec EFI_BAD_BUFFER_SIZE et Invalid Content-Length
  • La modification porte sur un seul fichier, httpboot.c, avec 7 lignes ajoutées et 1 ligne supprimée, et la faute de frappe Content-Lenght a aussi été corrigée en Content-Length

Déroulement de la vulnérabilité

  • CVE-2023-40547 est un problème qui survient lorsque shim récupère un fichier via HTTP ou un protocole associé
  • Lors de l’allocation du tampon destiné à stocker les données reçues, la valeur de taille de l’en-tête HTTP était utilisée
  • Un en-tête HTTP peut être manipulé et indiquer une taille inférieure à celle des données réellement reçues
  • Dans le flux existant, la valeur de l’en-tête servait à allouer le tampon, tandis que la copie des données depuis le tampon rx se basait sur les métadonnées du protocole
  • Cette différence pouvait conduire à copier plus de données que la taille du tampon alloué, provoquant ainsi une écriture hors limites

Contenu du correctif

  • Le correctif ajoute une vérification défensive dans receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size) de httpboot.c
  • Quand *buf_size == 0, il corrige la faute de frappe du message d’erreur existant puis passe à goto error
    • Failed to get Content-LenghtFailed to get Content-Length
  • La nouvelle vérification teste la condition *buf_size < rx_message.BodyLength
    • Si la condition est vraie, efi_status = EFI_BAD_BUFFER_SIZE est défini
    • L’erreur Invalid Content-Length est affichée
    • Puis l’exécution passe à goto error

Portée de la modification

  • Le seul fichier modifié est httpboot.c
  • Le volume de changement est de 7 lignes ajoutées et 1 ligne supprimée
  • L’essentiel est la logique qui vérifie si rx_message.BodyLength, la longueur du corps reçu, est supérieure à *buf_size, la taille allouée

Références associées

  • Ce commit est indiqué comme une modification corrigeant CVE-2023-40547
  • Le message de commit précise que le problème provient d’une confiance excessive dans les en-têtes HTTP
  • Le signalement de la vulnérabilité est attribué à Bill Demirkapi du Microsoft Security Response Center

1 commentaires

 
GN⁺ 2024-01-27
Avis Hacker News
  • shim est un chargeur de démarrage EFI couramment utilisé par les distributions Linux qui veulent activer Secure Boot.
    Du point de vue des distributions, plutôt que de demander aux utilisateurs d’enregistrer eux-mêmes leurs clés, elles veulent permettre d’activer facilement Secure Boot avec la clé de signature Microsoft intégrée par défaut.
    Mais comme Microsoft ne signe généralement pas les chargeurs de démarrage GPL comme GRUB, shim a été créé afin de pouvoir être signé avec la clé Microsoft ; shim vérifie ensuite la signature de ce qu’il va démarrer avec une clé distincte appelée Machine Owner Key, ou MOK.
    Lorsqu’on indique à shim le binaire EFI à démarrer, on peut lui fournir une URL HTTP ; si le serveur HTTP est malveillant, cela peut alors provoquer une écriture hors limites.
    Cela dit, comme il sert généralement à démarrer un chargeur de démarrage local de second niveau comme GRUB, le problème semble peu susceptible d’affecter la plupart des installations.
    Secure Boot a été conçu dès le départ pour pouvoir révoquer même des binaires déjà signés via la liste DBX ; si cette liste est ajoutée à l’UEFI, le binaire concerné est refusé même avec une signature valide.
    Si les signatures des anciens binaires shim affectés par ce bug sont ajoutées à la liste, chacun peut mettre à jour la liste sur sa machine ; elle peut aussi être distribuée via des mises à jour capsule comme LVFS, et si vous gérez directement les clés et variables Secure Boot, vous pouvez aussi télécharger la liste depuis https://uefi.org/revocationlistfile et l’enregistrer.

    • Je suis la personne qui a découvert le bug de l’article original, et c’est une idée reçue fréquente que ce problème ne serait exploitable que lorsqu’on utilise le démarrage HTTP.
      Si c’était le cas, il n’aurait pas été classé Critical.
      Ce bug peut être exploité lorsqu’un malware local disposant de privilèges écrase la partition EFI, lors d’une attaque de l’homme du milieu sur un réseau voisin où le démarrage PXE est activé, ou à distance par l’homme du milieu lorsqu’on utilise le démarrage HTTP.
      Un attaquant distant non privilégié placé en homme du milieu peut l’exploiter sans accès direct si la machine victime utilise le démarrage HTTP.
      Un attaquant distant qui a obtenu des privilèges et l’exécution de code sur la machine victime peut contourner Secure Boot si le firmware prend en charge HTTP, même si la victime n’utilise pas le démarrage HTTP.
      Par exemple, il peut modifier les variables d’ordre de démarrage pour pointer vers un serveur qu’il contrôle, ou écraser le chargeur de démarrage de la partition EFI avec un shim légitime et une image GRUB2, puis faire en sorte que grub.cfg chaîne-charge un nouveau shim via HTTP.
      C’est possible parce que la syntaxe des périphériques de GRUB2 permet de désigner des périphériques pris en charge, dont HTTP.
      De plus, un attaquant voisin non privilégié en position d’homme du milieu peut l’exploiter si la machine victime utilise le démarrage PXE, en enchaînant shim via PXE → GRUB2 via PXE → shim via HTTP.
    • C’est parce que l’équipe juridique de Microsoft estime que si Microsoft signait GRUB, un chargeur de démarrage sous licence GPLv3, la GPLv3 pourrait conférer le droit d’exiger la fourniture des clés de signature aux développeurs.
      Source : https://techcommunity.microsoft.com/t5/hardware-dev-center/u...
    • Je me demandais pourquoi un chargeur de démarrage faisait des communications réseau ; l’explication selon laquelle on peut désigner un binaire EFI par une URL HTTP rend les choses claires.
    • Je me demande comment shim évite le problème qui inquiète Microsoft.
      Si la clause anti-Tivoization de la GPLv3 peut exiger la fourniture des clés de signature Secure Boot, il me semble qu’il faudrait aussi fournir les clés de signature MOK sur demande.
      Dans ce cas, n’importe qui obtiendrait une clé permettant de signer du code arbitraire qui sera démarré indirectement via Secure Boot ; je ne vois pas bien en quoi ce serait significativement différent, dans les faits, du fait que Microsoft émette simplement une clé de signature pour des projets GPLv3 comme GRUB.
    • Si l’objectif est de démarrer un chargeur de démarrage local de second niveau, Windows Boot Manager ne pourrait-il pas jouer le même rôle ?
      Sur de vieilles machines BIOS, j’avais déjà fait en sorte que WBM chaîne-charge GRUB, mais je ne l’ai pas encore essayé sur des machines UEFI, donc je ne sais pas s’il y a un point de blocage.
  • On peut se demander : « pourquoi démarrer depuis un serveur non fiable ou compromis ? », ou « si le serveur est compromis, il suffit d’envoyer un binaire malveillant, donc quel intérêt ? ». En bref, le binaire que shim démarre au final doit être signé avec la MOK.
    Par conséquent, que l’on démarre dans un réseau compromis, via HTTP ou depuis un serveur compromis, les mêmes garanties de sécurité doivent être conservées indépendamment de l’usage de HTTPS.
    Secure Boot n’empêche pas les rétrogradations ; le fait qu’un serveur compromis puisse servir à une attaque par rétrogradation est donc indépendant de cette vulnérabilité.
    La protection contre les attaques par rétrogradation doit de toute façon être implémentée séparément, de manière plus robuste.
    Cela dit, je ne sais pas pourquoi shim doit prendre directement en charge le démarrage HTTP.
    Cela pourrait être géré par un second binaire EFI local signé avec la MOK ; ils ont probablement considéré que c’était une fonctionnalité relativement simple à implémenter.

  • Je ne comprends pas pourquoi ce code traite la longueur du corps selon deux critères différents
    Selon la RFC, en HTTP/1.1, Content-Length est l’information faisant autorité sur la longueur du corps d’une requête/réponse HTTP
    Les données présentes sur le fil au-delà de cette longueur font, par définition, partie d’un autre message
    À l’inverse, si Content-Length est supérieur à rx_message.BodyLength, cela signifie qu’on n’a pas encore reçu le message complet, donc il faut attendre davantage ou déclencher une erreur de délai d’attente
    Dans un cas comme dans l’autre, si rien ne garantit que rx_message.BodyLength est égal à Content-Length, c’est une valeur incorrecte
    Si l’on veut être plus permissif, il n’y a aucune raison de regarder l’en-tête Content-Length : il suffit d’utiliser rx_message.BodyLength comme taille de tampon et d’interpréter toutes les données sur le fil comme le message reçu
    Le code actuel est inutilement complexe, et c’est ainsi que ce genre de bug s’introduit

    • Si l’on regarde seulement ce commit, il est facile de se tromper
      En regardant le code autour de https://github.com/rhboot/shim/blob/0226b56513b2b8bd5fd281bc..., on voit que, dans la boucle, il reçoit des fragments de données et vérifie à chaque fois que les nouvelles données ne dépassent pas la capacité du tampon déterminée par Content-Length
      Mais auparavant, cette vérification n’était pas faite pour la première lecture en dehors de la boucle, et c’était le bug
      En revanche, je ne vois pas de code qui vérifie à la fin que la taille téléchargée est égale à *buf_size, c’est-à-dire à Content-Length
      Si cette condition n’est pas respectée, cela peut indiquer que la connexion a été fermée trop tôt
  • C’est clairement un bug et tant mieux qu’il soit corrigé, mais je me demande qui démarre sa machine depuis un hôte non fiable
    Si un attaquant contrôle suffisamment le service HTTP pour envoyer des en-têtes malveillants, éviter cet overflow est le plus petit de ses problèmes : le certificat est aussi compromis, et il pourrait aussi envoyer une charge utile conforme contenant du code malveillant
    C’est bien un bug, mais je ne suis pas sûr qu’il soit Critical

    • Les gens qui poussent Secure Boot sont du même acabit
      Ils croient sérieusement à une stratégie de sécurité où tout ce qui pourrait potentiellement être compromis ne doit surtout pas être signé pour Secure Boot
      S’il existe ne serait-ce qu’un élément signé vulnérable, on peut l’utiliser pour déchiffrer les disques chiffrés Secure Boot+TPM de tout le monde
      J’ai du mal à comprendre pourquoi cette approche a été considérée comme un modèle de sécurité valable, et il existe déjà une quantité de vulnérabilités de ce type
      En plus, elle ignore complètement l’énorme éléphant dans la pièce qu’est Windows
      Exemple de cette façon de penser : https://lkml.org/lkml/2018/4/3/767
      Malgré les inquiétudes de Linus, dans beaucoup de distributions, démarrer avec Secure Boot active effectivement le mode d’intégrité
      C’est probablement dû aux règles de Microsoft et au fait que les distributions sont contraintes de suivre la procédure décrite dans ce fil pour obtenir la signature UEFI de Microsoft
      Résultat : quand Secure Boot est activé, les fonctionnalités de la distribution sont généralement limitées, par exemple l’hibernation devient inutilisable
    • Une bonne défense consiste uniquement à empiler plusieurs couches de protection, comme dans la défense en profondeur, et ce bug perce un trou dans l’une de ces couches
    • Il pourrait aussi servir à s’introduire dans certains appareils verrouillés
    • À en juger par l’excellente explication dans un autre fil https://news.ycombinator.com/item?id=39135275, le vecteur d’attaque ne se limite pas à HTTP, donc on peut le considérer comme Critical
  • Quand quelque chose d’important se passe, il faut utiliser le S de HTTP
    Le démarrage d’un appareil en fait partie, et les en-têtes HTTPS ont toujours été chiffrés
    Cela reste toutefois un bug bien trouvé

    • Ici, HTTPS n’a rien à voir
      Un en-tête incorrect peut être envoyé dans les deux cas
    • Je ne suis pas sûr qu’HTTPS soit possible pour cet usage
      Le chiffrement exige une heure et une date exactes
      Le RTC peut être valide, mais je ne sais pas s’il gère bien les fuseaux horaires, et de toute façon l’heure peut être incorrecte
    • Le problème n’a rien à voir avec la présence ou l’absence du S
      Il me semble que tu n’as pas bien compris le problème
  • Content-length n’est pas la longueur réelle du corps, mais la longueur après Content-encoding

    • « HTTP/1.1 est un protocole délicieusement simple quand on en ignore la majeure partie »
  • Ces builds de shim incluent-ils httpboot ?
    À ma connaissance, shim ne sert qu’à exécuter d’autres binaires EFI sur le disque, et je ne crois pas avoir déjà vu la fonction de boot réseau de shim réellement utilisée

  • Je me trompe peut-être, mais je pensais que la plupart des clients HTTP ne lisaient que jusqu’au Content-Length indiqué, et considéraient comme une erreur le fait d’avoir lu moins d’octets que Content-Length

    • Le client HTTP est fourni par UEFI sous forme de pilote EFI
      À mon avis, la spécification UEFI ne définit pas précisément le comportement lorsque l’en-tête Content-Length et la longueur du corps de la réponse ne correspondent pas
      Il est donc tout à fait possible que certaines implémentations génèrent simplement une requête connection:close et ne vérifient pas Content-Length
      Cette vulnérabilité a été signalée par le MSRC, et la description de la CVE ne mentionne pas d’exploitation réelle
      Cela sera peut-être rendu public plus tard, ou il peut s’agir d’un problème théorique
  • Quelqu’un peut expliquer pourquoi lire moins que la longueur réelle du corps est dangereux ?
    J’aurais plutôt pensé que l’inverse était dangereux

    • Quand shim récupère un fichier via HTTP ou un protocole associé, il tente d’allouer un tampon pour stocker les données reçues
      Mais la taille vient d’un en-tête HTTP contrôlable, et l’attaquant peut indiquer une taille inférieure aux données reçues
      Dans ce cas, le code utilise la valeur de l’en-tête pour l’allocation, puis la taille issue des métadonnées du protocole lors de la copie depuis le tampon de réception, ce qui provoque une écriture hors limites
    • D’après l’explication, il alloue le tampon selon Content-Length, mais copie selon la taille du tampon réellement reçu, ce qui écrit en dehors de la zone allouée