Feb 26, 2026·6 min read·11 visits
Directus versions before 11.14.0 failed to validate the destination URL provided in the SAML `RelayState` parameter during the login callback. Attackers can craft malicious login links that, upon successful authentication, redirect users to an external, attacker-controlled domain.
A classic Open Redirect vulnerability in the Directus Headless CMS SAML authentication flow allows attackers to hijack the post-login redirection. By manipulating the SAML `RelayState` parameter, an attacker can turn a legitimate authentication flow into a gateway for phishing or session theft.
SAML (Security Assertion Markup Language) is XML's revenge on humanity. It is a beast of a protocol—complex, verbose, and historically fragile. But enterprises love it because it powers Single Sign-On (SSO). If you are using Directus in a corporate environment, you are probably using SAML to let your users log in via Okta, Auth0, or Active Directory.
Here is the scenario: You trust Directus to handle your data. You trust your Identity Provider (IdP) to handle your users. But trust is a transitive property in distributed systems. If Directus blindly trusts a parameter passed through the IdP, that chain of trust breaks.
CVE-2026-22032 is a textbook Open Redirect vulnerability hiding in the complexity of the SAML handshake. It is not a remote code execution (RCE) that will burn your server down instantly, but it is the kind of subtle logic flaw that makes sophisticated phishing campaigns succeed. It turns your legitimate login portal into an open door for redirection attacks.
To understand this bug, you need to understand the concept of RelayState. In the SAML protocol, when a user initiates a login, the Service Provider (Directus) often needs to remember where the user was trying to go before they were rudely interrupted by a login screen. This state is preserved in a parameter called RelayState.
The flow looks like this:
/admin/content.RelayState=/admin/content.RelayState.RelayState to send them to their destination.The vulnerability here is a classic case of asymmetric validation. Directus was smart enough to check where it was sending users during the initiation of the request. However, it completely dropped the ball during the callback (step 5). When the user came back from the IdP, Directus took the RelayState value—which effectively comes from the client—and tossed it into a res.redirect() without checking if it pointed to a safe, local path.
The vulnerability lived in @directus/api, specifically within api/src/auth/drivers/saml.ts. This file handles the heavy lifting for SAML authentication. The logic for the callback endpoint essentially extracted the RelayState and treated it as gospel.
Below is a reconstruction of the logic flow based on the patch analysis. The developer assumed that because the RelayState made the round trip through the trusted IdP, it was still safe. Spoiler: It wasn't.
// simplified pseudo-code of the vulnerable state
const relayState = body.RelayState || query.RelayState;
// ... verify SAML signature ...
// The fatal flaw: Blindly redirecting
if (relayState) {
return res.redirect(relayState);
}The fix, applied in commit dad9576ea9362905cc4de8028d3877caff36dc23, introduces a mandatory check using a utility function isLoginRedirectAllowed. This function likely checks the URL against the configured PUBLIC_URL or ensures it is a relative path (e.g., starts with / but not //).
import { isLoginRedirectAllowed } from '../../utils/is-login-redirect-allowed.js';
// ... inside the callback handler ...
if (relayState && isLoginRedirectAllowed(relayState, providerName) === false) {
// If the URL is sketchy, we throw an error instead of redirecting
throw new InvalidPayloadError({
reason: `URL "${relayState}" can't be used to redirect after login`
});
}
return res.redirect(relayState);This simple if statement closes the loop. It enforces that even if an attacker manages to inject a malicious URL into the RelayState, the application refuses to honor it upon return.
Exploiting this does not require a debugger or heap spraying. It requires social engineering. An attacker wants to steal credentials or session tokens from an employee of a company using Directus.
Recon: The attacker notices the target company uses Directus at cms.corp.com.
Crafting the Link: The attacker creates a URL that initiates the SAML login but includes a malicious RelayState. Since the initiation flow might validate it, the attacker might target the callback endpoint directly or find a way to initiate the flow where the parameters aren't scrutinized until the return trip.
Target URL: https://cms.corp.com/auth/login/saml?RelayState=https://attacker-site.com/fake-login
The Phish: The attacker sends an email: "URGENT: Please review the Q1 content draft on the CMS."
The Execution:
https://attacker-site.com/fake-login.The Harvest: The attacker's site looks exactly like the Directus dashboard but displays a "Session Expired, please re-authenticate" message. The user, having just logged in, thinks "stupid glitch" and enters their credentials again. Game over.
You might look at a CVSS score of 4.3 and think, "I'll patch this next quarter." That is a mistake. While the technical severity is Medium because it requires user interaction (UI:R) and doesn't directly compromise the server (Scope: Unchanged), the business impact can be severe.
Open redirects are force multipliers for phishing. They bypass the most common advice given to users: "Check the URL." When the link starts with https://cms.your-company.com, the user's guard goes down.
Furthermore, because the redirection happens after a successful login, the attacker lands the user on their malicious site at the exact moment the user expects to land on a dashboard. This context switching is disorienting and makes the subsequent social engineering attack significantly more likely to succeed.
The remediation is straightforward: Update to Directus 11.14.0. The Directus team has patched the hole by ensuring the callback strictly validates the RelayState against allowed domains.
If you cannot upgrade immediately (why?), you might be able to mitigate this at the network layer. If you have a WAF (Web Application Firewall), you could theoretically create a rule to inspect the RelayState parameter in POST requests to /auth/login/saml/callback and block values that contain http:// or https:// pointing to external domains. However, regex is fragile and WAF rules are band-aids. The real fix is in the code.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Directus Directus | < 11.14.0 | 11.14.0 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-22032 |
| CVSS v3.1 | 4.3 (Medium) |
| CWE | CWE-601 (Open Redirect) |
| Attack Vector | Network (Spearphishing) |
| Affected Component | SAML Authentication Driver |
| Patch Commit | dad9576ea9362905cc4de8028d3877caff36dc23 |
The web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This facilitates phishing attacks.