Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions src/pentesting-web/saml-attacks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,64 @@ CSRF delivery pattern:

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.

## Pre-verification XML-signature preprocessing and length oracles

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>

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>

- IdP role: signed `AuthnRequest` or `LogoutRequest` messages at `/saml/login` (`samlIdPProfile`).
- SP role: a `SAMLResponse` assertion signature at `/cgi/samlauth` (`samlAction`).

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>

### Non-destructive patch check with a control request

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>

| Request result | Interpretation |
| --- | --- |
| 575 bytes: `500 Internal Server Error 43549`; 35 bytes: a different response | Size check absent on the reached path (`VULNERABLE`) |
| 575 bytes: `200 Malformed Assertion sent to Netscaler`; 35 bytes: a different response | Size check reached and present (`PATCHED`) |
| Both lengths return the same response | Rejected before the size discriminator (`INCONCLUSIVE`, not patched) |

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>

```bash
# Test each Gateway/AAA VIP, not the management interface
./cve_2026_8452_check.py https://gateway.example.com:9443
./cve_2026_8452_check.py -f targets.txt --brief
./cve_2026_8452_check.py -f targets.txt --json > results.json
```

> 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>

`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>

### Scope and incident triage

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>

```bash
show authentication vserver
show vpn vserver
show authentication samlAction
show authentication samlIdPProfile
show ns runningConfig | grep -i saml
show ns version
```

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>

```bash
find /var/core -name 'NSPPE-*'
grep -Ei 'nsppe:.*signal (10|11)|pitboss.*unexpectedly died' /var/log/ns.log
zgrep -Ei 'nsppe:.*signal (10|11)|pitboss.*unexpectedly died' /var/log/ns.log*.gz
find /var/vpn/theme -type f
```

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>

## Unterminated / unquoted SAML attribute overread (IdP parser bugs)

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>
Expand Down Expand Up @@ -497,5 +555,8 @@ The same parser weakness that gives an overread can also crash the SAML processi
- [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)
- [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/)
- [15] [youtube.com - Watch](https://www.youtube.com/watch?v=WHn-6xHL7mI)
- [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/)
- [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)
- [18] [BishopFox CVE-2026-8452 patch-state detector](https://github.com/BishopFox/CVE-2026-8452-check)

{{#include ../../banners/hacktricks-training.md}}