2 points par GN⁺ 12 일 전 | 1 commentaires | Partager sur WhatsApp
  • L’intégration SSH utilise des escape sequences du terminal pour communiquer avec le shell distant, et à cause de cette architecture, une sortie terminal ordinaire peut elle aussi être interprétée comme le protocole conductor
  • Le problème central est un échec de confiance : non seulement le véritable conductor distant, mais aussi des fichiers malveillants, bannières, MOTD ou réponses serveur peuvent se faire passer pour conductor via de faux DCS 2000p et OSC 135
  • Le simple fait d’exécuter cat readme.txt peut suffire : si un faux transcript conductor est rendu, iTerm2 enchaîne lui-même le flux getshell · pythonversion · run(...), et la sortie d’attaque n’a plus qu’à imiter les réponses
  • L’exploit tire parti d’une confusion autour du PTY : une commande base64 écrite dans le PTY tombe en entrée texte clair du shell local lorsqu’il n’y a pas de véritable SSH conductor, et peut être exécutée quand le dernier chunk est interprété comme le chemin ace/c+aliFIo
  • Le correctif a été intégré dans le commit du 31 mars a9e745993c2e2cbb30b884a16617cd5495899f86, mais au moment de la publication il n’était pas encore inclus dans une stable release, créant une fenêtre d’exposition avant la diffusion du patch

Contexte de l’intégration SSH d’iTerm2

  • L’iTerm2 SSH integration est une fonctionnalité conçue pour mieux comprendre les sessions distantes, avec un fonctionnement basé sur l’envoi d’un petit script helper appelé conductor sur le shell distant
    • démarrage de l’intégration SSH via it2ssh
    • transmission de conductor, script distant de bootstrap, via la session SSH existante
    • ce script distant joue ensuite le rôle de pair du protocole iTerm2
  • iTerm2 et conductor distant ne communiquent pas comme un service réseau classique, mais en s’échangeant des escape sequences sur les E/S du terminal
    • détection du shell de connexion
    • vérification de la présence de Python
    • changement de répertoire
    • upload de fichiers
    • exécution de commandes

Fonctionnement du PTY

  • Les émulateurs de terminal modernes sont des versions logicielles des anciens terminaux matériels, chargés de l’affichage, de l’entrée clavier et de l’interprétation des séquences de contrôle du terminal
  • Les shells et programmes en ligne de commande s’attendent toujours à un périphérique ressemblant à un vrai terminal, d’où l’usage d’un PTY fourni par l’OS
    • le PTY est un pseudoterminal placé entre l’émulateur de terminal et le processus au premier plan
  • Dans une session SSH classique, iTerm2 écrit des octets dans le PTY, puis le processus au premier plan, ssh, les transmet à la machine distante, où conductor les lit sur stdin
  • Quand iTerm2 envoie une commande au conductor distant, localement cela revient donc à écrire des octets dans le PTY

Protocole conductor

  • Le protocole de transport de l’intégration SSH repose sur des escape sequences du terminal
  • Deux éléments sont centraux
    • DCS 2000p sert à hooker le SSH conductor
    • OSC 135 sert aux messages conductor pré-encapsulés
  • Au niveau du code source, DCS 2000p amène iTerm2 à créer un conductor parser, qui traite ensuite les messages OSC 135
    • begin <id>
    • lignes de sortie de commande
    • end <id> <status> r
    • unhook
  • Un conductor distant légitime peut donc communiquer avec iTerm2 uniquement par la sortie terminal

Vulnérabilité principale

  • La vulnérabilité est fondamentalement un échec de confiance : iTerm2 accepte comme protocole SSH conductor des sorties terminal qui ne proviennent pas d’une véritable session conductor de confiance
  • En conséquence, une sortie terminal non fiable peut se faire passer pour un conductor distant
    • fichier malveillant
    • réponse serveur
    • bannière
    • MOTD
  • Une entrée d’attaque peut afficher un faux hook DCS 2000p et de fausses réponses OSC 135, et iTerm2 se comporte alors comme si un véritable échange d’intégration SSH était en cours

Fonctionnement de l’exploit

  • Le fichier d’exploit contient un faux transcript conductor
  • Quand l’utilisateur exécute cat readme.txt, iTerm2 rend le fichier, mais celui-ci ne contient pas un simple texte : il inclut aussi
    • une fausse ligne DCS 2000p signalant une fausse session conductor
    • de faux messages OSC 135 répondant aux requêtes d’iTerm2
  • Une fois le hook accepté, iTerm2 lance le workflow normal de conductor : dans le code amont, Conductor.start() envoie immédiatement getshell(), puis, en cas de succès, pythonversion()
  • L’attaque n’a pas besoin d’injecter ces requêtes : c’est iTerm2 qui les émet de lui-même, et la sortie malveillante n’a qu’à imiter les réponses

Déroulement de la machine à états

  • Les faux messages OSC 135 sont minimaux, mais ordonnés avec précision
    • début du corps de commande pour getshell
    • renvoi d’une ligne ressemblant à une sortie de détection du shell
    • fin réussie de cette commande
    • début du corps de commande pour pythonversion
    • fin en échec de cette commande
    • unhook
  • Ce simple enchaînement suffit pour faire entrer iTerm2 dans son chemin de repli normal, puis lui faire considérer que le workflow d’intégration SSH a été suffisamment mené à bien pour passer à l’étape suivante
  • L’étape suivante consiste à construire puis envoyer la commande run(...)

Rôle de sshargs

  • Le faux hook DCS 2000p contient plusieurs champs, dont sshargs, contrôlé par l’attaquant
  • Cette valeur est ensuite utilisée par iTerm2 comme matière première de commande lors de la construction de sa requête run ... pour conductor
  • L’exploit choisit sshargs de façon à ce qu’au moment où iTerm2 encode en base64 les données suivantes
    • run <padding><magic-bytes>
  • le dernier chunk de 128 octets devienne ace/c+aliFIo
  • Cette chaîne n’est pas arbitraire : elle est choisie pour satisfaire simultanément deux conditions
    • être une sortie valide du chemin d’encodage de conductor
    • être un nom de chemin relatif valide

La confusion PTY qui rend l’exploit possible

  • Dans une session normale d’intégration SSH, iTerm2 écrit dans le PTY des commandes conductor encodées en base64, puis ssh les transmet au conductor distant
  • Dans le scénario d’exploit, iTerm2 écrit de la même manière les commandes dans le PTY, sauf qu’en l’absence de véritable SSH conductor, le shell local les reçoit comme une entrée en texte clair
  • Dans la session enregistrée, on observe
    • getshell sous forme base64
    • pythonversion sous forme base64
    • puis une longue payload run ... encodée en base64
    • dont le dernier chunk est ace/c+aliFIo
  • Les chunks précédents échouent comme commandes sans signification, mais le dernier s’exécute si ce chemin existe localement et est exécutable

Procédure de reproduction

  • Le PoC original basé sur des fichiers peut être reproduit avec genpoc.py
    • python3 genpoc.py
    • unzip poc.zip
    • cat readme.txt
  • Cette procédure génère deux fichiers
    • un script helper exécutable nommé ace/c+aliFIo
    • un readme.txt contenant les séquences malveillantes DCS 2000p et OSC 135
  • Le premier fichier pousse iTerm2 à communiquer avec le faux conductor, et le second fournit au shell la cible réellement exécutée à l’arrivée du dernier chunk
  • Pour que l’exploit réussisse, cat readme.txt doit être exécuté dans le répertoire où se trouve ace/c+aliFIo, afin que le dernier chunk contrôlé par l’attaquant soit interprété comme un chemin réellement exécutable

Divulgation et calendrier du patch

  • signalement du bug à iTerm2 le 30 mars
  • correctif finalisé le 31 mars dans le commit a9e745993c2e2cbb30b884a16617cd5495899f86
  • au moment de la rédaction, le correctif n’était toujours pas inclus dans une stable release
  • après l’intégration du commit de correctif, une tentative de reconstruction complète de l’exploit à partir du seul patch a été menée
    • les prompts utilisés figurent dans prompts.md
    • le résultat est genpoc2.py
    • son fonctionnement est très proche de genpoc.py

Question sur le moment de la divulgation

  • La divulgation est intervenue avant que le correctif n’atteigne une stable release, ouvrant une période où la plupart des utilisateurs ne pouvaient pas réellement être protégés alors même que la vulnérabilité devenait publique
  • Une telle tension sur le calendrier de publication nécessite une justification claire
  • Deux semaines, c’est à la fois trop court pour espérer une diffusion significative du correctif, et trop court aussi pour justifier qu’une divulgation anticipée était nécessaire pour forcer la réponse
  • En pratique, cela a créé une fenêtre de divulgation où la faille était largement connue alors que le correctif n’était pas encore accessible aux utilisateurs qui en avaient réellement besoin
  • Une meilleure option aurait été d’attendre que la version corrigée arrive réellement chez les utilisateurs, ou d’expliquer clairement pourquoi une exposition anticipée était nécessaire, mais aucune de ces deux conditions n’a été remplie

1 commentaires

 
GN⁺ 12 일 전
Commentaires sur Hacker News
  • Je me suis demandé pourquoi cela avait été rendu public maintenant alors qu’aucun correctif n’était encore sorti sur la version stable. Cela ne faisait que 18 jours que c’était signalé en amont, et l’article de blog était bien plus détaillé que le commit public, au point de sembler augmenter la possibilité d’une exploitation réelle. L’auteur a montré qu’il avait pu produire un exploit à partir du seul commit amont avec un LLM, mais j’estime quand même que ce billet a davantage accru la visibilité de la faille

    • Je ne suis pas la personne qui a découvert la vulnérabilité, mais l’auteur du blog. J’ai pu créer un exploit à partir du seul commit amont, et je pense que n’importe qui surveillant les commits d’iTerm2 aurait pu faire de même. Mon intention était d’augmenter la visibilité de cette vulnérabilité, et c’est effectivement ce qui s’est passé. Au départ, l’auteur d’iTerm2 ne pensait pas que c’était assez grave pour justifier une publication d’urgence, mais il semble maintenant en train de reconsidérer la question
    • Je pense qu’il y a des exceptions au délai de divulgation lorsqu’une exploitation active est soupçonnée, ou lorsque le correctif est déjà public, par exemple via un commit git, ce qui permet de fabriquer rapidement un exploit. Dans ce genre de situation, la communauté préfère plutôt une divulgation publique de la vulnérabilité
    • À partir du moment où le commit est public, je considère que le secret n’existe plus. Se retenir d’en parler n’aide que les attaquants et affaiblit la sécurité du côté défensif
    • J’ai l’impression que la notion traditionnelle de période d’embargo va perdre de plus en plus de son sens à cause de l’IA. Si même des modèles ouverts et bon marché peuvent retrouver des vulnérabilités, il est naturel de supposer que les attaquants les ont déjà trouvées de la même manière
    • J’ai l’impression que ce bug renforce mon argument en faveur d’une réduction de la fenêtre de mise à jour. Même si un bug très obscur est d’abord repéré par un modèle puissant comme Claude, dès qu’un correctif arrive sur git, des modèles plus petits peuvent aussi le redécouvrir facilement. Je ne serais pas surpris que, dans les 1 à 2 prochaines années, l’intervalle entre la publication d’un commit et un véritable scan de ports tombe à quelques heures, voire quelques minutes. De ce point de vue, les SaaS fermés ont un avantage, parce que les changements ne sont pas visibles et qu’une fois déployés, les connaître rapporte peu en pratique
  • C’est un beau travail, mais ce n’était pas si surprenant. C’est un problème récurrent dans les applications de terminal riches en fonctionnalités, et plusieurs vulnérabilités similaires ont été publiées au cours des 15 dernières années. Des outils comme less ou vim n’y ont pas échappé, et une bonne partie de ces problèmes relèvent davantage de bugs logiques que de sécurité mémoire, donc les réécrire en Rust ne les résoudrait pas automatiquement. D’un côté, on veut que les outils au niveau de l’OS restent simples et prévisibles, mais de l’autre, on veut de jolies couleurs, des animations et une personnalisation sans fin. Et maintenant, on y ajoute même des agents IA, si bien qu’un fichier texte malveillant peut se contenter de contenir des phrases comme "ignore les instructions précédentes"

    • Je pense que le problème d’iTerm2, la prompt injection, la SQL injection et le XSS relèvent au fond du même type d’erreur. Le problème central, c’est de mélanger dans le même flux des données in-band et des données de contrôle out-of-band. Si on commence à reconnaître ce schéma comme un signal de danger, on mettra peut-être moins facilement des commandes de contrôle à côté du contenu utilisateur
    • Une partie du problème vient, à mon avis, d’interfaces vieillissantes. Il faudrait une API de terminal moderne qui ne dépende pas de séquences de commande in-band, quelque chose d’aussi programmable qu’une GUI tout en conservant la simplicité d’usage d’un terminal distant classique
    • Je me suis demandé si des interfaces de terminal riches comme Claude Code avaient des vulnérabilités comparables. Plutôt que de greffer de force des fonctionnalités sur un protocole de terminal textuel, la bonne solution serait à mon avis de concevoir dès le départ un protocole GUI aux types et à la sémantique explicites. Cela éviterait d’interpréter ensemble les données utilisateur et le code central de l’interface. Mais dans la réalité, c’est souvent l’économie qui pousse à améliorer l’existant plutôt qu’à adopter un nouveau protocole
    • Cela m’a rappelé la blague HAL 9000 du genre « Désolé Dave, je ne peux pas permettre ça »
    • Je me souviens que xterm avait aussi permis des attaques similaires via le code d’échappement du titre de fenêtre
  • Cela m’a rappelé l’époque du PDP-10. Un collègue avait découvert qu’en maintenant la touche retour arrière, le gestionnaire de terminal effaçait aussi des caractères avant le début du tampon, puis qu’en utilisant le caractère d’échappement qui effaçait toute une ligne, on pouvait carrément faire tomber le système d’exploitation

    • En entendant cette histoire, j’ai pensé à Real Life Tron on an Apple IIgs, et je trouve qu’il y a un certain charme étrange qui n’apparaît que lorsque la mémoire système est mal interprétée
    • L’usage de control+u pour effacer la ligne est peut-être une habitude relativement récente. Autrefois, @ servait à effacer la ligne et # à effacer le dernier caractère, et aujourd’hui encore, le comportement des touches varie pas mal selon les systèmes
  • Il y avait déjà eu quasiment le même problème de sécurité iTerm2 il y a 6 ans

    • On dirait donc qu’ils n’ont rien appris
  • Je suis l’auteur d’iTerm2. Ce problème peut servir de maillon dans une chaîne d’exploitation, mais dire comme dans le titre que c’est en soi un énorme danger me semble exagéré. Je suis actuellement en voyage en famille, et je publierai une version corrigée à mon retour

    • Je ne suis pas le découvreur de la vulnérabilité, mais l’auteur du blog. Merci d’avoir accepté de publier un correctif. J’ai été surpris qu’il n’y ait pas eu de version officielle alors que ce bug affecte aussi des workflows ordinaires et apparemment inoffensifs, et comme le commit de correctif présentait le problème comme hypothétique, je voulais montrer que ce n’était pas le cas. Je suis heureux qu’une version corrigée soit prévue
    • J’utilise iTerm2 avec reconnaissance. Merci d’avoir répondu, et bonnes vacances
    • J’aime vraiment beaucoup iTerm2, merci
  • Je n’ai pas été surpris de voir un bug subtil apparaître dans un système complexe qui utilise des scripts de bootstrap, des agents conductor distants, des séquences d’échappement, etc. Quand on assemble des composants d’une manière qui n’était pas prévue à l’origine, ce genre de problème arrive facilement. Je comprends le problème comme suit : si une sortie non fiable affichée à l’écran, par exemple un fichier texte ou une bannière serveur, contient des codes spéciaux, la structure les traite sans vérifier leur provenance

  • J’ai eu l’impression d’avoir déjà vu cette histoire. L’intégration SSH d’iTerm2 avait déjà été à l’origine d’un CVE, et cela m’a rappelé CVE-2025-22275. Il y a eu d’autres cas auparavant, et l’ancien problème mentionné dans ce fil concernait l’intégration tmux. Peut-être vaudrait-il mieux intégrer ce genre de fonctionnalités d’intégration de façon un peu moins agressive

    • L’approche de l’intégration SSH de ghostty m’inspire des inquiétudes similaires. Il vaudrait mieux, à mon avis, collaborer avec upstream ncurses pour améliorer terminfo
    • Ce genre de chose s’est répété à plusieurs reprises
  • Le titre est trop sensationnaliste. Le problème, ce n’est pas cat, mais l’intégration SSH d’iTerm, et cette architecture avec un canal de contrôle non séparé du flux de données semble risquée. Si on n’utilise pas cette fonctionnalité et qu’on se contente de SSH classique, cela semble globalement aller

    • J’ai donc modifié le titre sur HN pour le rendre un peu moins alarmiste
  • Les anciens émulateurs de terminal permettaient même le rebinding clavier via des codes d’échappement. C’est pour cela qu’il était quasiment évident de ne pas faire cat sur des fichiers non fiables, mais de les ouvrir avec des outils comme less

    • Je me souviens que certains terminaux permettaient, via de simples séquences d’échappement, d’écrire des fichiers ou même d’exécuter des programmes. Encore aujourd’hui, éviter de laisser passer arbitrairement des octets bruts dans le flux du terminal reste un conseil tout à fait raisonnable
  • La formulation de l’article est imprécise. Le deuxième paragraphe se lit comme « utiliser iTerm2 n’est pas sûr », alors qu’en réalité, le problème semble surtout concerner l’usage de la fonctionnalité optionnelle Shell Integration. Si cette fonction est désactivée par défaut, la portée de l’impact est limitée, si je comprends bien. Je veux bien être corrigé si je me trompe

    • Trouver que tout l’article est médiocre à cause d’une seule phrase exagérée me paraît excessif
    • Cette fonctionnalité était activée par défaut, et on peut le vérifier directement