Depuis Linux 6.9, la suspension de LUKS n’efface pas les clés de chiffrement disque de la mémoire
(mathstodon.xyz)- Depuis Linux 6.9, un outil qui verrouille les disques lors de la mise en veille d’un ordinateur portable échouait silencieusement, laissant en mémoire les clés de chiffrement complet de disque LUKS
- La cause est une interaction inattendue entre le refactoring de l’accès aux périphériques bloc intégré à Linux 6.9 en mai 2024 et le code de chiffrement ; le correctif proposé tient en une ligne
- Le problème n’apparaissait pas lors d’un arrêt complet, mais en suspend-to-RAM les clés restaient présentes, ce qui mettait un attaquant ayant saisi un ordinateur portable encore alimenté en position de les extraire de la RAM
- La découverte a commencé lors du nettoyage du port NixOS de
cryptsetup-suspendde Debian, en observant des entrées dans/proc/keys; un dump mémoire QEMU a confirmé qu’une volume key qui aurait dû être effacée restait présente - Des tests d’intégration NixOS et un patch d’avertissement pour cryptsetup ont été proposés ; les fonctions de sécurité comme la suppression des clés juste avant la mise en veille peuvent sembler fonctionner normalement, mais il est facile de manquer leur échec sans vérification réelle de la mémoire
Le problème des clés LUKS restant présentes pendant la suspension depuis Linux 6.9
- Depuis Linux 6.9, c’est-à-dire depuis mai 2024, un outil qui verrouille les disques lors de la mise en veille d’un ordinateur portable échouait silencieusement
- Le chiffrement complet de disque LUKS sert à protéger les données en cas de perte, saisie ou vol d’un ordinateur portable, mais dans ce cas les clés de chiffrement restaient en mémoire pendant la mise en veille
- Cela fonctionnait toujours lors d’un arrêt complet, mais l’impact est important car les portables sont souvent laissés en suspend-to-RAM plutôt qu’entièrement éteints
- Si quelqu’un mettait la main sur un ordinateur portable encore sous tension, les clés restées en mémoire pouvaient être exposées
- VeraCrypt a été mentionné comme logiciel remplissant le même rôle sous Windows, mais un commentaire ultérieur a corrigé que “canonical software” ne signifiait pas le logiciel le plus utilisé, mais une recommandation de référence dans le domaine de la sécurité informatique
Cause et patch d’une ligne
- La cause était le commit de refactoring du noyau Linux md: port block device access to file
- Le changement lui-même était un refactoring raisonnable et utile, mais il a provoqué une interaction à distance avec le code de chiffrement
- Le correctif proposé est un patch d’une ligne
- L’auteur du patch indique que, sans vérification formelle, il ne peut pas affirmer que ce patch est correct ni qu’il n’existe pas d’autres interactions à distance
- Des travaux de suivi pour éviter une récidive ont aussi été proposés
- Test automatique NixOS : test d’intégration destiné à détecter les futures régressions
- Demande de fusion cryptsetup : patch pour afficher un avertissement au lieu d’échouer silencieusement
Processus de découverte
- Le point de départ était un travail de nettoyage du port NixOS de
cryptsetup-suspendde Debian - L’original Debian comme le port NixOS présentaient tous deux une condition de concurrence gênante mais non nuisible, qui empêchait parfois l’ordinateur portable de se mettre en veille
- Pour résoudre cela, il a été tenté de ressusciter le patch noyau non fusionné de Pali Rohár, dm-crypt suspend/hibernation key wiping patch
- En examinant alors le code source de cryptsetup et du noyau, il a été constaté que, d’après la documentation, le keyring est attaché au thread appelant et supprimé lorsque ce thread se termine
- Mais des entrées apparaissaient dans
/proc/keys, qui n’était pas connu auparavant, ce qui a renforcé les soupçons - Finalement, une machine virtuelle QEMU a été lancée et sa mémoire dumpée, confirmant que la LUKS volume key qui aurait dû être effacée était toujours présente
Projet secure suspend-to-RAM pour NixOS
- Le projet
secure-suspend, publié séparément, fournit un secure suspend-to-RAM expérimental pour NixOS - Avec le chiffrement complet de disque classique, lorsque l’ordinateur portable est en veille, les clés restent en mémoire, ce qui peut le rendre vulnérable aux cold boot attacks ou aux techniques d’exfiltration de RAM
- Ce projet ressuscite l’ancien patch noyau de Pali Rohár afin d’effacer les clés de chiffrement LUKS lors de la mise en veille
- Il s’inspire de
cryptsetup-suspendde Debian, mais utilise un patch noyau pour éviter la condition de concurrence qui empêche parfois le portable de se mettre en veille, et ajoute des mesures préventives supplémentaires - Il prend entièrement en charge les root filesystems chiffrés et fournit aussi des tests d’intégration
- Le patch noyau et les outils en espace utilisateur peuvent être adaptés à d’autres distributions Linux
- Le projet est publié sous le nom secure-suspend
Pourquoi la vérification de la sécurité de la suspension est difficile
- Le fait de voir l’écran de verrouillage après une mise en veille ne signifie pas que le périphérique de stockage a réellement été verrouillé
- Si le disque est immédiatement accessible juste après la sortie de veille, cela indique que le périphérique de stockage n’a jamais été verrouillé
- Par exemple, on peut vérifier l’accès au disque avec un script qui continue de tourner derrière l’écran de verrouillage
- S’il est possible de se connecter par clé publique SSH après la mise en veille sans d’abord déverrouiller le stockage chiffré, il est facile de confirmer que le périphérique de stockage n’a pas été verrouillé
- Certains commentaires indiquent que les configurations par défaut d’Ubuntu ou Debian n’ont même pas tenté de fournir une telle protection
- Il faut vérifier séparément si la tentative de verrouillage du stockage a réellement fonctionné correctement
- Les horodatages des logs peuvent avoir été générés avant la mise en veille mais écrits après le réveil
- À l’inverse, des logs créés juste après le réveil, avant l’ajustement de l’heure système, peuvent sembler dater du moment de la mise en veille
- Que le verrouillage du stockage ait lieu juste avant la mise en veille ou juste après la reprise peut sembler identique pour l’utilisateur, mais c’est une différence décisive du point de vue de la sécurité
- Le test d’intégration NixOS démarre le système dans une machine virtuelle puis dumpe la mémoire afin de vérifier que la clé a bien été effacée pendant la mise en veille
1 commentaires
Avis de Hacker News
C’est bien un bug intéressant, mais le titre donne un peu l’impression d’être du clickbait
D’après ce que j’ai compris,
cryptsetup luksSuspendest plutôt une extension créée par Debian qu’une fonctionnalité officiellement prise en charge, donc j’ai l’impression que cette régression n’a touché que DebianJe ne sais pas trop si l’on peut reprocher au noyau un problème sur une fonctionnalité qui n’est ni prise en charge ni largement testée
Cela dit, c’est impressionnant, et c’est une bonne chose que des tests aient été ajoutés pour éviter que cette régression ne réapparaisse. Je suis aussi d’accord avec l’avis de l’OP selon lequel NixOSTests est vraiment excellent
Mais à lire le titre seul, on a l’impression d’un problème répandu, et pas limité à une distribution précise
C’est vrai. Les personnes qui utilisent la configuration par défaut ne sont pas affectées, puisqu’elles ne s’attendent de toute façon pas à ce que la clé du volume soit en sécurité pendant la suspension
La solution de Debian a été portée vers plusieurs autres distributions, probablement la plupart, et il devait aussi y avoir pas mal de gens maintenant leurs propres ports
La page de manuel
thread-keyring(7)promet que « le trousseau de clés du thread est détruit quand le thread qui le référence se termine »Le projet cryptsetup s’appuyait sur cette propriété dans le mécanisme qui fait passer les clés de l’espace utilisateur à l’espace noyau, mais le noyau 6.9 a introduit une régression qui a cassé cette propriété
Je l’ai déjà utilisé de temps en temps sur Arch et openSUSE, et il existe clairement aussi dans des distributions autres que Debian
Vous pensez probablement à l’intégration automatique avec la suspension système, mais ce n’est pas le cœur du sujet.
luksSuspendest documenté comme supprimant les clés de la mémoire système, et ce comportement a cessé de fonctionner à cause du patch de refactorisation concerné dans Linux 6.9Cela dit, en pratique, on peut aussi voir cela comme un bug côté cryptsetup. Il dépendait d’un comportement de durée de vie très précis des clés du keyring du noyau, et on peut soutenir qu’il aurait dû les effacer plus explicitement depuis l’espace utilisateur
[1]: https://gitlab.com/cryptsetup/cryptsetup/-/commit/3cea5dcc7b...
[2]: https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/docs/v1...
[3]: https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/93...
Vous parlez probablement des machines qui, après
luksSuspend, déclenchent une suspension RAM d’une manière réellement utile ; cela visait Debian au départ, puis c’est arrivé aussi sur Arch, mais dans les deux cas ce n’était pas le comportement par défautJe ne vois pas bien d’autre méthode. Avec la veille, c’est-à-dire le RAM suspend, tout est stocké en RAM et chiffré, mais il me semble que la clé maître reste dans la mémoire du noyau
En revanche, avec l’hibernation, c’est-à-dire la suspension sur disque, tout le contenu de la RAM, y compris la clé maître, est écrit sur le disque, chiffré, puis la RAM est effacée
Au réveil, il faut ressaisir la phrase de passe afin de déchiffrer la clé maître et de recharger le contenu du disque en mémoire
Mais Debian a d’abord créé un module optionnel
cryptsetup-suspend, qui exécute la commandeluksSuspendcensée effacer les clés de la mémoire, puis redemande la phrase de passe à la repriseJusqu’au noyau 6.8, cela fonctionnait comme décrit, mais à partir du noyau 6.9, cela a cessé de fonctionner silencieusement
Si cette fonction est activée, les attaques par démarrage à froid appartiennent au passé. Elle est généralement désactivée par défaut parce qu’elle réduit les performances de la RAM d’environ 0,5 %
Puisqu’on ne ressaisit pas le mot de passe de démarrage après une sortie de veille, la clé de chiffrement est forcément encore en mémoire
cryptsetup-luksSuspendCe n’est pas un problème qui me préoccupe beaucoup
La seule raison pour laquelle j’utilise le chiffrement de disque, c’est pour ne pas avoir à craindre que quelqu’un fouille dans mes documents fiscaux ou mes informations de carte bancaire quand je vends mon ordinateur portable
Bien sûr, j’efface aussi l’ordinateur, mais si les données sont chiffrées au niveau du disque, j’estime que le risque de les récupérer avec des outils d’analyse forensique, par exemple, est très faible
LUKS utilise un algorithme anti-forensique qui exige la clé de volume complète pour ouvrir le disque. Les blocs de clé sont combinés par un algorithme de diffusion puis XORés pour produire la véritable clé maître ; en théorie, effacer un seul secteur de la clé de volume devrait rendre l’ensemble irrécupérable
Autrement dit, s’il manque ne serait-ce qu’un bloc de la clé, on ne peut pas facilement deviner le reste
Je ne suis absolument pas expert en sécurité, mais quand on voit ces derniers temps des failles de sécurité critiques découvertes régulièrement, du genre « une ligne de vérification en C, à cheval sur plusieurs fichiers, oubliée pendant un refactoring », le postulat même d’une énorme base de code C open source sûre me paraît douteux
Ce n’est pas un problème propre au C, mais il me semble qu’en C, en particulier, il est plus difficile d’imposer et de suivre des invariants de manière cohérente, surtout lors de changements de code
Je ne sais pas non plus si la programmation fonctionnelle, qui encode les invariants dans les types, est une solution réellement extensible. Model checking ? fuzzing par LLM ? Moins de primitives, avec des frontières plus nettes ? seLinux a-t-il été « vérifié » de cette façon ?
En gros, c’est de cet ordre :
original:
DoTheThing()new:
DoTheThingSlightlyDifferentButKeepMyCredentialsAlive()fix:
DoTheThingSlightlyDifferentButDoInFactNOTKeepMyCredentialsAlive()D’expérience, une grande partie des bugs difficiles viennent de violations d’invariants système de haut niveau, et ce n’est pas le genre de chose qui me semble automatisable
Même avec quelque chose comme Lean, on peut prouver qu’un programme satisfait une propriété donnée, mais encore fallait-il avoir pensé à cette propriété au préalable. La preuve ne découvre pas les invariants à votre place
Si l’on avait pensé à la propriété de sécurité pertinente, il n’aurait sans doute pas été difficile d’écrire un test de régression. À mon avis, la vraie difficulté n’est pas d’exprimer l’implémentation de façon sûre, mais de comprendre qu’il existe une propriété que l’implémentation doit préserver
Le problème est qu’une meilleure auditabilité ne signifie pas automatiquement qu’il y aura davantage d’audits
Il faut que des personnes suffisamment compétentes y consacrent suffisamment de temps
C’est un bug dû au croisement de préoccupations et à un manque de connaissance inter-domaines. En Lisp ou en assembleur, cela aurait probablement été pareil
Autrement dit, il n’existe pas de définition rigoureuse de ce qui constitue un problème de sécurité
Une agence fédérale avait-elle désespérément besoin d’un moyen d’obtenir les clés ? Est-ce une bugdoor ? Le commit a-t-il été retracé ?
J’ai vu beaucoup de schémas de ce genre récemment, et je commence à devenir un peu méfiant. C’est peut-être aussi simplement parce que les gens y sont plus sensibles et les signalent davantage
Le fait que la clé de chiffrement soit en mémoire ne signifie pas forcément qu’elle peut être extraite. C’est plutôt qu’elle est restée inutilement et indéfiniment à un endroit où elle n’aurait pas dû se trouver
Ce genre de régression est facile à rater, parce que tout continue à « fonctionner ». Les bugs de sécurité ne se manifestent pas souvent d’eux-mêmes
C’était aussi intéressant à écrire, et cela a permis de lancer
git-bisectpour trouver le refactoring précis du noyau qui a introduit ce bug : https://github.com/NixOS/nixpkgs/pull/532499Sur mon ordinateur portable Fedora, j’ai configuré Linux pour hiberner sur disque 15 minutes après la mise en veille. Si l’on coupe l’alimentation de la mémoire, ce bug spécifique à Debian n’est plus un problème
L’extension des outils Linux par Debian est séduisante en théorie, mais si l’on s’inquiète vraiment des attaques par cold boot, il faut effacer de la mémoire non seulement les clés LUKS, mais aussi toutes les clés et tous les documents importants
Donc, au final, la seule vraie façon de se protéger contre le cold boot, c’est l’hibernation
À ma connaissance, ce n’est pas pratique sans utiliser un TPM. Et avec un TPM, on remet en fait son destin entre les mains du TPM
Il suffit d’imaginer à quoi aurait ressemblé ce fil HN si cette vulnérabilité avait été présente dans un système d’exploitation commercial
Le commentaire le plus haut aurait certainement dit qu’Applosoft ne se soucie plus de la qualité logicielle, ou que « voilà ce qui arrive quand on laisse entrer les déchets de vibe coding dans l’OS »
Les commentaires en dessous auraient été des théories du complot sur le complexe industriel de la surveillance et la NSA ; ailleurs ce serait délirant, mais pas sur HN
Je ne comprends pas pourquoi quelque chose d’aussi important n’est pas testé à chaque build