{"id":"GHSA-g9g6-qhrc-p3qc","revision":"9fde21f589e64a3b8d4094d41a13a59e","records":[{"revision":"9fde21f589e64a3b8d4094d41a13a59e","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GHSA-g9g6-qhrc-p3qc","aliases":["CVE-2026-58422","GO-2026-6056"],"title":"Gitea: Improper authorization on OAuth sign-in callback silently re-enables administrator-disabled accounts","description":"### Summary\n\nThe OAuth2 sign-in callback in Gitea 1.26.1 unconditionally re-enables a locally-disabled account whenever the user authenticates through a linked external identity provider, silently undoing any administrator-initiated `Disable Account` action and issuing a fresh authenticated session in the same response. Once a user has linked any external IdP, the administrator's disable toggle becomes non-binding — the user can re-enable themselves on demand by completing one OAuth callback, regaining full read and write access to their repositories, organizations, and access tokens.\n\n### Details\n\nImproper Authorization is present on the `code` and `state` parameters of the `/user/oauth2/{source-name}/callback` endpoint in Gitea version 1.26.1. The handler `routers/web/auth/oauth.go::handleOAuth2SignIn` reads the local user's `IsActive` flag at lines 350-352 and, when it is `false`, sets `opts.IsActive = optional.Some(true)` before calling `user_service.UpdateUser` and immediately establishing a session. The active-state precondition that the request middleware `routers/web/web.go::verifyAuthWithOptions` enforces for every other request is therefore evaluated against the freshly-flipped row on the next request, so a site administrator's \"disable user\" action is silently undone the next time the user authenticates through any linked external identity provider.\n\nThe root cause is that the callback path treats the local `IsActive` flag as stale bookkeeping to reconcile against the external identity, rather than as an authoritative administrative override. The local-credential sign-in path correctly renders `IsErrUserInactive` in the same condition; only the OAuth callback path flips the flag and proceeds.\n\n### PoC\n\n1. As a site administrator, configure an OAuth2 authentication source via `Site Administration -> Authentication Sources -> Add Authentication Source`. Any external provider works (a self-hosted Keycloak, an upstream Gitea acting as OIDC, GitHub, GitLab, Microsoft, Google).\n2. Have a normal user (`alice` in the example) sign in through that source at least once. The callback creates the row in `external_login_user` linking alice's local account to the IdP identity.\n3. As the site administrator, disable alice's account: `Site Administration -> User Accounts -> alice -> \"Disable Account\"`. Confirm the flag is set with the admin API.\n\n#### HTTP REQUEST\n\n```http\nGET /api/v1/admin/users/alice HTTP/1.1\nHost: <HOST>\nAuthorization: token <ADMIN_TOKEN>\nAccept: application/json\n```\n\n#### HTTP RESPONSE\n\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json;charset=utf-8\n\n{\"id\":2,\"login\":\"alice\",\"active\":false,...}\n```\n\n4. Have alice click \"Sign in with <source-name>\" on the Gitea login page and complete the OAuth dance with the IdP. The callback handler runs, observes `IsActive=false`, sets `opts.IsActive=true`, calls `UpdateUser`, and issues a session on the same response. Alice lands on `/`.\n5. Re-read the same admin API endpoint and observe that `active` has flipped back to `true` without administrator action.\n\n#### HTTP REQUEST\n\n```http\nGET /api/v1/admin/users/alice HTTP/1.1\nHost: <HOST>\nAuthorization: token <ADMIN_TOKEN>\nAccept: application/json\n```\n\n#### HTTP RESPONSE\n\n```http\nHTTP/1.1 200 OK\nContent-Type: application/json;charset=utf-8\n\n{\"id\":2,\"login\":\"alice\",\"active\":true,...}\n```\n\n### Impact\n\nOnce a user has linked any external identity provider, the site administrator's \"disable user\" toggle becomes non-binding on that account: the user can re-enable themselves on demand by completing one OAuth callback, regaining full read and write access to their repositories, organizations, and tokens. In SSO-fronted Gitea deployments where the disable toggle is the documented incident-response action for compromised accounts or departed employees, this defeats the only short-term remediation available before the identity provider can revoke the underlying federated identity.","state":0,"publishedAt":"2026-07-21T21:48:48+00:00","modifiedAt":"2026-07-22T21:11:29.3895906+00:00","affected":[{"ecosystem":"Go","name":"code.gitea.io/gitea","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"1.26.4"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V4","vector":"CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/go-gitea/gitea/security/advisories/GHSA-g9g6-qhrc-p3qc","https://nvd.nist.gov/vuln/detail/CVE-2026-58422","https://github.com/go-gitea/gitea/pull/38009","https://github.com/go-gitea/gitea/commit/c43eb7c33a100ffc7b2367adf165f7085e0ccdc5","https://blog.gitea.com/release-of-1.26.3-and-1.26.4","https://github.com/go-gitea/gitea","https://github.com/go-gitea/gitea/releases/tag/v1.26.4"],"sourceUrl":"https://github.com/advisories/GHSA-g9g6-qhrc-p3qc","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-g9g6-qhrc-p3qc","identifiers":["GHSA-g9g6-qhrc-p3qc","CVE-2026-58422","GO-2026-6056"]},"retained":false,"historicalAffected":[]}],"coverage":[]}