1 pontos por GN⁺ 2025-03-16 | 1 comentários | Compartilhar no WhatsApp
  • As CVE-2025-25291 e CVE-2025-25292, encontradas no ruby-saml 1.17.0 ou inferior, aumentam o risco de sequestro de contas em ambientes SAML SSO ao permitir login como qualquer usuário com apenas uma assinatura válida
  • A vulnerabilidade decorre de uma diferença entre parsers pela qual REXML e Nokogiri podem interpretar elementos Signature diferentes no mesmo documento XML dentro do fluxo de verificação de assinatura
  • Como SignedInfo, SignatureValue, o hash da assertion e DigestValue acabam sendo combinados a partir dos resultados de parsers diferentes, cada verificação pode passar, mas a ligação entre hash e assinatura pode se romper
  • O GitHub conduziu bug bounty e revisão do Security Lab ao reavaliar a reintrodução do ruby-saml, encontrou instâncias exploráveis no GitLab e notificou a equipe de segurança; atualmente, a autenticação do GitHub não usa ruby-saml
  • Os usuários devem atualizar para o ruby-saml 1.18.0, e bibliotecas que dependem de ruby-saml, como omniauth-saml, também devem ser atualizadas para versões que referenciem a correção

Impacto do bypass de autenticação no ruby-saml

  • Foram confirmadas 2 vulnerabilidades de bypass de autenticação de alta gravidade no ruby-saml 1.17.0 ou inferior
    • CVE-2025-25291
    • CVE-2025-25292
  • Se o atacante tiver uma única assinatura válida gerada com a chave usada para validar respostas SAML ou assertions da organização-alvo, ele pode criar diretamente uma SAML assertion e entrar como qualquer usuário
  • As possíveis origens dessa assinatura incluem:
    • assertion ou response assinada de outro usuário com privilégios baixos
    • em alguns casos, metadata assinada de um SAML IdP acessível publicamente
  • Na prática, essa vulnerabilidade pode ser usada em ataques de sequestro de conta
  • O GitHub atualmente não usa ruby-saml para autenticação, mas avaliou o ruby-saml enquanto estudava voltar a usar uma biblioteca open source para autenticação SAML
  • O ruby-saml também é usado em outros projetos e produtos populares, e o GitHub encontrou uma instância explorável no GitLab e notificou a equipe de segurança do GitLab

Por que o GitHub voltou a analisar o ruby-saml

  • O GitHub usou ruby-saml até 2014, mas na época faltavam recursos necessários, então migrou para uma implementação própria de SAML
  • Depois disso, a implementação própria também recebeu relatórios de bug bounty sobre falhas como CVE-2024-9487, uma vulnerabilidade relacionada a assertions criptografadas, e o GitHub passou a considerar reintroduzir o ruby-saml
  • Em outubro de 2024, foi divulgada a vulnerabilidade de bypass de autenticação CVE-2024-45409 no ruby-saml, descoberta por ahacker1
  • Para avaliar com mais cuidado a possibilidade de migrar para ruby-saml, o GitHub iniciou um bug bounty privado
    • Pesquisadores selecionados receberam acesso a um ambiente de teste do GitHub que fazia autenticação SAML com ruby-saml
    • O GitHub Security Lab também revisou a superfície de ataque do ruby-saml

Divergência de validação criada por dois parsers XML

  • Durante a revisão de código, ahacker1 e o GitHub Security Lab confirmaram que dois parsers XML eram usados juntos no fluxo de verificação de assinatura do ruby-saml
    • REXML: parser XML implementado em Ruby puro
    • Nokogiri: fornece APIs sobre libxml2, libgumbo e Xerces para JRuby, com suporte a parsing de XML e HTML
  • O caminho problemático era o método validate_signature em xml_security.rb
  • Esse método lê o primeiro elemento Signature e o SignatureValue real com REXML
    • REXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG})
  • Já no mesmo fluxo de verificação, Signature e SignedInfo são buscados novamente e canonicalizados com Nokogiri
    • document.at_xpath('//ds:Signature', 'ds' => DSIG)
    • noko_signed_info_element.canonicalize(canon_algorithm)
  • A assertion é extraída, canonicalizada e hasheada com Nokogiri, mas o DigestValue usado na comparação vem do REXML
  • No fim, o material de verificação se divide assim:
    • a assertion é extraída e canonicalizada com Nokogiri, depois hasheada
    • o hash usado na comparação vem do DigestValue lido pelo REXML
    • SignedInfo é extraído e canonicalizado com Nokogiri
    • SignatureValue é extraído com REXML

A ligação de segurança quebrada na verificação de assinatura SAML

  • A SAML response transmite, do IdP para o SP, informações de login do usuário em formato XML
  • Quando se usa HTTP POST binding, a SAML response passa pelo navegador do usuário antes de chegar ao SP, então é necessária verificação de assinatura para impedir que o usuário altere a mensagem
  • Em uma SAML response simplificada, as informações mais importantes normalmente ficam em Subject e NameID dentro de Assertion
  • Em geral, pode-se assinar a assertion ou a SAML response inteira
  • Quando a assertion é assinada, a verificação ocorre em duas etapas
    • a assertion sem Signature é canonicalizada e hasheada para comparação com DigestValue
    • SignedInfo é canonicalizado e a assinatura é verificada com SignatureValue
  • Nesta vulnerabilidade, mesmo que as duas etapas passem separadamente, isso não garante que tratem dos mesmos dados
    • o hash pode ser o hash da assertion real
    • a assinatura pode ser uma assinatura sobre outro elemento SignedInfo
  • A propriedade de segurança necessária é que o conteúdo hasheado, o hash e a assinatura estejam diretamente ligados entre si, e que, após a verificação, as informações sejam lidas apenas da parte efetivamente validada

Como o exploit real é montado

  • A condição central era fazer REXML e Nokogiri verem Signature diferentes no mesmo documento XML, e isso de fato era possível
  • Durante o bug bounty, ahacker1 criou primeiro um exploit funcional baseado em diferenças entre parsers
  • O GitHub Security Lab criou outro exploit baseado em diferenças entre parsers usando o fuzzer para Ruby ruzzy da Trail of Bits
  • Um exploit de exemplo coloca uma Signature extra, visível apenas ao Nokogiri, dentro do elemento StatusDetail
  • O fluxo de verificação então se separa da seguinte forma
    • o SignedInfo da signature vista pelo Nokogiri é canonicalizado
    • a verificação é feita com o SignatureValue extraído da signature vista pelo REXML
    • a assertion encontrada por ID pelo Nokogiri é canonicalizada e hasheada
    • o hash é comparado com o DigestValue lido pelo REXML
  • No fim, um SignedInfo válido e uma assinatura válida continuam combinando entre si, e a assertion manipulada também combina com o digest calculado, fazendo o ruby-saml aceitar a assertion

Mitigações e limites de detecção

  • Verificar erros de parsing ao processar a SAML response com Nokogiri pode bloquear parte dos exploits privados atualmente conhecidos
  • Erros de parsing do Nokogiri não são lançados como exceção, então é preciso checar diretamente o membro errors do documento parseado
  • O código de exemplo usa as opções Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET e gera erro quando doc.errors.any?
  • Essa abordagem não é uma correção completa, mas impede pelo menos um exploit conhecido
  • Não são conhecidos indicadores de comprometimento confiáveis
    • um indicador potencial só funciona em ambientes parecidos com debug
    • ele não é divulgado porque isso revelaria detalhes demais de implementação de um exploit funcional
  • O método recomendado de verificação é procurar, no lado do SP, logins SAML suspeitos, como acessos vindos de endereços IP incompatíveis com a localização esperada do usuário

Direção da correção e o que atualizar

  • A correção inicial não removeu um dos parsers XML por questões de compatibilidade de API
  • O problema mais fundamental era a separação entre verificação de hash e verificação de assinatura, e essa separação se tornou explorável por meio de diferenças entre parsers
  • A remoção de um dos parsers XML já estava planejada por outros motivos e pode acontecer em uma versão principal junto com melhorias adicionais
  • Usuários de ruby-saml devem atualizar para a versão 1.18.0, que inclui a correção
  • Bibliotecas que usam ruby-saml também precisam ser verificadas
    • Ex.: omniauth-saml
    • Essas bibliotecas devem ser atualizadas para versões que apontem para uma versão corrigida do ruby-saml
  • O GitHub Security Lab pretende publicar futuramente um exploit de prova de conceito no repositório do GitHub Security Lab

Cronograma de divulgação e resposta

  • 2024-11-04: foi recebido um relatório de bug bounty demonstrando bypass de autenticação no ambiente de testes do GitHub que avaliava autenticação SAML com ruby-saml
  • 2024-11-04: começou o trabalho de identificar e testar possíveis mitigações
  • 2024-11-12: foi descoberto um segundo bypass de autenticação que anulava o primeiro plano de mitigação
  • 2024-11-13: ocorreu o primeiro contato com o maintainer do ruby-saml, Sixto Martín
  • 2024-11-14: a diferença entre os dois parsers foi reportada ao ruby-saml, e o maintainer respondeu imediatamente
  • 2024-11-14: o maintainer e ahacker1 começaram a trabalhar em possíveis patches
    • uma das ideias iniciais era remover um dos parsers XML, mas isso não era possível sem quebrar compatibilidade retroativa
  • 2025-02-04: ahacker1 propôs uma correção incompatível com versões anteriores
  • 2025-02-06: ahacker1 também propôs uma correção compatível com versões anteriores
  • 2025-02-12: expirou o prazo de 90 dias do advisory do GitHub Security Lab
  • 2025-02-16: o maintainer começou a trabalhar em uma correção que preservasse compatibilidade retroativa e fosse mais fácil de entender
  • 2025-02-17: ocorreu o primeiro contato com o GitLab para coordenar o lançamento do ruby-saml corrigido e da versão do produto on-premise do GitLab
  • 2025-03-12: foi lançada a versão corrigida do ruby-saml

1 comentários

 
GN⁺ 2025-03-16
Opiniões no Hacker News
  • Acho que a implementação de SAML do GitHub é inútil
    A intenção original é trazer contas pessoais para o ambiente corporativo e usá-las ali; dentro do site do GitHub isso funciona até certo ponto, mas, depois que um app conectado via GitHub é aprovado no nível da organização, nada impede que ele leia a associação à organização
    A sessão SAML só é necessária quando esse app busca dados com o token que recebeu daquele usuário; na prática, ferramentas de SAST quase sempre usavam tokens de instância de app e mostravam o código bastando haver uma conta GitHub dentro da organização
    A Tailscale corrigiu quando foi avisada, a Sonarcloud pediu para não contar a ninguém, e o GitHub respondeu semanas depois que era “comportamento totalmente esperado”, mas nenhum dos fornecedores avisados entendia a coisa dessa forma, e a documentação também contradizia a resposta do GitHub
    Relatar bugs de segurança não é recompensador nem quando você os encontra por acaso, e é difícil imaginar fazer disso uma profissão

    • Essa abordagem de “trazer contas pessoais para a empresa” também cria problemas além de autorização
      O GitHub, muito ocasionalmente, chegou a usar o endereço de e-mail pessoal como padrão ao fazer merge de PRs de trabalho, então, se alguém me perguntar, eu recomendaria não misturar uso pessoal e profissional na mesma conta, seja no GitHub ou em qualquer outro lugar
    • Dá para entender em alguma medida o lado dos fornecedores, porque o GitHub torna extremamente difícil implementar isso corretamente aqui
      Ao criar uma ferramenta de análise de repositórios/commits/PRs, https://dev.log.xyz, deu muito trabalho fazer com que “só o que você consegue ver no GitHub também possa ser visto no Devlog”, e a experiência toda foi muito frustrante
      O seletor de permissões OAuth do GitHub também é confuso demais, então é difícil ter certeza de quais informações de qual organização estão sendo compartilhadas ao “entrar com GitHub”
    • Esse modo de operação se repete em todas as camadas
      Já é difícil convencer jovens desenvolvedores familiarizados com JavaScript de que só validação do lado do cliente não basta; para empresários que definem requisitos de funcionalidade e orçamento, é ainda mais difícil
    • A conveniência de credenciais compartilhadas e a premissa de que SSO pode melhorar a segurança desmoronam com uma vulnerabilidade como esta
      Qualquer forma de cofre de senhas — até mesmo um cofre físico ou a reutilização de senhas — pode acabar sendo mais segura
      No fim, o minimalismo venceu de novo
    • Para quem tenta relacionar o comentário acima com esta pesquisa de vulnerabilidade, isso não parece ter relação
      O texto diz: “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”
  • Recentemente precisei fazer uma implementação de SAML, e esse título não me surpreende nem um pouco
    A especificação SAML em si é bastante razoável, mas XML Signature, que está na base dela — e, indo além, a canonicalização de XML — é de um nível realmente insano, se é que dá para chamar isso de padrão
    Só um comitê conseguiria criar uma especificação tão distorcida e corrompida; parece que a mente de uma única pessoa não conseguiria sustentar e combinar ideias tão contraditórias assim
    Se a assinatura fosse simplesmente transmitida fora da banda, SAML teria se tornado algo agradável de implementar

    • É muito pior do que foi dito
      XML é literalmente eXtensible Markup Language, mas o comitê de padronização do SAML ainda inventou em cima disso sua própria linguagem de mecanismo de extensão
      Colocar um protocolo em cima de outro protocolo para um conjunto minúsculo de dados, não muito diferente do que iria em um cookie de autenticação, é um tipo especial de estupidez que só os maiores e mais burocráticos comitês conseguiriam produzir
    • Não sei se SSO é sequer algo recuperável desde o início
      A ideia de fazer login separadamente em contas diferentes parece ok
      Uma estrutura que conecta contas entre si para que possam ser comprometidas em massa de uma só vez é fundamentalmente mais perigosa
  • SAML, e mais amplamente XML-DSIG, é literalmente o pior entre os protocolos de segurança amplamente usados, na minha opinião
    Em geral, deve-se migrar para OAuth a qualquer custo e, no mínimo, eu me recusaria a lançar um produto novo no mercado que dependesse disso
    É muito perigoso, e é difícil imaginar que esta será a última vulnerabilidade de DSIG, ou a pior, a menos que haja algum avanço em validação prática de formatos

    • Um dia vou escrever sobre todas as coisas inacreditavelmente idiotas que XML DSig faz
      Nem toquei em criptografia, e ainda assim isso já é o ápice do pensamento típico de software corporativo
      Alguém precisa se aprofundar no horror das listas de discussão e dos órgãos de padronização de coisas como WS-* e OASIS/XACML
    • Vai ser interessante ver como o Security Cryptography Whatever vai tratar a bagunça de SAML desta semana
    • Se entendi corretamente, o SAML ainda tem como diferencial o fato de que o provedor de SSO pode cancelar uma sessão
      É isso mesmo?
  • Argh, ninguém deveria usar REXML a menos que não haja outra opção
    Ele aceita de bom grado fazer parsing de XML inválido, causando infinitos problemas nas etapas seguintes
    Literalmente faz parsing de XML com expressões regulares, e é um ótimo exemplo de por que não se deve fazer isso
    Os projetos não começaram a usar Nokogiri por desempenho, mas por corretude

    • Não é um exemplo de manual?
      Não faça parsing com expressões regulares de algo que não é uma linguagem regular
    • Um dos riscos dos assistentes de código com IA é que eles não necessariamente enxergam o contexto mais amplo das bibliotecas usadas em uma base de código grande
      Testei o o3 recentemente e, sempre que ele tentava corrigir um problema não relacionado à biblioteca de um bloco de código específico, continuava trocando a biblioteca usada naquele bloco
      Não vi esse comportamento no Sonnet
      Parece fácil um problema se infiltrar quando, durante a correção, o código é trocado por uma biblioteca/gem inferior que já existe na base de código ou na biblioteca padrão, de modo que os testes passam e nenhuma mudança no Gemfile é necessária
  • SAML é inseguro por design
    Já existem textos antigos que explicam isso melhor; por exemplo, https://joonas.fi/2021/08/saml-is-insecure-by-design/
    A frase que ficou na memória daquele thread antigo foi “assine bytes, não significado”
    Diferenças entre parsers são previsíveis e, às vezes, inevitáveis
    O que você pretende obter de uma resposta assinada é muito importante
    Um dos dilemas do TLS moderno é que, às vezes, você quer confiar em uma única CA interna, e esse é o caminho fácil; mas, ao aceitar o certificado de CA de um parceiro, se houver vários parceiros, você já não pode olhar apenas para o certificado final, e a raiz da cadeia passa a ser igualmente importante na tomada de decisão
    Por isso, quando possível, também recomendo evitar os algoritmos de assinatura da AWS
    A V4 é teoricamente segura, mas a AWS errou duas vezes, e SigV1 e SigV3 eram inseguras por design, ainda assim de algum modo passaram pela revisão de design e foram lançadas publicamente

  • Excelente texto
    Como o próprio artigo menciona, é preciso aplaudir muito ahacker1
    Ele vem fazendo um trabalho extremamente sofisticado e valioso para tornar implementações de SAML seguras, e a SSOReady é realmente grata pelo trabalho dele
    No começo desta semana, a WorkOS também publicou um bom texto sobre a colaboração com ahacker1: https://workos.com/blog/samlstorm

  • Há um trecho dizendo “encontramos um caso explorável dessa vulnerabilidade no GitLab e notificamos a equipe de segurança”; para quem ficou curioso, o GitLab já lançou uma correção
    https://about.gitlab.com/releases/2025/03/12/patch-release-g...

  • Um texto relacionado é o artigo da Latacora de 2019, How (not) to sign a JSON object[1]
    Em resumo, assinar estruturas em árvore aninhadas é difícil e cheio de armadilhas
    É mais fácil que o envelope contenha a mensagem como uma string bruta e assinar essa string bruta
    [1]: https://www.latacora.com/blog/2019/07/24/how-not-to/

  • A conclusão mais simples aqui não seria procurar a assinatura no lugar onde ela deve estar?
    Não use um XPath genérico demais, como “//ds:Signature”, a ponto de acabar encontrando qualquer assinatura em um lugar inesperado

    • As respostas a vulnerabilidades geralmente me parecem frouxas demais
      Não basta remover cirurgicamente apenas o componente perigoso; é preciso jogar fora um pouco do bebê junto com a água do banho, cortar em massa e tratar como quimioterapia
      Qualquer administrador de TI que tenha orgulho do que faz deveria excluir SAML dos planos futuros
      A ideia inteira de SSO também é suspeita, e parsing de XML já apanhou duas vezes nesta semana, então deve ser evitado daqui para frente
      O que há de errado em uma política de substituir XML por JSON?
    • Colocando de forma um pouco radical, a conclusão é que deveríamos pegar um grande porrete e impedir que desenvolvedores web mexam perto de código sensível a segurança
      Isso vale desde o design, os protocolos e os formatos de dados
      Os hábitos e as considerações de design do desenvolvimento web comum não combinam com o que código de segurança exige e, muitas vezes, são exatamente o oposto do necessário para escrever código correto
  • É meio irritante que o post do blog explique a vulnerabilidade mas deixe de fora justamente a diferença entre parsers que causou o problema
    É como escrever a introdução da história e omitir o clímax

    • O post do blog citado em um comentário irmão <https://news.ycombinator.com/item?id=43374972> trazia detalhes relacionados
      Ao fazer (...//ds:DigestValue).firstChild.nodeValue, não verificaram se .firstChild era um Node; no caso problemático, era um Comment
      Assim, o lado não normalizado via a assinatura “oculta”, enquanto o lado modificado, que descartava comentários, via um Node; quando duas implementações chegam a conclusões diferentes sobre um documento assinado, coisas nada engraçadas acontecem
    • Parece que eles não quiseram assumir a responsabilidade de divulgar diretamente um zero-day que permitiria bypass de autorização em incontáveis sistemas pelo mundo antes que os responsáveis por esses sistemas tivessem a chance de corrigir
      Os detalhes concretos devem aparecer em breve