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
87 changes: 87 additions & 0 deletions src/pentesting-web/saml-attacks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,95 @@ 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.

## 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**.

This is especially interesting when the parsed fields are later **reflected** into:

- cookies
- logs
- redirect parameters
- debugging/error responses

### Quick detection idea

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.

```xml
<samlp:AuthnRequest Version="2.0" AssertionConsumerServiceURL=11
ID=22>
<saml:Issuer>test</saml:Issuer>
</samlp:AuthnRequest>
```

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.

### Escalating from parser confusion to overread

Useful heuristics when fuzzing SAML IdP parsers:

- Keep the **high-level SAML requirements** valid somewhere in the document (for example `AuthnRequest`, closing tag, valid `Issuer`).
- Corrupt the **low-level parser state** with an **unterminated opening tag** or an **unterminated attribute**.
- Move required elements into **weird but still accepted locations** to satisfy semantic checks while the attribute scanner keeps reading.
- Try payloads where the final attribute is left unterminated at the end of the request:

```xml
<samlp:AuthnRequest
<saml:Issuer>test</saml:Issuer>
</samlp:AuthnRequest>
Version="2.0"
ID="11"
AssertionConsumerServiceURL=
```

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

### Reflected sink hunting

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.

A practical workflow is:

1. Send a base64-encoded `SAMLRequest` to the IdP endpoint.
2. Capture the response without following redirects.
3. Extract and base64-decode the reflected cookie / parameter.
4. Inspect the parsed field (`ACSURL`, `ID`, etc.) for data that was never in your original request.

```bash
python3 - <<'PY'
import base64
print(base64.b64decode('NSC_TASS_VALUE_HERE'))
PY
```

If the leaked field contains:

- fragments of later XML tags
- stale heap/stack marker bytes
- partial pointers
- binary data that changes with request length

then you likely have a **real memory disclosure primitive**, not just malformed-XML confusion.

### Request-length shaping

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.

### DoS variant

Also try **incomplete attributes** such as:

```xml
<samlp:AuthnRequest ID=
```

The same parser weakness that gives an overread can also crash the SAML processing worker.

## References

- [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/)
- [watchTowr-vs-Netscaler-CVE-2026-8451](https://github.com/watchtowrlabs/watchTowr-vs-Netscaler-CVE-2026-8451)
- [https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/)
- [https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-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/)
- [https://epi052.gitlab.io/notes-to-self/blog/2019-03-16-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/)
Expand Down