Skip to content
CVE-2026-104627

CVE-2026-104627

github.com • October 18, 2026

Gitea accepts a valid OpenID 2.0 assertion at the authenticated security-settings callback without verifying that the current browser session initiated an OpenID-linking transaction. The verified OpenID identity is attached to whichever user is currently authenticated as ctx.Doer .

Because OpenID 2.0 permits unsolicited positive assertions, an attacker-controlled external OpenID provider can create this callback without first starting a linking flow in the victim's session. After the attacker identity is linked, a second assertion to Gitea's OpenID login callback signs the attacker in as the victim. This path also bypasses the local TOTP/WebAuthn checks used by password sign-in.

2. Affected Assets & Attack Surface

Primary affected operations:

GET /user/settings/security when openid.return_to is present.

GET /user/login/openid when processing a positive assertion.

The user_open_id credential mapping table.

Local accounts, including accounts protected by TOTP or WebAuthn.

routers/web/user/setting/security/security.go:27-44 Security

routers/web/user/setting/security/openid.go:21-110 OpenIDPost settingsOpenIDVerify

modules/auth/openid/openid.go:25-54 Verify RedirectURL

models/user/openid.go:35-80 AddUserOpenID

routers/web/auth/openid.go:123-158 signInOpenIDVerify

routers/web/auth/auth.go:310-415 password MFA checks handleSignIn handleSignInFull

modules/setting/service.go:268-286 OpenID feature defaults

OpenID feature defaults

modules/setting/session.go:34-66 default SameSite=Lax session cookie

default SameSite=Lax session cookie

OpenID sign-in is enabled. At the validated revision, a fresh installation initializes it as enabled before INSTALL_LOCK is set and persists that installer value unless the operator changes it.

The victim is signed in and can manage credentials.

The attacker controls a publicly reachable OpenID 2.0 provider and an unused claimed identifier.

The victim follows or otherwise navigates to the crafted callback.

3. Technical Root Cause Analysis

The normal linking flow sends the browser to an OpenID provider with a fixed return URL:

The return URL contains no unguessable transaction reference. Gitea also creates no server-side pending-link record containing the initiating session, user ID, expected identity, operation, or expiration.

The vulnerable call chain is:

Security runs under reqSignIn , proving only that a Gitea user is currently authenticated.

If openid.return_to is present, it calls settingsOpenIDVerify .

settingsOpenIDVerify reconstructs the current URL and calls openid.Verify(fullURL) .

yohcop/[email protected] verifies signed fields, provider signature, return URL, discovery information, and nonce freshness.

The verifier deliberately supports discovery after receiving an unsolicited positive assertion. It does not require a browser-session initiation record.

Gitea inserts UserOpenID{UID: ctx.Doer.ID, URI: id} .

A later assertion for that identity reaches signInOpenIDVerify , which calls GetUserByOpenID and then handleSignIn .

The password path's TOTP/WebAuthn branch is not invoked. handleSignInFull creates a victim session directly.

Provider authentication is therefore incorrectly treated as authorization to modify the current local account. The global OpenID nonce prevents replay of one assertion but does not bind the assertion to an initiating user or session.

Additional weaknesses:

Credential insertion occurs from a GET callback.

No recent-password or MFA confirmation is required to add a login method.

OpenID URI allowlist/blacklist checks used by the interactive sign-in POST are not reapplied to unsolicited settings and login callbacks.

Successful exploitation grants the attacker a fully authenticated session as the victim without knowing the victim's password or completing their local MFA.

Potential consequences include:

Read and write access to the victim's private repositories.

Organization and team access inherited from the victim.

Modification of account settings, tokens, SSH keys, and repositories within the victim's permissions.

Complete administrative compromise when the victim is an instance administrator.

Use of the compromised account to plant repository content for follow-on attacks.

The attack requires one victim but does not require an attacker Gitea account.

6. Remediation Guidance

When linking starts, create a cryptographically random, single-use server-side transaction.

Bind the transaction to: current session identifier; current user ID; normalized expected OpenID identifier; operation type ( link rather than login ); short expiration time.

current session identifier;

normalized expected OpenID identifier;

operation type ( link rather than login );

short expiration time.

Include only an opaque transaction reference in the return URL.

On callback, atomically load and consume the transaction before inserting the credential.

Reject every settings assertion lacking a matching pending transaction, even if OpenID verification succeeds.

Extracted Entities

Attack Types (1)

Domains (1)

MITRE ATT&CK (1)