Jun 9, 2026·8 min read·6 visits
Dex token-exchange endpoint fails to validate AllowedConnectors, allowing attackers with a compromised client secret to escalate privileges via unauthorized identity providers.
An improper authorization vulnerability in the unreleased development master branch of Dex allows clients to bypass the AllowedConnectors access control list using the token-exchange endpoint.
Dex is an open-source OpenID Connect (OIDC) identity and OAuth 2.0 provider that acts as an intermediary portal to various external identity providers. It allows organizations to establish centralized authentication across federated platforms, managing user directories like Okta, Active Directory, LDAP, or GitHub. The primary attack surface of Dex is exposed through its HTTP server endpoints, which handle authentication flows, client registrations, and token distribution.
In multi-tenant or enterprise environments, clients are configured with varying access levels and security policies. To maintain isolation, Dex introduced the AllowedConnectors configuration field, which acts as an Access Control List (ACL) limiting the identity providers a specific OAuth 2.0 client can leverage. For example, a client for a public-facing developer application can be restricted to standard social logins, whereas an administrative portal client might be restricted exclusively to a secure corporate directory.
A critical flaw in the unreleased development master branch of Dex allows this connector restriction to be bypassed. Specifically, the token-exchange endpoint, which processes token swap requests under RFC 8693, fails to enforce the client-specific AllowedConnectors restrictions. Consequently, an attacker holding a valid client secret can access any configured backend connector, neutralizing the configured isolation boundary and escalating privileges.
The root cause of GHSA-7QJX-GP9H-65QJ lies in the omission of an authorization check within the handleTokenExchange handler located in server/handlers.go. This endpoint was introduced to process RFC 8693 token-exchange requests, facilitating the exchange of security tokens issued by external identity providers for Dex-signed identity tokens. When a token-exchange request is received, the handler processes client authentication and validates the request structure before proceeding to map the user's claims.
During the normal authorization-code flow, Dex strictly validates the requested identity provider against the client's AllowedConnectors schema. If a client attempts to initialize a session using an unauthorized connector, the request is blocked during either authorization parsing or interactive login. The helper function isConnectorAllowed(allowedList []string, targetConn string) bool performs this validation by checking if the target connector ID is present in the client's configuration list.
However, within the handleTokenExchange code path, the developers only implemented checks to verify that the connector itself was active and permitted token-exchange grants. The logic completely bypassed any validation of the client's AllowedConnectors ACL. Because this authorization check is missing, any authenticated client can initiate a token exchange with any connector configured on the Dex server, irrespective of the administrator's intended access controls.
Analyzing the vulnerable code path in server/handlers.go demonstrates the missing authorization verification. In the vulnerable version of the master branch, the handleTokenExchange function processes the exchange of credentials but does not reference the client's configuration when evaluating the target connector. The following block highlights the vulnerable implementation before the security fix was applied.
// server/handlers.go (Vulnerable Code Path)
func (s *Server) handleTokenExchange(w http.ResponseWriter, r *http.Request) {
// ... client authentication and request validation ...
connID := r.FormValue("connector_id")
if connID == "" {
s.tokenErrHelper(w, errInvalidRequest, "Missing connector_id.", http.StatusBadRequest)
return
}
conn, err := s.getConnector(ctx, connID)
if err != nil {
s.tokenErrHelper(w, errInvalidGrant, "Connector failed to initialize.", http.StatusBadRequest)
return
}
if !GrantTypeAllowed(conn.GrantTypes, grantTypeTokenExchange) {
s.tokenErrHelper(w, errUnsupportedGrantType, "Connector does not support token exchange.", http.StatusBadRequest)
return
}
// MISSING: isConnectorAllowed(client.AllowedConnectors, connID) check
// Token extraction and issuance proceed unconditionally
identity, err := conn.Connector.ExchangeToken(ctx, r)
// ...
}To correct this structural omission, the patch introduces the missing helper validation immediately prior to executing the token exchange. The patched code queries the client's configurations and evaluates them using the pre-existing validation helper. If the evaluation fails, execution halts and a 403 Forbidden error status is returned to the client, preventing unauthorized token generation.
// server/handlers.go (Patched Code Path)
func (s *Server) handleTokenExchange(w http.ResponseWriter, r *http.Request) {
// ... client authentication and request validation ...
connID := r.FormValue("connector_id")
if connID == "" {
s.tokenErrHelper(w, errInvalidRequest, "Missing connector_id.", http.StatusBadRequest)
return
}
conn, err := s.getConnector(ctx, connID)
if err != nil {
s.tokenErrHelper(w, errInvalidGrant, "Connector failed to initialize.", http.StatusBadRequest)
return
}
if !GrantTypeAllowed(conn.GrantTypes, grantTypeTokenExchange) {
s.tokenErrHelper(w, errUnsupportedGrantType, "Connector does not support token exchange.", http.StatusBadRequest)
return
}
// PATCH: Enforce AllowedConnectors ACL
if !isConnectorAllowed(client.AllowedConnectors, connID) {
s.logger.ErrorContext(r.Context(), "connector not allowed for client via token-exchange",
"connector_id", connID, "client_id", client.ID)
s.tokenErrHelper(w, errInvalidGrant, "Connector not allowed for this client.", http.StatusForbidden)
return
}
identity, err := conn.Connector.ExchangeToken(ctx, r)
// ...
}This patch is complete because it relies on the same centralized logic (isConnectorAllowed) used by other authentication endpoints. It ensures that the client-to-connector ACL is strictly enforced regardless of the ingress mechanism or grant type. Because the remediation is implemented on the server side prior to contacting the external connector, there are no known alternative paths to bypass this logic within the token exchange context.
Exploiting this vulnerability requires specific prerequisites, making it a high-privilege attack vector. The attacker must obtain a valid client secret for an existing client that has restricted connector configuration, such as a developer application. Additionally, the attacker must have a valid identity token from an external identity provider that is configured on the Dex server but restricted for that specific client.
Once these parameters are secured, the attacker constructs a crafted HTTP POST request to the Dex /token endpoint. The attacker sets the grant_type parameter to urn:ietf:params:oauth:grant-type:token-exchange and provides the compromised client credentials. Critically, they specify the restricted connector's identifier within the connector_id parameter and include their external identity token inside the subject_token parameter.
POST /token HTTP/1.1
Host: dex.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
&client_id=low-trust-client-id
&client_secret=compromised-client-secret
&connector_id=restricted-corporate-idp
&subject_token=eyJhbGciOiJSUzI1NiIs...
&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token
&scope=openid+groupsUpon receiving this request, Dex authenticates the client using the compromised secret. Since the vulnerability prevents checking the AllowedConnectors list, Dex contacts the restricted IDP, processes the token exchange, and generates a Dex-signed identity token for the low-trust client. This new token contains groups and privileges belonging to the attacker, allowing them to access downstream applications that trust the low-trust client but base authorization decisions on identity claims.
The security impact of GHSA-7QJX-GP9H-65QJ is assessed as High, resulting in a CVSS v3.1 score of 8.7. The vulnerability represents an authorization bypass that breaks the trust boundaries defined by administrators to isolate low-trust and high-trust environments. By exploiting this flaw, an attacker holding a compromised secret can leverage external credentials to mint unauthorized administrative-level tokens.
The CVSS Vector string CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N highlights key aspects of this risk vector. The attack vector is Network (AV:N), as the endpoint is accessible over standard TLS/HTTP pathways. The Scope is Changed (S:C) because compromising a single client secret allows the attacker to obtain a valid identity token that downstream applications rely on for authorization.
This downstream escalation is particularly impactful in Kubernetes or microservice environments where Dex serves as the central identity provider. Many internal applications trust Dex implicitly to authenticate users and pass identity claims such as group memberships. Consequently, an attacker can escalate access from a simple developer scope to cluster-level administrator privileges depending on the external token claims they possess.
Remediation of this vulnerability requires upgrading the Dex installation to a commit that includes the validation check. Because the vulnerability only exists on the unreleased development master branch, standard production deployments running on stable tags such as v2.45.1 or older are unaffected. Operators using master branch builds must pull and rebuild Dex using master branch commit 204dbb2e3ff7692af3b7ca4362b1ee46fb43c227 or later.
If an immediate upgrade is not feasible, operators can apply several temporary mitigations to lower the risk. First, rotate all client secrets regularly, especially those associated with low-trust clients that may have broader exposure. Second, configure the web application firewall (WAF) or ingress controller to drop or block incoming POST requests to the /token endpoint that contain the token-exchange grant type parameter, provided this feature is not explicitly required by business workflows.
To detect potential exploitation or verify vulnerability status, security teams can audit historical HTTP server logs for anomalous token-exchange patterns. A high volume of token exchange requests coming from client IDs that are restricted to specific connectors is a strong indicator of unauthorized attempts. Furthermore, teams can conduct structural code review on server/handlers.go to ensure that the isConnectorAllowed validator resides within the body of the handleTokenExchange function.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Dex DexIDP | Master branch (>= f80a89d and < 204dbb2e3ff7) | Master branch commit 204dbb2e3ff7692af3b7ca4362b1ee46fb43c227 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-285 |
| Attack Vector | Network |
| CVSS v3.1 | 8.7 (High) |
| Impact | Privilege Escalation |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The software does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.
An unauthenticated remote code execution (RCE) vulnerability exists in phoenix_storybook versions 0.5.0 through 1.0.x due to improper input sanitization during HEEx template generation. By sending crafted WebSocket messages, an attacker can escape HTML attribute boundaries and execute arbitrary Elixir code.
An unauthenticated Denial-of-Service (DoS) vulnerability exists in phoenix_storybook versions 0.2.0 through 1.0.11 due to allocation of resources without limits (CWE-770). The application dynamically converts user-supplied parameter keys to atoms, leading to BEAM Atom Table exhaustion and immediate virtual machine crash.
A security vulnerability in the Elixir package phoenix_storybook (versions 0.4.0 up to 1.1.0) allows unauthenticated remote attackers to perform cross-session PubSub topic injection. By manipulating URL parameters, an attacker can hijack the real-time communications channel, enabling them to capture user state and control parameters from active sessions.
CVE-2024-29203 identifies a cross-site scripting (XSS) vulnerability in the content ingestion and parsing mechanics of TinyMCE rich text editor. Due to a failure to enforce sandbox attributes on dynamic iframe elements and safely handle legacy embed objects, unauthenticated attackers can inject malicious elements that execute scripts within the context of the parent application session.
A technical breakdown of the OS command injection vulnerability in the shell-quote NPM package (CVE-2026-9277 / GHSA-w7jw-789q-3m8p). The bug resides in the character-by-character backslash-escaping logic applied to the .op field of object-tokens within the quote() function, which fails to match and escape line terminators due to a regex matching oversight in JavaScript. This allows unauthenticated remote attackers to execute arbitrary shell commands if they can control inputs processed by this library.
A high-severity memory corruption vulnerability exists in the V8 JavaScript engine of Google Chrome before versions 149.0.7827.102/103. The flaw arises from an incorrect bounds-check elimination during JIT compilation by the TurboFan optimizer, allowing remote attackers to achieve out-of-bounds read and write access inside the sandboxed renderer process.