Uma falha crítica de segurança foi descoberta na biblioteca ruby-saml, utilizada para autenticação de usuários em diversos serviços. A vulnerabilidade permite que um atacante, com posse de uma assinatura válida, burle a autenticação e se passe por qualquer usuário. A atualização para a versão 1.18.0 é crucial para evitar o bypass de autenticação SAML e proteger contas de usuários.
A seguir, vamos detalhar como essa vulnerabilidade funciona e quais medidas podem ser tomadas para mitigar os riscos.
Detalhes da Vulnerabilidade em ruby-saml
A biblioteca ruby-saml, amplamente utilizada para autenticação única (SSO) via SAML, foi alvo de uma descoberta preocupante. Pesquisadores identificaram falhas que permitem o bypass de autenticação SAML, comprometendo a segurança de diversos sistemas que dependem dessa biblioteca.
A vulnerabilidade reside na forma como a biblioteca processa e valida as assinaturas SAML. Um atacante que possua uma única assinatura válida, criada com a chave de validação da organização alvo, pode construir suas próprias declarações SAML e, assim, efetuar login como qualquer usuário. Essa falha abre portas para ataques de apropriação de contas, onde um invasor assume o controle total de uma conta legítima.
O GitHub, que já utilizou a ruby-saml no passado, mas migrou para uma implementação própria devido à falta de funcionalidades, estava reavaliando o uso da biblioteca. A descoberta dessa vulnerabilidade reforçou a necessidade de uma análise de segurança rigorosa antes de qualquer implementação.
Em outubro de 2024, outra vulnerabilidade de bypass de autenticação SAML (CVE-2024-45409) foi encontrada na ruby-saml. Esse evento motivou o GitHub a iniciar um programa de bug bounty para avaliar a segurança da biblioteca de forma mais aprofundada.
A Descoberta da Vulnerabilidade: Dois Parsers em Ação
Durante a revisão do código, tanto um pesquisador participante do programa de bug bounty do GitHub quanto um membro do GitHub Security Lab notaram um comportamento incomum: a ruby-saml utilizava dois parsers XML diferentes durante o processo de verificação de assinatura: REXML e Nokogiri.
REXML é um parser XML implementado em Ruby, enquanto Nokogiri oferece uma API para diferentes bibliotecas, como libxml2. Nokogiri foi adicionado à ruby-saml para suportar a canonicalização e outras funcionalidades não suportadas pelo REXML na época.
A análise da função validate_signature
no arquivo xml_security.rb
revelou que o elemento de assinatura a ser verificado era lido tanto pelo REXML quanto pelo parser XML do Nokogiri. Essa constatação levantou a possibilidade de que, se os parsers fossem induzidos a recuperar diferentes elementos de assinatura para a mesma query XPath, seria possível enganar a ruby-saml para verificar a assinatura errada.
Essa diferença de interpretação entre os parsers, conhecida como “parser differential“, poderia levar a um bypass de autenticação SAML.
As Etapas para a Exploração da Falha
A descoberta desse bypass de autenticação SAML envolveu quatro etapas principais:
- Identificação do uso de dois parsers XML diferentes durante a revisão de código.
- Avaliação de como essa diferença de parsers poderia ser explorada.
- Descoberta de uma diferença real de parser para os parsers em uso.
- Criação de um exploit completo para demonstrar o impacto da vulnerabilidade.
Para comprovar o impacto da vulnerabilidade, foi necessário completar todas as etapas e criar um exploit que permitisse o bypass de autenticação SAML.
Como as Respostas SAML São Validadas
As respostas SAML (Security Assertion Markup Language) são utilizadas para transmitir informações sobre um usuário autenticado do provedor de identidade (IdP) para o provedor de serviços (SP) em formato XML. Frequentemente, a informação mais importante é o nome de usuário ou o endereço de e-mail.
Quando o binding HTTP POST é utilizado, a resposta SAML viaja do IdP para o SP através do browser do usuário final. Por isso, é crucial que haja algum tipo de verificação de assinatura para evitar que o usuário manipule a mensagem.
Em termos gerais, a parte principal de uma resposta SAML simples é o elemento de declaração (Assertion), que contém informações como o nome de usuário. Normalmente, a Assertion é canonicalizada e comparada com o DigestValue, enquanto o SignedInfo é canonicalizado e verificado contra o SignatureValue.
Em alguns casos, a Assertion é assinada, enquanto em outros, toda a resposta SAML é assinada.
A Busca por Diferenças de Parsers
Com o conhecimento de que a ruby-saml utiliza dois parsers XML diferentes (REXML e Nokogiri) para validar a resposta SAML, a atenção se voltou para a verificação da assinatura e a comparação do digest.
A função validate_signature
no arquivo xml_security.rb
realiza uma query XPath com o REXML para encontrar o primeiro elemento de assinatura dentro do documento SAML. Posteriormente, o SignatureValue é lido desse elemento.
É importante notar que o elemento Signature contém tanto a assinatura real no nó SignatureValue quanto a parte que é realmente assinada no nó SignedInfo. O elemento DigestValue contém o digest (hash) da Assertion e informações sobre a chave utilizada.
Mais adiante na mesma função, outra query é realizada para encontrar as Signature(s), mas dessa vez com o Nokogiri. O elemento SignedInfo é então extraído dessa assinatura e canonicalizado.
O elemento SignedInfo também é extraído com o REXML, e a partir desse elemento, o nó Reference é lido. O código então procura por nós com o ID do elemento assinado utilizando o Nokogiri.
Explorando as Diferenças de Parsers
A questão central é: seria possível criar um documento XML onde o REXML veja uma assinatura e o Nokogiri veja outra?
A resposta é sim. Um dos pesquisadores participantes do bug bounty conseguiu criar um exploit funcional utilizando uma diferença de parsers, inspirando-se em vulnerabilidades de XML roundtrips publicadas por Juho Forsén, da Mattermost.
Outro pesquisador desenvolveu um exploit utilizando uma diferença de parser diferente, com a ajuda do fuzzer Ruby da Trail of Bits, chamado ruzzy.
Ambos os exploits resultam em um bypass de autenticação SAML. Isso significa que um atacante, com posse de uma única assinatura válida criada com a chave utilizada para validar as respostas ou declarações SAML da organização alvo, pode utilizá-la para construir declarações para qualquer usuário, que serão aceitas pela ruby-saml.
Essa assinatura pode vir de uma declaração ou resposta assinada de outro usuário (não privilegiado) ou, em certos casos, até mesmo de metadados assinados de um provedor de identidade SAML (que pode ser acessível publicamente).
Como o Ataque Acontece na Prática
Em um ataque, uma assinatura adicional é adicionada como parte do elemento StatusDetail, que é visível apenas para o Nokogiri. O elemento SignedInfo da assinatura visível para o Nokogiri é canonicalizado e verificado contra o SignatureValue extraído da assinatura vista pelo REXML.
A Assertion é recuperada via Nokogiri procurando por seu ID. Essa Assertion é então canonicalizada e hashed. O hash é comparado com o hash contido no DigestValue, que foi recuperado via REXML. Esse DigestValue não tem uma assinatura correspondente.
Assim, duas coisas acontecem:
- Um SignedInfo válido com DigestValue é verificado contra uma assinatura válida (o que é confirmado).
- Uma Assertion canonicalizada fabricada é comparada com seu digest calculado (o que também é confirmado).
Isso permite que um atacante, em posse de uma Assertion assinada válida para qualquer usuário (não privilegiado), fabrique Assertions e, assim, se passe por qualquer outro usuário.
Verificação de Erros ao Utilizar o Nokogiri
Parte dos exploits atualmente conhecidos pode ser evitada verificando se há erros de parsing do Nokogiri nas respostas SAML. Infelizmente, esses erros não resultam em exceções, mas precisam ser verificados no membro errors
do documento parsed.
doc = Nokogiri::XML(xml) do |config|
config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET
end
raise "XML errors when parsing: " + doc.errors.to_s if doc.errors.any?
Embora essa não seja uma correção perfeita para os problemas em questão, ela torna pelo menos um exploit inviável.
Recomendações e Próximos Passos
A vulnerabilidade exposta na biblioteca ruby-saml serve como um lembrete crítico da importância de validações rigorosas e da complexidade inerente ao uso de múltiplos parsers em contextos de segurança. Para os usuários da biblioteca, a atualização imediata para a versão 1.18.0 é essencial para mitigar os riscos de bypass de autenticação SAML.
Além disso, é recomendável verificar a presença de erros de parsing do Nokogiri nas respostas SAML, como medida adicional de segurança. A colaboração entre pesquisadores de segurança e mantenedores de bibliotecas é fundamental para identificar e corrigir vulnerabilidades, garantindo a segurança dos sistemas que dependem dessas ferramentas.
Esteja atento a logins suspeitos via SAML, especialmente de endereços IP que não correspondem à localização esperada do usuário, pois podem indicar uma tentativa de exploração da vulnerabilidade.
Como alternativa, considere usar um padrão menos complexo para transportar um nome de usuário assinado criptograficamente entre dois sistemas.
Para se proteger, verifique se há erros de análise do Nokogiri nas respostas SAML. Essa medida, embora não seja uma solução completa, pode impedir pelo menos uma exploração prática.
Este conteúdo foi auxiliado por Inteligência Artificiado, mas escrito e revisado por um humano.
Via The GitHub Blog