Skip to main content

Vulnérabilité 0-day d’exécution de code à distance sur Gitpod via WebSocket

Écrit par:
blog-feature-pypi-spoof

27 février 2023

0 minutes de lecture

En résumé

Cet article présente un projet actif de l’équipe Security Labs de Snyk portant sur les environnements de développement dans le cloud (CDE). Il a abouti à la prise de contrôle totale du compte de la plateforme Gitpod et du compte SCM de l’utilisateur. Les problèmes que nous avons relevés ont été divulgués de manière responsable à Gitpod et résolus sous un jour ouvrable seulement !

Environnements de développement dans le cloud et Gitpod

De plus en plus d’entreprises adoptent les environnements de développement dans le cloud pour bénéficier de performances supérieures, améliorer l’expérience des développeurs, profiter d’environnements cohérents et réduire les temps de configuration. Nous n’avons pas pu nous empêcher de nous demander quelles étaient les implications de ces environnements en matière de sécurité.

Commençons par résumer le fonctionnement des CDE pour bien comprendre la différence entre le développement dans le cloud et le développement plus traditionnel en local, ainsi que les implications de ces différences pour la sécurité des développeurs.

Contrairement aux environnements de développement classiques, les CDE s’exécutent sur une machine hébergée dans le cloud avec un IDE en backend. Ils se présentent généralement sous la forme d’une version Web de l’IDE et prennent en charge l’intégration à un IDE local via SSH, ce qui permet aux utilisateurs de bénéficier d’une expérience fluide, sans risque de dépaysement. Avec un CDE, le code de l’entreprise et tous les services associés, par exemple la base de données de développement, sont hébergés dans le cloud. Le diagramme suivant représente le flux d’informations au sein d’un CDE.

blog-websocket-takeover-cde-diagram

Les risques de sécurité posés par les environnements de développement locaux sont connus. Pour autant, ils n’ont jamais vraiment été pris en compte par les développeurs. En mai 2021, Snyk a par exemple dévoilé l’existence d’extensions VS Code vulnérables permettant des fuites de données ou l’exécution de commandes arbitraires en un clic. Ces environnements de développement sur station de travail (instance locale de VS Code ou d’IntelliJ, par exemple), posent aussi d’autres problèmes liés à la sécurité des informations, comme le risque de panne matérielle, les violations de la sécurité des données et l’exposition aux malwares. Si ces problèmes peuvent être résolus par un chiffrement complet du disque, le contrôle des versions, des sauvegardes et des logiciels antimalwares, beaucoup de questions liées à la migration de ces environnements vers le cloud restent sans réponse : 

  • Que se passe-t-il si l’espace de travail d’un IDE dans le cloud est infecté par un malware ?

  • Que se passe-t-il lorsque les contrôles d’accès ne suffisent pas et permettent à d’autres utilisateurs ou mêmes organisations d’accéder à des espaces de travail qui ne sont pas les leurs ?

  • Que se passe-t-il lorsqu’un développeur malhonnête exfiltre la propriété intellectuelle d’une entreprise à partir d’une machine dans le cloud, sur laquelle le logiciel de prévention des pertes de données ou le logiciel de sécurité des points de terminaison de l’entreprise n’a aucune visibilité ?

Chez Snyk, nous travaillons sur les implications de l’adoption d’IDE hébergés dans le cloud. Dans cet article, nous présentons une étude de cas concernant une des vulnérabilités découvertes lors de notre première exploration de la plateforme Gitpod. 

Examen de la plateforme Gitpod

Avertissement : nous avons choisi d’étudier des IDE auto-hébergées ou dans le cloud dont le fournisseur dispose d’une politique de sécurité clairement définie qui protège les chercheurs. 

L’un des environnements de développement dans le cloud les plus populaires est Gitpod. C’est l’un des premiers produits que nous avons étudiés, car il est très utilisé et propose de nombreuses fonctionnalités, notamment des sauvegardes automatiques, une intégration Git clé en main et de multiples backends d’IDE.

La première étape de notre recherche a consisté à comprendre le fonctionnement des workflows de base de Gitpod, à configurer une organisation et à tester le produit tout en capturant le trafic avec Burpsuite pour visualiser les différentes API et transactions. Nous avons ensuite extrait le code source de Gitpod à partir de GitHub pour étudier le fonctionnement interne de ses API et consulté la documentation de l’architecture pour mieux comprendre chaque composant et sa fonction. Cette vidéo proposant une analyse détaillée de l’architecture de Gitpod nous a beaucoup aidés. En gros, Gitpod s’articule autour de multiples microservices déployés dans un environnement Kubernetes, où chaque espace de travail utilisateur est déployé dans un pod éphémère dédié. 

Ses principaux composants externes sont liés au tableau de bord, à l’authentification, et à la création et à la gestion d’espaces de travail, d’organisations et de comptes. Au cœur de Gitpod se trouve un composant nommé server, un nom particulièrement parlant. Cette application TypeScript expose une API JSONRPC sur WebSocket, consommée par une interface React appelée dashboard

Depuis ce tableau de bord, vous pouvez intégrer très facilement un fournisseur SCM comme GitHub ou Bitbucket pour importer un dépôt et lancer un environnement de développement qui envoie ensuite le code source et vous fournit un environnement de travail Git. Une fois l’espace de travail mis en service, il est rendu accessible via SSH et HTTPS sur un sous-domaine de gitpod.io (https://[NOM_ESPACE_DE_TRAVAIL].[NOM_CLUSTER].gitpod.io) par le biais d’un composant en Golan nommé ws-proxy

La faille de sécurité que nous avons découverte lors de nos recherches est principalement liée au composant server et à l’API JSONRPC accessible via la connexion WebSocket. Elle permet de prendre le contrôle d’un espace de travail Gitpod.

Détails techniques

WebSocket et politique de même origine

WebSocket est une technologie permettant une communication bilatérale et en temps réel entre un client (généralement un navigateur Web) et un serveur. Cette communication est également persistante, ce qui permet de transférer en continu des données en temps réel, sans multiplier les requêtes HTTP. 

Une caractéristique de sécurité intéressante de WebSocket réside dans le fait qu’elle s’exonère d’un mécanisme de sécurité des navigateurs, à savoir la politique de même origine (SOP). Ce contrôle de sécurité empêche un site Web d’émettre une requête AJAX vers un autre site Web et d’en lire la réponse. Il répond à un vrai problème de sécurité, car les navigateurs envoient généralement des cookies avec chaque requête (même pour les requêtes Cross Origin, comme lors des attaques cross-site request forgery (CSRF)). Sans SOP, n’importe quel site Web pourrait envoyer des requêtes vers des sites externes et obtenir vos données à partir d’autres domaines.

Ce qui nous amène donc aux vulnérabilités de type détournement WebSocket cross-site. Ces attaques peuvent être décrites comme une combinaison de Cross-Site Request Forgery et d’erreur de configuration CORS. Lorsque l’établissement d’une liaison WebSocket repose uniquement sur des cookies HTTP pour l’authentification, un site Web malveillant peut instancier une nouvelle connexion WebSocket avec l’application vulnérable et permettre ainsi à un attaquant d’envoyer et de recevoir des données via cette connexion.

Lors de l’étude d’une application gérant des connexions WebSocket, il est toujours utile d’examiner ce point dans le détail. Penchons-nous maintenant sur la requête WebSocket envoyée au serveur Gitpod.

blog-websocket-takeover-gitpod.io-upgrade
blog-websocket-takeover-switching-protocols-feb-9

En temps normal, la connexion est validée et la communication démarre. Aucune authentification supplémentaire n’intervient lors de l’échange WebSocket à proprement parler, et l’API JSONRPC peut être appelée via la connexion WebSocket.

blog-websocket-takeover-jsonrpc-2.0

Jusqu’ici, nous n’avons pas trouvé d’authentification supplémentaire au sein du canal établi, un bon signe pour des attaquants potentiels. Assurons-nous maintenant qu’aucun contrôle supplémentaire de l’origine n’est effectué en altérant l’en-tête Origin de l’établissement d’une liaison que nous avons observée.

blog-websocket-takeover-origin-workspace-id
blog-websocket-takeover-switching-protocols-feb-12

Cela paraît prometteur ! Il semble que le domaine evil.com puisse envoyer des requêtes WebSocket cross-origin à gitpod.io. Toutefois, un autre mécanisme de sécurité nous pose problème.

Contournement des cookies SameSite

Les cookies SameSite sont assez récents et permettent de régler en partie la question des attaques Cross-Site Request Forgery (CSRF). Leur adoption n’est pas encore généralisée, mais la plupart des grands navigateurs en font le choix par défaut pour l’ensemble des cookies qui ne désactivent pas explicitement SameSite au profit de Lax. La vulnérabilité sous-jacente reste donc présente, mais sans contournement des cookies SameSite, notre stratégie d’attaque resterait principalement théorique et ne fonctionnerait que sur quelques navigateurs de niche et obsolètes.

Voyons maintenant ce qu’est un site dans le contexte de SameSite. Pour le dire simplement, le site correspond à la combinaison du schéma et du domaine enregistrable (s’il existe) de l’hôte d’origine. Si nous regardons les spécifications de SameSite, nous constatons que les sous-domaines ne sont pas pris en compte. Ce fonctionnement est moins strict que la spécification d’une origine utilisée par la politique de même origine, qui se compose d’un schéma, d’un hôte (sous-domaines compris) et d’un port (p.ex : https://security.snyk.io:8443).

blog-websocket-takeover-url-breakdown

Tout à l’heure, nous avons vu que l’espace de travail était exposé par le biais d’un sous-domaine de gitpod.io. Dans le contexte de SameSite, l’URL de l’espace de travail est considérée comme étant le même site que gitpod.io. Il devrait donc être possible pour un espace de travail d’envoyer une requête SameSite à un domaine *.gitpod.io en y joignant les cookies de son utilisateur d’origine. Voyons si nous pouvons nous servir d’un espace contrôlé pour envoyer une charge utile de détournement WebSocket. 

Commençons par vérifier si les cookies sont bien transmis et si la communication WebSocket fonctionne. Pour cela, nous allons ouvrir la console du navigateur à partir d’un espace de travail et tenter de démarrer une connexion WebSocket.

blog-websocket-takeover-gitpod-outline-cropped

Comme le montre la capture d’écran ci-dessus, nous avons tenté d’ouvrir une nouvelle connexion WebSocket, puis d’envoyer une requête à l’API JSONRPC. Le résultat de la console indique qu’un message contenant le résultat de notre requête a été reçu, ce qui confirme que les cookies sont envoyés et que l’origine utilisée permet d’ouvrir une connexion WebSocket avec gitpod.io.

Nous avons maintenant besoin d’un moyen d’envoyer du JavaScript depuis l’espace de travail accessible par un utilisateur Gitpod. En étudiant les fonctions disponibles, nous voyons qu’il est possible d’exposer des ports de l’espace de travail et de les rendre accessibles à l’aide d’un utilitaire en ligne de commande appelé gitpod-cli. Cet utilitaire peut être exécuté sur le chemin interne de l’espace de travail en tapant la commande gp. Nous pouvons appeler gp ports expose 8080, puis configurer un serveur Web Python de base avec la commande python -m http.server 8080. Cette opération crée un sous-domaine sur lequel le port exposé est accessible, comme illustré ci-dessous.

blog-websocket-takeover-consol-error

Malheureusement, la connexion a échoué. Ce problème est inquiétant et nécessite d’enquêter de manière plus approfondie. Nous avons donc ouvert le code source et commencé à chercher d’où pouvait provenir le problème. Nous avons trouvé l’expression régulière suivante, qui semble être utilisée pour extraire le nom de l’espace de travail de l’URL.

blog-websocket-takeover-base-workspace-id

Le nom d’espace de travail qu’elle extrait est incorrect, comme le montre la capture d’écran suivante :

blog-websocket-takeover-test-string

On dirait donc que nous ne pouvons pas envoyer notre contenu depuis un port exposé d’un espace de travail hébergé par Gitpod et qu’il nous faut trouver une autre solution. Nous savons désormais que nous disposons d’un accès privilégié à une machine qui exécute le service VS Code et envoie des requêtes destinées à l’URL de notre espace de travail. Y-a-t-il un moyen de détourner ce fonctionnement ?

Notre première idée a été d’arrêter le processus vscode et de démarrer un serveur Web Python pour envoyer un fichier HTML. Malheureusement, ça n’a pas fonctionné et a entraîné le redémarrage de l’espace de travail. Cette opération semble avoir été exécutée par un service local nommé supervisor. En testant cette approche, nous avons remarqué que lorsque nous arrêtions un processus sans en lier un autre au port VS Code, le service supervisor redémarrait automatiquement le processus vscode, ce qui entraînait un bref blocage de l’interface, sans pour autant entraîner de redémarrage complet de l’espace de travail.

Ce comportement offre une piste prometteuse. Pouvons-nous altérer VS Code pour qu’il distribue un exploit intégré ?

Cette opération n’a pas posé de vrai problème. Il nous a suffi de comparer le code source du serveur VS Code d’origine à celui de la version distribuée pour trouver un endroit approprié à la distribution de l’exploit.

VS Code contient un point de terminaison d’API /version qui renvoie le commit de la version actuelle :

blog-websocket-takeover-pathname-version

Nous l’avons modifié de sorte qu’il renvoie l’en-tête Content-Typetext/html correct, ainsi que le contenu d’un fichier HTML. Ensuite, nous avons arrêté le processus vscode pour permettre le chargement de nos modifications dans une nouvelle instance de ce processus :

blog-websocket-takeover-template-python

Enfin, nous n’avons plus qu’à utiliser les méthodes JSONRPC getLoggedInUser, getGitpodTokens, getOwnerToken et addSSHPublicKey pour créer une charge utile qui nous donne le plein contrôle sur l’espace de travail de tout utilisateur de Gitpod qui clique sur notre lien !

Voici notre piège en action :

blog-websocket-takeover-pwnpod-info

Nous constatons que nous avons pu extraire des informations sensibles sur le compte de l’utilisateur et que nous avons pu ajouter notre clé SSH au compte. Voyons si nous pouvons nous connecter à l’espace de travail via SSH :

blog-websocket-takeover-task-list-redacted

Mission accomplie ! Comme illustré ci-dessus, nous avons un accès total aux espaces de travail des utilisateurs qui ont cliqué sur le lien que nous leur avons envoyé !

Chronologie

  • Lundi 13 février 2023 : divulgation de la vulnérabilité au fournisseur

  • Lundi 13 février 2023 : confirmation de la réception de l’information par le fournisseur

  • Mardi 14 février 2023 : déploiement d’une nouvelle version sur l’instance Gitpod SaaS en production

  • Mercredi 22 février 2023 : attribution de la CVE 2023-0957

  • Mercredi 1er mars 2023 : publication d’une nouvelle version de l’édition auto-hébergée de Gitpod et d’un avis de sécurité

En résumé

Dans cet article, nous avons présenté les premiers résultats tirés de notre analyse des environnements de développement dans le cloud (CDE). Nous sommes parvenus à prendre le contrôle complet d’un compte via un simple lien en exploitant une vulnérabilité souvent mal comprise (le détournement de WebSocket) et en utilisant un contournement de cookie SameSite. Les espaces de travail dans le cloud destinés aux développeurs sont de plus en plus populaires, et il est donc essentiel de tenir compte des risques nouveaux qu’ils peuvent introduire.

Nous tenons à féliciter Gitpod pour sa réactivité dans la correction de cette faille de sécurité et avons hâte de présenter d’autres informations sur les solutions de développement à distance dans le cloud d’ici peu.

blog-feature-pypi-spoof

Vous voulez l’essayer par vous-même ?

Snyk interviewed 20+ security leaders who have successfully and unsuccessfully built security champions programs. Check out this playbook to learn how to run an effective developer-focused security champions program.