flawopen.com/Incidents/Citrix Bleed

Citrix Bleed: how misreading one return value bypassed multi-factor authentication

Critical — Session hijacking CWE-125: Out-of-bounds Read Disclosed October 2023
ELI5

You ask an assistant to copy a message onto a small card and tell you how long it is. The card only fits 50 characters, so they write 50 — but when you ask how long the message was, they honestly answer "800 characters", because that is how long the original was. You then hand someone the card and tell them to read 800 characters from it. They read your 50, then keep going into whatever was printed on the rest of the sheet.

Key terms on this page
snprintf
A C function that formats a string into a fixed-size buffer. It returns the number of characters that would have been written had the buffer been large enough — not the number actually written.
session token
A credential proving a user has already authenticated. Possessing one means you do not need the password or the second factor, because the authentication already happened.
NetScaler ADC / Gateway
Citrix appliances providing remote access and load balancing, typically deployed at the network edge and facing the internet.

What happened

In October 2023 Citrix disclosed CVE-2023-4966, an information disclosure flaw in NetScaler ADC and NetScaler Gateway. An unauthenticated attacker could send a request with an oversized Host header to an affected endpoint and receive back a chunk of the appliance's memory — which routinely contained valid session tokens belonging to already-authenticated users.

The consequence is what earned the nickname. With a stolen session token, an attacker replays it and is inside as that user. Multi-factor authentication provides no protection, because the token represents a session that already completed authentication. The second factor was satisfied by the legitimate user, hours earlier.

The flaw was exploited as a zero-day before the patch, and afterwards became one of the most heavily abused vulnerabilities of the period — used for initial access by multiple ransomware operations against large organisations. CISA published dedicated remediation guidance.

The technical root cause

The root cause is one of the best-known footguns in the C standard library, and it is worth stating precisely because so much code gets it wrong in the same way.

snprintf returns the length it wanted, not the length it wrote

snprintf truncates its output to fit the destination buffer — that is its entire purpose, and it does this correctly. But its return value is the length of the untruncated string. Code that uses that return value as "how many bytes are now in the buffer" will believe the buffer holds more than it does.

In the affected code, a response was formatted into a fixed static buffer while handling an OpenID Connect discovery request. The return value of snprintf — influenced by the length of the attacker-supplied Host header — was then used to decide how many bytes to send back to the client. A sufficiently long header made that number exceed what was actually written, and the appliance transmitted the difference: adjacent memory, including other users' session tokens.

THE VULNERABLE PATTERN
/* n is what WOULD have been written,
   which may exceed sizeof(buf)      */
int n = snprintf(buf, sizeof(buf),
                 "{\"issuer\":\"https://%s\"}",
                 host);

/* sends n bytes — reading past the
   data actually written into buf   */
send_response(buf, n);
CORRECT
int n = snprintf(buf, sizeof(buf),
                 "{\"issuer\":\"https://%s\"}",
                 host);

/* detect truncation explicitly */
if (n < 0 || (size_t)n >= sizeof(buf))
    return ERR_TOO_LONG;

send_response(buf, n);

/* Better still: validate the Host
   header length before formatting. */

Why patching was not enough

This is the operational lesson teams repeatedly missed, and it mirrors the GitLab path traversal two years later. Upgrading the appliance stopped further memory disclosure — but any session tokens already stolen remained valid. Attackers who had harvested tokens before the patch simply continued using them afterwards.

Citrix's guidance was therefore explicit that remediation required both upgrading and terminating all active sessions on the appliance. Organisations that patched without killing sessions stayed compromised, and several high-profile intrusions followed exactly that path.

The lessons that actually transfer

FAQ

How is this different from Heartbleed?

Both are out-of-bounds reads that leak adjacent process memory to an unauthenticated attacker, and both leaked credentials. The underlying mistake differs: Heartbleed trusted an attacker-supplied length field directly, while Citrix Bleed trusted a length derived from a standard library function whose semantics were misunderstood.

How do I know whether tokens were stolen before I patched?

Often you cannot determine this conclusively from appliance logs, which is precisely why the guidance is to terminate all sessions unconditionally rather than attempt to establish exposure first.

Related reading

Sources