Technique de contournement de l’authentification SAML SSO exploitant des différences entre parseurs
(github.blog)- Les CVE-2025-25291 et CVE-2025-25292, découvertes dans ruby-saml 1.17.0 et versions antérieures, permettent de se connecter en tant qu’utilisateur arbitraire avec une seule signature valide, augmentant le risque de prise de contrôle de compte dans les environnements SAML SSO
- La vulnérabilité provient d’une différence entre parseurs dans le chemin de vérification de signature, où REXML et Nokogiri peuvent interpréter des éléments
Signaturedifférents dans un même document XML SignedInfo,SignatureValue, le hachage de l’assertion etDigestValuesont combinés à partir de résultats de parseurs différents : chaque contrôle réussit, mais le lien entre le hachage et la signature peut être rompu- En réévaluant l’adoption de ruby-saml, GitHub a mené un bug bounty et une revue par Security Lab, a trouvé chez GitLab une instance exploitable et en a informé son équipe sécurité ; aujourd’hui, l’authentification GitHub n’utilise pas ruby-saml
- Les utilisateurs doivent mettre à jour vers ruby-saml 1.18.0 ; les bibliothèques qui dépendent de ruby-saml, comme
omniauth-saml, doivent aussi être mises à niveau vers une version qui référence la version corrigée
Impact du contournement d’authentification dans ruby-saml
- Deux vulnérabilités de contournement d’authentification à gravité élevée ont été confirmées dans ruby-saml 1.17.0 et versions antérieures
- CVE-2025-25291
- CVE-2025-25292
- Un attaquant disposant d’une seule signature valide créée avec la clé utilisée pour vérifier la SAML response ou l’assertion de l’organisation cible peut fabriquer directement une assertion SAML et se connecter en tant qu’utilisateur arbitraire
- Les sources possibles de signature sont les suivantes
- Une assertion ou response signée d’un autre utilisateur disposant de faibles privilèges
- Dans certains cas, les metadata signées d’un IdP SAML accessible publiquement
- Cette vulnérabilité peut au final être exploitée pour une prise de contrôle de compte
- GitHub n’utilise actuellement pas ruby-saml pour son authentification, mais a évalué ruby-saml alors qu’il envisageait de réutiliser une bibliothèque open source pour l’authentification SAML
- ruby-saml est aussi utilisé dans d’autres projets et produits populaires ; GitHub a découvert chez GitLab une instance exploitable et en a informé l’équipe sécurité de GitLab
Pourquoi GitHub a réexaminé ruby-saml
- GitHub a utilisé ruby-saml jusqu’en 2014, puis est passé à sa propre implémentation SAML faute de fonctionnalités nécessaires à l’époque
- Par la suite, cette implémentation interne a aussi fait l’objet de rapports de bug bounty, notamment CVE-2024-9487, une vulnérabilité liée aux assertions chiffrées, et GitHub a commencé à envisager de réintroduire ruby-saml
- En octobre 2024, une vulnérabilité de contournement d’authentification dans ruby-saml, CVE-2024-45409, découverte par ahacker1, a été rendue publique
- GitHub a lancé un bug bounty privé afin d’évaluer plus finement la possibilité de migrer vers ruby-saml
- Les chercheurs sélectionnés ont obtenu un accès à un environnement de test GitHub effectuant l’authentification SAML avec ruby-saml
- GitHub Security Lab a également examiné la surface d’attaque de ruby-saml
Une incohérence de vérification créée par deux parseurs XML
- Lors de la revue de code, ahacker1 et GitHub Security Lab ont confirmé que deux parseurs XML étaient utilisés ensemble dans le chemin de vérification de signature de ruby-saml
- REXML : un parseur XML implémenté en Ruby pur
- Nokogiri : prend en charge le parsing XML et HTML, et fournit une API encapsulant libxml2, libgumbo, Xerces pour JRuby, etc.
- Le chemin concerné est la méthode
validate_signaturedansxml_security.rb - Cette méthode lit le premier élément
Signatureet leSignatureValueréel avec REXMLREXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG})
- En revanche, dans le même flux de vérification,
SignatureetSignedInfosont de nouveau recherchés puis canonisés avec Nokogiridocument.at_xpath('//ds:Signature', 'ds' => DSIG)noko_signed_info_element.canonicalize(canon_algorithm)
- L’assertion est extraite, canonisée et hachée avec Nokogiri, mais la valeur de comparaison,
DigestValue, provient de REXML - Les matériaux de vérification finaux se séparent ainsi
- L’assertion est extraite et canonisée avec Nokogiri, puis hachée
- Le hachage de comparaison vient du
DigestValuelu par REXML SignedInfoest extrait et canonisé avec NokogiriSignatureValueest extrait avec REXML
Le lien de sécurité rompu dans la vérification de signature SAML
- Une SAML response transmet les informations de l’utilisateur connecté de l’IdP au SP au format XML
- Avec le HTTP POST binding, la SAML response passe par le navigateur de l’utilisateur avant d’arriver au SP ; une vérification de signature est donc nécessaire pour empêcher l’utilisateur de modifier le message
- Dans une SAML response simplifiée, les informations importantes se trouvent généralement dans
SubjectetNameIDà l’intérieur deAssertion - En général, l’assertion ou l’ensemble de la SAML response peut être signé
- Lorsque l’assertion est signée, la vérification se déroule en deux étapes
- Supprimer
Signaturede l’assertion, canoniser et hacher l’assertion, puis comparer le résultat àDigestValue - Canoniser
SignedInfoet vérifier la signature avecSignatureValue
- Supprimer
- Dans cette vulnérabilité, même si les deux étapes réussissent, elles ne garantissent pas qu’elles portent sur les mêmes données
- Le hachage peut être celui de l’assertion réelle
- La signature peut être celle d’un autre élément
SignedInfo
- La propriété de sécurité nécessaire est que le contenu haché, le hachage et la signature soient directement liés ; après vérification, les informations ne doivent être lues que dans la partie effectivement vérifiée
Construction d’un exploit réel
- La condition clé était de faire en sorte que REXML et Nokogiri voient des
Signaturedifférentes dans le même document XML, ce qui s’est avéré possible - Pendant le bug bounty, ahacker1 a d’abord créé un exploit fonctionnel utilisant une différence entre parseurs
- Il s’est inspiré de XML roundtrips vulnerabilities, publié en 2021 par Juho Forsén de Mattermost
- GitHub Security Lab a créé un autre exploit fondé sur une différence entre parseurs à l’aide du fuzzer Ruby ruzzy de Trail of Bits
- L’exemple d’exploit insère dans l’élément
StatusDetailuneSignaturesupplémentaire visible uniquement par Nokogiri - Le flux de vérification se sépare ainsi
- Le
SignedInfode la signature vue par Nokogiri est canonisé - Il est vérifié avec le
SignatureValueextrait de la signature vue par REXML - L’assertion trouvée par ID par Nokogiri est canonisée et hachée
- Le hachage est comparé au
DigestValuelu par REXML
- Le
- Au final, un
SignedInfovalide et une signature valide correspondent entre eux, tandis que l’assertion modifiée et son digest calculé correspondent également, ce qui conduit ruby-saml à accepter l’assertion
Mesures d’atténuation et limites de détection
- Vérifier les erreurs de parsing lors du parsing d’une SAML response avec Nokogiri peut bloquer certains exploits privés actuellement connus
- Les erreurs de parsing Nokogiri ne lèvent pas d’exception ; il faut donc vérifier directement le membre
errorsdu document parsé - L’exemple de code utilise les options
Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONETet déclenche une erreur lorsquedoc.errors.any? - Cette approche n’est pas une correction complète, mais elle rend au moins un exploit inexécutable
- Aucun indicateur de compromission fiable n’est connu
- Un indicateur potentiel ne fonctionne que dans un environnement de type debug
- Le publier révélerait trop de détails d’implémentation d’un exploit fonctionnel, il n’est donc pas rendu public
- La méthode de vérification recommandée consiste à rechercher côté SP des connexions SAML suspectes, par exemple depuis des adresses IP ne correspondant pas à l’emplacement attendu de l’utilisateur
Orientation du correctif et éléments à mettre à jour
- Le correctif initial ne supprime pas l’un des parseurs XML en raison de problèmes de compatibilité d’API
- Le problème plus fondamental était la séparation entre vérification du hachage et vérification de la signature, séparation devenue exploitable via des différences entre parseurs
- La suppression d’un parseur XML était déjà prévue pour d’autres raisons, et pourrait intervenir dans une version majeure avec d’autres améliorations
- Les utilisateurs de ruby-saml doivent mettre à jour vers la version corrigée 1.18.0
- Les bibliothèques utilisant ruby-saml doivent également être vérifiées
- Exemple : omniauth-saml
- Ces bibliothèques doivent être mises à jour vers une version référençant la version corrigée de ruby-saml
- GitHub Security Lab prévoit de publier ultérieurement un exploit de preuve de concept dans le GitHub Security Lab repository
Calendrier de divulgation et de réponse
- 2024-11-04 : réception d’un rapport de bug bounty démontrant un contournement d’authentification dans l’environnement de test GitHub qui évaluait l’authentification SAML avec ruby-saml
- 2024-11-04 : début de l’identification et des tests de mesures d’atténuation potentielles
- 2024-11-12 : découverte d’un second contournement d’authentification neutralisant le premier plan d’atténuation
- 2024-11-13 : premier contact avec Sixto Martín, mainteneur de ruby-saml
- 2024-11-14 : signalement à ruby-saml des différences entre les deux parseurs, avec réponse immédiate du mainteneur
- 2024-11-14 : le mainteneur et ahacker1 commencent à travailler sur un correctif potentiel
- L’une des idées initiales consistait à supprimer l’un des parseurs XML, mais cela n’était pas possible sans casser la compatibilité descendante
- 2025-02-04 : ahacker1 propose un correctif non rétrocompatible
- 2025-02-06 : ahacker1 propose également un correctif rétrocompatible
- 2025-02-12 : fin du délai de 90 jours de l’advisory GitHub Security Lab
- 2025-02-16 : le mainteneur commence à travailler sur une approche de correction préservant la rétrocompatibilité et facile à comprendre
- 2025-02-17 : premier contact avec GitLab afin de coordonner la publication de ruby-saml et celle des produits GitLab on-premise
- 2025-03-12 : publication de la version corrigée de ruby-saml
1 commentaires
Avis sur Hacker News
Je considère que l’implémentation SAML de GitHub ne sert à rien
L’intention initiale était de permettre d’utiliser des comptes personnels au sein d’une entreprise ; cela fonctionne dans une certaine mesure sur le site de GitHub, mais une fois qu’une application connectée via GitHub a été approuvée au niveau de l’organisation, rien n’empêche l’accès à la liste des membres de l’organisation
Une session SAML n’est nécessaire que lorsque l’application récupère des données avec le jeton qu’elle a reçu de cet utilisateur ; en pratique, les outils SAST utilisent presque toujours des jetons d’instance d’application et affichaient le code dès lors qu’il existait simplement un compte GitHub dans l’organisation
Tailscale a corrigé le problème après en avoir été informé, SonarCloud a demandé de n’en parler à personne, et GitHub a répondu quelques semaines plus tard qu’il s’agissait d’un « comportement entièrement attendu » ; pourtant aucun des fournisseurs informés ne l’avait compris ainsi, et la documentation contredisait la réponse de GitHub
Signaler des bugs de sécurité, même découverts par hasard, n’est pas gratifiant, et il est difficile d’imaginer en faire son métier
GitHub a très occasionnellement utilisé par défaut l’adresse e-mail personnelle lors de la fusion de PR professionnels ; donc si l’on me demande, je conseillerais de ne pas mélanger personnel et professionnel dans le même compte, que ce soit sur GitHub ou ailleurs
En créant https://dev.log.xyz, un outil d’analyse de dépôts/commits/PR, il a fallu beaucoup d’efforts pour garantir que « ce qui est visible sur GitHub est aussi visible dans Devlog », et toute l’expérience a été très frustrante
Le sélecteur d’autorisations OAuth de GitHub est lui aussi excessivement confus, si bien qu’il est difficile de savoir quelles informations de quelles organisations on partage en utilisant « Se connecter avec GitHub »
Il est déjà difficile de convaincre de jeunes développeurs familiers de JavaScript que la validation côté client ne suffit pas ; c’est encore plus difficile avec les décideurs qui fixent les exigences fonctionnelles et les budgets
N’importe quelle forme de coffre-fort à mots de passe, même un stockage physique ou la réutilisation de mots de passe, peut au final s’avérer plus sûre
En fin de compte, le minimalisme l’emporte encore
Il est écrit : “GitHub doesn’t currently use ruby-saml for authentication, but began evaluating the use of the library with the intention of using an open source library for SAML authentication once more”
J’ai récemment dû implémenter SAML, et ce titre ne me surprend absolument pas
La spécification SAML elle-même est assez raisonnable, mais sa base, les signatures XML, et plus encore la canonicalisation XML, est d’un niveau de folie difficile à qualifier de standard
Seul un comité pouvait produire une spécification aussi tordue et dévoyée ; j’ai l’impression qu’un esprit individuel n’aurait pas pu contenir et combiner des idées aussi contradictoires
Rien que le fait de transmettre la signature hors bande aurait rendu SAML agréable à implémenter
XML signifie littéralement eXtensible Markup Language, et le comité de normalisation SAML a encore inventé par-dessus son propre langage de mécanisme d’extension
Empiler un protocole sur un autre protocole pour une quantité minuscule de données, guère différente de ce qu’on mettrait dans un cookie d’authentification, relève d’un type très particulier de stupidité que seuls les comités les plus gros et bureaucratiques peuvent produire
L’idée de se connecter séparément à des comptes différents semble correcte
Une architecture qui relie les comptes entre eux pour permettre de les compromettre massivement d’un seul coup est fondamentalement plus dangereuse
SAML, et plus largement XML-DSIG, est selon moi littéralement le pire des protocoles de sécurité largement utilisés
En général, il faut migrer vers OAuth à presque n’importe quel prix, et je refuserais au minimum de lancer un nouveau produit qui en dépende
C’est très dangereux, et à moins qu’une percée n’apparaisse dans la validation pratique des formats, il est difficile d’imaginer que ce soit la dernière vulnérabilité DSIG, ou même la pire
Même sans toucher à la cryptographie, c’est déjà le sommet de la pensée façon logiciel d’entreprise
Quelqu’un devrait vraiment fouiller les listes de diffusion et les organismes de normalisation derrière des choses comme WS-* et OASIS/XACML
C’est bien ça ?
Beurk, personne ne devrait utiliser REXML sauf s’il n’y a vraiment pas d’autre option
Il accepte volontiers de parser du XML invalide, ce qui provoque ensuite une infinité de problèmes dans les étapes suivantes
Il parse littéralement du XML avec des expressions régulières, et constitue un excellent exemple de pourquoi il ne faut pas le faire
Les projets n’ont pas commencé à utiliser Nokogiri pour des raisons de performance, mais pour des raisons de correction
Ne pas parser avec des expressions régulières quelque chose qui n’est pas un langage régulier
J’ai récemment testé o3, et chaque fois qu’il tentait de corriger un problème sans rapport avec la bibliothèque d’un bloc de code précis, il changeait sans cesse la bibliothèque utilisée par ce bloc
Je n’ai pas observé ce phénomène avec Sonnet
On voit facilement comment, au fil des corrections, un problème peut s’introduire en remplaçant par une bibliothèque/gem inférieure déjà présente dans la base de code ou la bibliothèque standard, de sorte que les tests passent et qu’aucune modification du Gemfile ne soit nécessaire
SAML n’est pas sûr par conception
Il existe depuis longtemps des articles qui l’expliquent mieux, par exemple https://joonas.fi/2021/08/saml-is-insecure-by-design/
La formule qui m’est restée en tête dans cet ancien fil était : « signez les octets, pas le sens »
Les différences entre parseurs sont prévisibles et parfois inévitables
Ce que l’on cherche à obtenir d’une réponse signée est crucial
L’un des dilemmes de TLS moderne est que l’on veut parfois faire confiance à une CA interne, ce qui est la voie facile, mais si l’on accepte le certificat de CA d’un partenaire et qu’il y a plusieurs partenaires, on ne peut plus se contenter de regarder le certificat final : la racine de la chaîne devient tout aussi importante dans la prise de décision
C’est pourquoi il est recommandé d’éviter aussi, si possible, l’algorithme de signature AWS
La V4 est théoriquement sûre, mais AWS s’est déjà trompé deux fois, et SigV1 comme SigV3 étaient non sûrs par conception tout en ayant, d’une manière ou d’une autre, passé la revue de conception et été publiés
Excellent article
Comme l’article le mentionne aussi, il faut applaudir très fort ahacker1
Il mène un travail très sophistiqué et précieux pour rendre les implémentations SAML sûres, et SSOReady lui est vraiment reconnaissant pour son travail
Plus tôt cette semaine, WorkOS a également publié un bon article sur sa collaboration avec ahacker1 : https://workos.com/blog/samlstorm
Il y a un passage disant : « nous avons trouvé dans GitLab un cas où cette vulnérabilité était exploitable et l’avons signalé à l’équipe sécurité » ; pour ceux que ça intéresse, GitLab a déjà publié un correctif
https://about.gitlab.com/releases/2025/03/12/patch-release-g...
Un article connexe est celui de Latacora, publié en 2019, How (not) to sign a JSON object[1]
En résumé, signer des structures arborescentes imbriquées est difficile et plein de pièges
Il est plus simple que l’enveloppe contienne le message sous forme de chaîne brute, puis de signer cette chaîne brute
[1]: https://www.latacora.com/blog/2019/07/24/how-not-to/
La conclusion la plus simple ici n’est-elle pas qu’il faut chercher la signature à l’endroit où elle est censée se trouver ?
Ne pas utiliser un XPath trop générique comme « //ds:Signature », qui permettrait de trouver n’importe quelle signature à un emplacement inattendu
Il ne faut pas seulement retirer chirurgicalement le composant dangereux ; il faut aussi jeter un peu le bébé avec l’eau du bain, couper largement et traiter ça comme une chimiothérapie
Tout administrateur IT qui se respecte devrait exclure SAML de ses plans futurs
L’idée même du SSO est suspecte, et comme le parsing XML s’est fait frapper deux fois cette semaine, il faudrait l’éviter à l’avenir
Qu’est-ce qui ne va pas avec une politique consistant à remplacer XML par JSON ?
Cela vaut dès la conception, les protocoles et les formats de données
Les habitudes et considérations de conception du développement web général ne correspondent pas à ce dont le code de sécurité a besoin, et sont souvent même à l’opposé de ce qu’il faut pour écrire du code correct
C’est un peu agaçant que l’article de blog explique la vulnérabilité tout en omettant justement la différence entre parseurs qui posait problème
C’est comme écrire l’introduction d’une histoire et en supprimer le point culminant
En faisant
(...//ds:DigestValue).firstChild.nodeValue, le code ne vérifiait pas que.firstChildétait un Node ; dans le cas problématique, c’était un CommentAinsi, le côté non normalisé voyait une signature « masquée », tandis que le côté corrigé qui supprimait les commentaires voyait un Node ; quand deux implémentations jugent différemment un document signé, des choses qui ne font pas rire peuvent arriver
Les détails concrets sortiront sans doute bientôt