1 points par GN⁺ 5 시간 전 | 1 commentaires | Partager sur WhatsApp
  • ssh-init-vm injecte via cloud-init une clé hôte SSH privée temporaire pour empêcher les attaques de l’homme du milieu lors de la première connexion SSH à une nouvelle VM, et ne lui accorde de confiance que le temps de générer ou récupérer les clés hôtes de long terme
  • Cela fonctionne aussi sur des VPS ou clouds sans fonctionnalité dédiée de protection d’accès, comme Hetzner Cloud, et ne nécessite que cloud-init, largement pris en charge
  • Dans le modèle habituel de Trust On First Use, si l’on saisit yes à la question de ssh « The authenticity of host [...] can't be established », un attaquant peut proxifier le trafic ou fournir une machine se faisant passer pour la VM de l’utilisateur
  • Mettre directement une clé hôte SSH privée de long terme dans les userdata cloud-init aide à authentifier la première connexion, mais peut exposer des données de clé sensibles via le service de métadonnées, le SSRF, les systèmes du fournisseur cloud ou le poste de travail de l’administrateur
  • ssh-init-vm place la clé temporaire dans un répertoire temporaire et ne l’ajoute pas à ~/.ssh/known_hosts; il n’enregistre pas non plus brut de décoffrage la sortie de la VM et s’appuie sur la rotation des clés hôtes d’OpenSSH pour consigner la clé de long terme

Problème d’exposition des userdata cloud-init

  • Injecter une clé hôte SSH privée de long terme avec cloud-init permet de placer la clé publique dans ~/.ssh/known_hosts afin d’authentifier la première connexion, mais la clé privée peut fuiter par plusieurs chemins
  • Un processus arbitraire à l’intérieur de la VM peut récupérer les userdata depuis le service de métadonnées, généralement lisible, et sur une VM Hetzner le contenu cloud-init peut être visible via http://169.254.169.254/hetzner/v1/userdata
  • Un attaquant peut exploiter un SSRF pour pousser un processus à divulguer les métadonnées, et ce blocage n’est pas toujours appliqué même dans des environnements dotés de protections dédiées
  • Les userdata peuvent aussi être exposés dans d’autres systèmes du fournisseur cloud; dans la documentation de son API de création de serveurs, Hetzner indique explicitement de ne pas y stocker « passwords or other sensitive information »
  • Le poste de travail de l’administrateur peut lui aussi être un endroit où les userdata cloud-init restent ou transitent; inclure une clé privée de long terme crée donc un risque d’exposition pendant toute la durée de validité de la clé

Analyse de sécurité et modèle de menace

  • Le point de départ est la confiance dans le protocole OpenSSH et son implémentation, sans dépendre de la capacité de l’administrateur à détecter une attaque
  • Protection contre un attaquant réseau

    • Ce qui est protégé, c’est l’intégrité du poste de travail de l’administrateur et de la VM
    • L’attaquant est un homme du milieu qui contrôle entièrement le réseau et peut apprendre les userdata cloud-init à n’importe quel moment après la fin du script, qu’il réussisse ou échoue
    • La protection tient au fait que l’attaquant ne connaît pas les éléments de clé au moment où ils ont encore de la valeur
    • Le script les stocke dans un répertoire temporaire afin d’éviter toute utilisation accidentelle de la clé hôte SSH temporaire, et n’ajoute pas cette clé temporaire à ~/.ssh/known_hosts
  • Si le poste de travail de l’administrateur est compromis

    • Ce qui est protégé se limite à la VM et à sa clé hôte SSH privée de long terme
    • L’attaquant contrôle totalement le réseau et le poste de travail de l’administrateur, mais on suppose qu’il n’accède pas à la VM réelle
    • La clé hôte SSH privée de long terme n’a jamais résidé sur le poste de travail de l’administrateur et, comme l’attaquant n’accède pas à la VM réelle, il ne peut pas obtenir la clé hôte de long terme de la VM
    • Si l’attaquant accède à la VM réelle, il a de fortes chances de pouvoir lire les clés hôtes SSH avec une commande comme ssh root@<VM> cat /etc/ssh/ssh_host_*
  • Si la VM ou le fournisseur est compromis

    • Ce qui est protégé se limite à l’intégrité du poste de travail de l’administrateur
    • L’attaquant peut contrôler entièrement le réseau ainsi que la VM ou le fournisseur
    • Même dans ce cas, l’intégrité du poste de travail de l’administrateur est protégée grâce à l’hypothèse qu’OpenSSH reste sûr
    • En défense supplémentaire, le script n’écrit pas directement la sortie de la VM dans ~/.ssh/known_hosts, mais s’appuie sur la rotation des clés d’OpenSSH pour y inscrire la clé hôte SSH de long terme
    • Cette approche empêche un hôte compromis d’injecter des données malveillantes dans le parseur de known_hosts, et garantit que seules les clés réellement contrôlées par la VM sont inscrites dans ~/.ssh/known_hosts
    • Elle permet aussi de gérer correctement des options OpenSSH comme HashKnownHosts, ainsi que d’éventuelles options liées à l’avenir

Conditions dans lesquelles une véritable attaque MITM peut réussir

  • Le succès d’une attaque de l’homme du milieu dépend du fait que l’utilisateur détecte réellement que toutes les connexions pointent dès le départ vers la mauvaise machine, refuse de saisir un mot de passe, ou configure l’agent ssh ou le transfert X11
  • D’après une version simplifiée des conditions décrites par ssh-mitm, la réussite est probable si l’attaquant peut tromper l’utilisateur en lui donnant accès à une machine sous son contrôle au lieu de l’hôte cible réel
  • L’attaque réussit aussi si l’attaquant parvient à tromper l’utilisateur pour obtenir des informations lui permettant de se connecter au véritable hôte
    • Si l’utilisateur se connecte à la machine de l’attaquant avec un mot de passe, l’attaquant peut réussir
    • Si l’utilisateur se connecte avec n’importe quel mode d’authentification puis saisit un mot de passe à l’invite, l’attaquant peut réussir
    • Si l’utilisateur se connecte avec n’importe quel mode d’authentification et transmet une connexion ssh-agent, l’attaquant peut réussir
  • En l’absence de ces conditions, l’attaquant a besoin d’un accès au véritable hôte pour tromper l’utilisateur, mais il a de fortes chances d’échouer car les seules entrées de l’utilisateur ne lui permettent pas de s’y connecter
  • Si l’utilisateur transfère une connexion X11, l’attaquant peut aussi réussir à attaquer le poste de travail de l’administrateur, indépendamment du mode d’authentification

1 commentaires

 
GN⁺ 5 시간 전
Commentaires Lobste.rs
  • C'est sympa de pouvoir l'automatiser. En manuel, je vérifiais jusqu'ici l'empreinte SSH du serveur via un canal séparé dans la console du fournisseur cloud
    Je n'ai pas énormément d'instances cloud à gérer, donc quelques étapes manuelles dans le provisioning ne me posent pas de problème

    • Cette méthode fonctionne aussi. C'est juste que le fournisseur que j'utilisais n'avait pas branché cette partie, et qu'en même temps j'étais en train de créer un script d'automatisation de configuration
  • Si vous avez automatisé votre zone DNS, il y a une autre approche possible : créer un jeton à usage unique avec un périmètre très restreint, qui autorise par exemple uniquement la création d'un enregistrement pour my-server-hostname.example.net
    Transmettre ce jeton au serveur via cloud-init, puis laisser le serveur publier sa clé SSH publique dans un enregistrement SSHFP du DNS. Le client SSH peut ensuite valider automatiquement l'enregistrement SSHFP, à condition que la zone DNS soit signée avec DNSSEC
    Ce flux permet au serveur de conserver sa clé d'hôte SSH privée tout en évitant les rotations de clés. La plupart des fournisseurs DNS ne prennent pas en charge des jetons d'accès jetables aussi fins, mais on peut placer un petit service web interne qui valide le jeton puis effectue l'appel API avec un jeton permanent, non jetable et à portée non restreinte. Le serveur SSH n'a pas accès à ce jeton permanent

    • C'est créatif, et avec des jetons à usage unique pour des services sur mesure, ça devrait être assez souple pour bien fonctionner
      Cela dit, je préférerais probablement générer des certificats SSH plutôt qu'écrire dans un domaine DNSSEC, et à partir de là cela devient surtout une question de savoir quelle solution convient le mieux à tel ou tel environnement
      Je me demande si vous connaissez un logiciel ou un fournisseur capable de générer des jetons aussi souples, ou s'il faut développer une partie de la solution soi-même
  • C'est assez propre. En revanche, on dirait que le mois et le jour sont inversés dans la date de l'article

    • C'est toujours un plaisir de travailler avec OpenSSH, et la notation mois/jour a été corrigée
  • J'avais autrefois créé un service expérimental pour explorer le même problème de l'œuf et de la poule avec SSH
    Il crée des enregistrements DNS SSHFP à la demande ; si ça vous intéresse : https://github.com/tedb/sshfp

  • Ravi de voir qu'on traite le service de métadonnées 169.254.169.254 de façon relativement cohérente entre fournisseurs de VM. On peut le constater dans les différentes entrées cloudinit/source/DataSource*.py du code source de cloud-init
    De mon côté, je ressens de plus en plus de fatigue vis-à-vis de la conception et des limites de cloud-init. Je m'intéresse à l'unification de la configuration système entre machines virtuelles QEMU locales, machines distantes, conteneurs et matériel physique
    Le projet arch-boxes montre comment l'image ArchLinux pour cloud-init est construite, avec un ensemble de scripts shell très simples. En adaptant cette approche avec guestfish ou µvm, on peut utiliser exactement les mêmes scripts pour des images compatibles OCI, des images disque pour QEMU ou pour des fournisseurs cloud, et le provisioning de nouvelles machines physiques
    Avec quelques options QEMU, on peut reproduire la même approche sans dépendre de cloud-init. À ma connaissance, systemd.system-credentials ne permet pas de transmettre des clés d'hôte temporaires ; il n'existe que des credentials pour ~/.ssh/authorized_keys, comme ssh.authorized_keys.root
    En revanche, on peut créer un fichier unit exécuté au stade initrd ou conjointement à systemd-firstboot.service. Ce fichier unit peut être préintégré dans l'image ou injecté temporairement via des credentials systemd.extra-unit.*, puis activé avec l'option de ligne de commande noyau systemd.wants=…. Dans QEMU, on peut simuler la présence d'un service de métadonnées avec -netdev user,id=metadata,net=169.254.0.0/16,dhcpstart=169.254.0.15,guestfwd=tcp:169.254.169.254:80-cmd:…. Il faudra toutefois probablement activer l'interface générée, et là encore il peut être préférable de le faire avec un fichier unit temporaire
    Cela offre une flexibilité considérable avec une complexité relativement faible lorsqu'il s'agit d'appliquer une configuration système cohérente sur plusieurs types de « machines ». En pratique, pour ce besoin précis, la meilleure approche me semble être que l'outil de création d'image produise des images machine avec des clés d'hôte fixes, puis installe comme service systemd un script personnalisé de rotation des clés d'hôte, exécuté au premier redémarrage ou à l'extinction

    • Sous ArchLinux, si vous activez le HOOK systemd dans /etc/mkinitcpio.conf, vous pouvez, et en pratique devez, écrire des fichiers unit SystemD pour ce qui doit s'exécuter dans l'initrd
      À l'usage, c'est à peine plus contraignant que d'écrire {/etc,/usr/lib}/initcpio/hooks
      Mais comme il est assez simple d'activer systemd-networking et systemd-resolved dans l'initrd, celui-ci peut prendre en charge le démarrage du système et planifier des tâches avant le basculement vers le système de fichiers racine
      Bien sûr, sur du matériel physique comme un portable, cela colle moins bien parce que la connexion Wi‑Fi peut nécessiter quelque chose comme NetworkManager, mais cela fonctionne bien pour les VM QEMU et les VM hébergées, et beaucoup de tâches de démarrage système trouvent naturellement leur place dans cet espace
      L'objectif est d'éviter de dépendre de cloud-init, de ne pas être lié à un fournisseur cloud particulier, d'obtenir de la cohérence entre machines physiques, conteneurs, VM locales et VM hébergées, et de réduire en pratique les dépendances à peu près à SystemD
    • Pour un très petit outil qu'on peut simplement étendre avec les fonctionnalités voulues, quelque chose comme https://github.com/the-maldridge/shinit/ pourrait aussi convenir, non ?