CVE-2023-40547 – une vulnérabilité causée par une confiance excessive de shim dans les en-têtes HTTP
(github.com/rhboot)- Le commit
0226b56de 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 dereceive_http_response(), une vérification de*buf_size < rx_message.BodyLengthet traite l’échec avecEFI_BAD_BUFFER_SIZEetInvalid 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 frappeContent-Lenghta aussi été corrigée enContent-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)dehttpboot.c - Quand
*buf_size == 0, il corrige la faute de frappe du message d’erreur existant puis passe àgoto errorFailed to get Content-Lenght→Failed 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_SIZEest défini - L’erreur
Invalid Content-Lengthest affichée - Puis l’exécution passe à
goto error
- Si la condition est vraie,
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
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.
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.cfgchaî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.
Source : https://techcommunity.microsoft.com/t5/hardware-dev-center/u...
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.
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’attenteDans un cas comme dans l’autre, si rien ne garantit que
rx_message.BodyLengthest égal à Content-Length, c’est une valeur incorrecteSi 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.BodyLengthcomme taille de tampon et d’interpréter toutes les données sur le fil comme le message reçuLe code actuel est inutilement complexe, et c’est ainsi que ce genre de bug s’introduit
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-LengthSi 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
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
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é
Un en-tête incorrect peut être envoyé dans les deux cas
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
Il me semble que tu n’as pas bien compris le problème
Content-lengthn’est pas la longueur réelle du corps, mais la longueur après Content-encodingCes 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
À 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:closeet ne vérifient pas Content-LengthCette 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
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