Nov 9, 2026·9 min read·1 visit
A logic flaw in traQ's OAuth 2.0 authorization endpoint allows registered OAuth clients with limited prior user consent to silently escalate privileges to all client-registered scopes by sending a `prompt=none` authorization request with an empty `scope` parameter.
CVE-2026-63198 (GHSA-8cfq-jgf7-8m27) is an incorrect authorization vulnerability (CWE-863) in traQ, an open-source messaging platform developed in Go by Digital Creators Club traP. The flaw resides in the OAuth 2.0 authorization endpoint handler (`router/oauth2/authorization_endpoint.go`). When processing silent authorization requests (`prompt=none`) with an omitted or empty `scope` parameter, the application evaluates prior user consent against the empty requested scope set prior to applying default scope fallback logic. Because an empty set vacuously satisfies the subset check against any existing token, consent verification succeeds if the user has ever authorized the client for any single scope. Subsequently, the token issuance logic defaults the empty scope parameter to all scopes registered to the client (`req.ValidScopes`), allowing a client application with partial consent to silently escalate privileges without user interaction.
CVE-2026-63198 (tracked as GHSA-8cfq-jgf7-8m27) is an authorization bypass and privilege escalation vulnerability in traQ, an open-source team messenger application written in Go. Within the traQ architecture, third-party applications and integrations interact with user data and platform APIs via an internal OAuth 2.0 authorization server implementation.
The vulnerability is located in the OAuth 2.0 authorization endpoint handler (AuthorizationEndpointHandler in router/oauth2/authorization_endpoint.go). This endpoint is responsible for validating authorization requests, verifying active user sessions, enforcing user consent requirements, and issuing authorization codes or access tokens.
Classified under CWE-863 (Incorrect Authorization), the flaw occurs when processing OpenID Connect / OAuth 2.0 silent authorization requests (prompt=none) where the scope parameter is either omitted or set to an empty string (scope=""). Due to an order-of-operations discrepancy between scope validation and default scope assignment, the endpoint validates prior user consent against the empty request scope rather than the expanded effective scope.
Consequently, any registered OAuth 2.0 client that a user has previously authorized for a limited subset of scopes (for example, read) can silently obtain an authorization code and access token encompassing all scopes registered to that client (req.ValidScopes, such as write or manage) without triggering a consent prompt.
Under the OAuth 2.0 framework (RFC 6749) and OpenID Connect Core 1.0 specifications, the prompt=none parameter instructs the authorization server that it must not display any authentication or consent user interface pages. If the user is not already authenticated or has not previously granted consent for the requested scopes, the server must immediately return an error response (login_required or consent_required). Simultaneously, RFC 6749 Section 3.3 permits clients to omit the scope parameter, allowing the server to fall back to a pre-defined set of default scopes.
In traQ versions prior to 3.28.3, when an incoming authorization request omits the scope parameter, the request parser populates req.Scopes as an empty map (len(req.Scopes) == 0). However, during the downstream token and authorization code issuance phase, traQ treats an empty req.Scopes value as an instruction to grant req.ValidScopes—the complete set of scopes configured for that specific OAuth 2.0 client during registration.
The root cause of CVE-2026-63198 is that the prompt=none consent verification routine inspects req.Scopes directly before expanding empty scopes into req.ValidScopes. To verify whether the user has previously consented to the requested scopes, the handler retrieves all active tokens (tokens) belonging to the user and iterates through them to find a token matching req.ClientID that contains every scope in req.Scopes.
Because req.Scopes is empty, the inner verification loop (for s := range req.Scopes) executes zero iterations. The boolean accumulator all is initialized to true prior to the loop and remains true by vacuous truth. As long as the user has at least one existing token for req.ClientID in the database—regardless of how restricted its scopes are—the condition passes, setting ok = true. The handler bypasses the consent_required rejection and proceeds to mint an authorization code carrying the client's full req.ValidScopes.
The vulnerability and its corresponding fix are contained within router/oauth2/authorization_endpoint.go in commit 5510fe8aa9bb15fc4755c78792381dbb03d576c3. Prior to the patch, the prompt=none evaluation block checked req.Scopes directly against each stored token's scope set (v.Scopes).
--- a/router/oauth2/authorization_endpoint.go
+++ b/router/oauth2/authorization_endpoint.go
@@ -203,10 +203,14 @@ func (h *Handler) AuthorizationEndpointHandler(c *echo.Context) error {
return c.Redirect(http.StatusFound, redirectURI.String())
}
ok := false
+ requiredScopes := req.Scopes
+ if len(requiredScopes) == 0 {
+ requiredScopes = req.ValidScopes
+ }
for _, v := range tokens {
if v.ClientID == req.ClientID {
all := true
- for s := range req.Scopes {
+ for s := range requiredScopes {
if !v.Scopes.Contains(s) {
all = false
breakIn the patch, the developers introduced a local variable requiredScopes initialized to req.Scopes. Before entering the token comparison loop, the code explicitly checks if len(requiredScopes) == 0 and assigns requiredScopes = req.ValidScopes when the condition is met. This aligns the scope set evaluated during consent verification with the scope set that will ultimately be granted during authorization code generation.
With this change in place, if a client submits prompt=none with an empty scope parameter, the inner loop for s := range requiredScopes iterates across every scope in req.ValidScopes. If the user's existing token (v.Scopes) only contains partial permissions (such as read when req.ValidScopes contains both read and write), !v.Scopes.Contains("write") evaluates to true, setting all = false and breaking out of the loop.
> [!NOTE]
> Fix Completeness Assessment: The patch completely resolves the discrepancy in the prompt=none code path by normalizing the effective scope set prior to verification. Additionally, if a user has no prior tokens for req.ClientID, the outer loop for _, v := range tokens never matches v.ClientID == req.ClientID, ensuring ok remains false in both unpatched and patched versions.
Exploiting CVE-2026-63198 requires a specific set of operational preconditions. First, the attacker must control a registered OAuth 2.0 client on the target traQ instance (client_id and redirect_uri) configured with multiple or privileged scopes (e.g., read, write, manage). Second, the target victim user must have previously granted consent to this client for at least one restricted scope (e.g., read). Third, the victim must have an active authenticated session cookie on the traQ web interface.
To execute the attack, the adversary lures the authenticated user to a web page controlled by the adversary or triggers a cross-origin navigation. Because prompt=none is designed for silent background authentication, the attacker can embed a hidden HTML <iframe> or top-level redirect that submits a request to the /oauth2/authorize endpoint:
POST /oauth2/authorize HTTP/1.1
Host: traq.example.com
Cookie: r_session=<victim_session_cookie>
Content-Type: application/x-www-form-urlencoded
client_id=<attacker_client_id>&response_type=code&prompt=none&scope=&redirect_uri=https://attacker.example.com/callbackWhen traQ processes this request, it validates the victim's session cookie, confirms that an existing token exists for <attacker_client_id>, and evaluates the empty scope parameter via the vacuous loop. Instead of redirecting to the callback URI with ?error=consent_required, traQ generates a valid authorization code bound to req.ValidScopes and responds with an HTTP 302 Found redirect containing ?code=<authorization_code>. The attacker's callback server then exchanges this code at /oauth2/token to obtain an access token with full read, write, and manage capabilities.
The official patch commit (5510fe8aa9bb15fc4755c78792381dbb03d576c3) includes a unit test in router/oauth2/authorization_endpoint_test.go that reproduces this exact attack scenario:
t.Run("Found (prompt=none with empty scope and partial consent)", func(t *testing.T) {
t.Parallel()
assert := assert.New(t)
user := env.CreateUser(t, rand, useUUIDV4)
scopesReadWrite := model.AccessScopes{}
scopesReadWrite.Add("read", "write")
clientReadWrite := &model.OAuth2Client{
ID: random.AlphaNumeric(36),
Name: "test client",
Confidential: false,
CreatorID: creatorID,
Secret: random.AlphaNumeric(36),
RedirectURI: "http://example.com",
Scopes: scopesReadWrite,
}
require.NoError(t, env.Repository.SaveClient(context.TODO(), clientReadWrite))
// User previously granted only 'read' scope (`scopesRead`) to the client
_, err := env.Repository.IssueToken(context.TODO(), clientReadWrite, user.GetID(), clientReadWrite.RedirectURI, scopesRead, 1000, false)
require.NoError(t, err)
e := env.R(t)
res := e.POST("/oauth2/authorize").
WithFormField("client_id", clientReadWrite.ID).
WithFormField("response_type", "code").
WithFormField("prompt", "none").
WithFormField("scope", "").
WithCookie(session.CookieName, env.S(t, user.GetID())).
Expect()
res.Status(http.StatusFound)
loc, err := res.Raw().Location()
if assert.NoError(err) {
assert.Equal(errConsentRequired, loc.Query().Get("error"))
}
})Successful exploitation of CVE-2026-63198 grants an attacker's OAuth 2.0 client unauthorized access to all permissions defined in the client's registration profile (req.ValidScopes) on behalf of the victim user. Depending on the scopes configured on the OAuth client, this enables unauthorized reading of private channels and direct messages, posting messages impersonating the victim, modifying user profile settings, or managing bot and webhook integrations.
The vulnerability is assigned a CVSS v3.1 base score of 7.3 (High) with the vector CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N. The Attack Vector is Network (AV:N) and Attack Complexity is Low (AC:L) because the flaw is triggered deterministically via standard HTTP requests. Privileges Required is Low (PR:L) and User Interaction is Required (UI:R), reflecting the requirement for a registered OAuth client with pre-existing partial consent and an authenticated user triggering the silent authorization flow.
Both Confidentiality (C:H) and Integrity (I:H) impacts are rated High because an escalated token allows full read and write access across the user's account resources within the maximum scope boundary of the client. Availability Impact is None (A:N) as the flaw does not allow crashing the server or exhausting system resources.
From a threat intelligence perspective, the Exploit Prediction Scoring System (EPSS) assigns CVE-2026-63198 a score of 0.00256 (0.26%), placing it in the 17.25th percentile. The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and no standalone public exploit tools outside of the unit test in the vendor repository have been observed.
Administrators operating traQ instances must upgrade to traQ version 3.28.3 or later, which incorporates commit 5510fe8aa9bb15fc4755c78792381dbb03d576c3. This update ensures that empty scope parameters in prompt=none requests are expanded to req.ValidScopes before comparing them against the user's previously granted tokens.
Upgrading the application binary only prevents future unauthorized token minting; it does not automatically invalidate access tokens that were issued prior to applying the patch. Consequently, administrators should audit the oauth2_tokens database table to identify active tokens whose granted scopes exceed the expected permissions of third-party clients, or perform a bulk revocation of active OAuth 2.0 tokens for non-first-party clients.
For environments where an immediate upgrade to v3.28.3 cannot be performed, operators can deploy a temporary workaround at the reverse proxy or Web Application Firewall (WAF) layer (such as Nginx or Caddy). Specifically, blocking or rejecting HTTP GET and POST requests to /oauth2/authorize that contain prompt=none without a non-empty scope parameter prevents the vulnerable code path from being reached.
Security teams can also audit historical HTTP access logs and reverse proxy logs for indicators of past exploitation. Specifically, requests to /oauth2/authorize containing prompt=none alongside an empty or absent scope parameter that resulted in a 302 Found redirect containing code= (rather than error=consent_required) indicate that silent scope expansion occurred.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
traQ traPtitech | < 3.28.3 | 3.28.3 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-63198 (GHSA-8cfq-jgf7-8m27) |
| Weakness Class | CWE-863: Incorrect Authorization |
| CVSS v3.1 Score | 7.3 High (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N) |
| Affected Component | router/oauth2/authorization_endpoint.go (AuthorizationEndpointHandler) |
| Attack Vector | Network (OAuth 2.0 Silent Authorization via prompt=none and empty scope) |
| Security Impact | Privilege escalation to all scopes registered to the OAuth 2.0 client |
| EPSS Score | 0.26% (17.25th Percentile) |
| Patched Version | traQ v3.28.3 |
The application performs authorization checks when an actor attempts to access a resource or perform an action, but does not correctly evaluate whether the actor possesses the permissions required for that action.
CVE-2026-63475 (GHSA-96wc-2m84-2cj7) is an uncontrolled recursion and infinite loop vulnerability in isomorphic-git prior to version 1.38.6. When processing Git protocol responses from a malicious or man-in-the-middle (MitM) server, isomorphic-git writes server-advertised symbolic reference (symref) capabilities directly to the local repository filesystem without validating for circular dependencies. Because the reference resolution logic in GitRefManager.resolve() lacks both cycle detection and a default recursion depth limit, any subsequent operation that resolves the circular references enters a non-terminating asynchronous recursion loop. Because the malicious references are persisted on disk, this condition induces a persistent denial of service (DoS) against the affected repository.
CVE-2026-63470 (GHSA-h667-4jj2-cgv4) is a code injection vulnerability (CWE-94) in fastapi-code-generator prior to version 0.8.1. The tool generates FastAPI route handlers by directly interpolating the OpenAPI Operation.summary field into triple-double-quoted Python docstrings without escaping embedded triple quotes or backslashes. An attacker who supplies or modifies an OpenAPI specification can break out of the docstring literal and inject arbitrary Python expressions into the generated route handler body, resulting in remote code execution when the affected HTTP endpoint is called.
CVE-2026-16454 is an object-level authorization bypass vulnerability (CWE-284, CWE-862) in Eclipse hawkBit versions 1.0.3 and prior. The flaw exists within the Direct Device Integration (DDI) REST controller (hawkbit-ddi-resource), where endpoints responsible for enumerating software module metadata and serving firmware binaries fail to verify whether the requested software module is assigned to the authenticated device. Consequently, any device with valid credentials (such as a TargetToken or GatewayToken) can enumerate software module identifiers and download arbitrary firmware artifacts across the entire tenant namespace.
CVE-2026-62284 (GHSA-5w5g-j6hw-6p8f) is a code injection and server-side request forgery vulnerability in the console-proxy component of the OSAC Operator. The RemoteConfigResolver parses Kubernetes Secrets containing kubeconfig files via clientcmd.RESTConfigFromKubeConfig without sanitizing execution-related fields. An attacker with RBAC permissions to create or modify labeled Secrets in monitored namespaces can supply a malicious exec credential plugin, resulting in arbitrary command execution within the console-proxy pod.
CVE-2026-62361 (GHSA-xgjr-7j9q-2h4r) is an authenticated SQL injection and authorization bypass vulnerability in listmonk, an open-source, self-hosted newsletter and mailing list manager written in Go. In versions prior to 6.2.0, the subscriber export endpoint (GET /api/subscribers/export) directly interpolates user-supplied SQL filter expressions into a PostgreSQL query without invoking the table allowlist validation function (validateQueryTables). Consequently, authenticated users possessing the subscribers:sql_query and subscribers:get_all permissions can bypass relational access controls, execute arbitrary subqueries against restricted tables such as users and settings, and modify database state via writable Common Table Expressions (CTEs).
CVE-2026-28385 (GHSA-3gq2-x4qg-p4g6) is a Server-Side Request Forgery (SSRF) vulnerability in Canonical LXD versions 4.12 through 6.9. An authenticated user possessing the can_create_images entitlement can force the LXD daemon to execute arbitrary outbound HTTP and HTTPS requests via the /1.0/images API endpoint. Due to the absence of egress destination validation, an attacker can probe host loopback services, RFC 1918 private network segments, and cloud Instance Metadata Services (IMDS) from the network position of the privileged LXD daemon.