{"id":"CVE-2026-52800","title":"Gogs Vulnerable to CSRF Leading to Organization Owner Takeover","summary":"Gogs Vulnerable to CSRF Leading to Organization Owner Takeover","severity":"high","cvss":8.8,"cwe":["CWE-352"],"vendor":"gogs","product":"gogs.io/gogs","ecosystem":"go","affected":["gogs.io/gogs < 0.14.3"],"patched":["gogs.io/gogs 0.14.3"],"published":"2026-06-23","updated":"2026-06-23","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-pwx3-qcgw-vh7h","references":[{"url":"https://github.com/gogs/gogs/security/advisories/GHSA-pwx3-qcgw-vh7h"},{"url":"https://github.com/gogs/gogs/pull/8321"},{"url":"https://github.com/gogs/gogs/commit/070df61ecd14c75b0aca93090f860b87ab17ac19"},{"url":"https://github.com/gogs/gogs/releases/tag/v0.14.3"},{"url":"https://github.com/advisories/GHSA-pwx3-qcgw-vh7h"}],"tags":["ghsa","go"],"epss":0.00248,"epssPercentile":0.1643,"ingestedAt":"2026-06-29T13:24:35.480Z","slug":"CVE-2026-52800","body":"## Overview\n\n## Summary\n\nIn **Gogs 0.14.1**, organization team member management can be performed via **GET requests without CSRF protection**.\nIf a victim who is an **organization owner** is logged in and is tricked into visiting a crafted link, an attacker-controlled user can be added to the **Owners** team. As a result, the attacker gains **organization owner–equivalent privileges**.\n\n---\n\n## Description\n\nWhen a victim is logged in as an organization owner, **team member management endpoints are exposed via routes reachable by GET requests**, allowing state-changing operations without a CSRF token.\n\n### Team action route allows GET\n\n`internal/cmd/web.go:390`\n\n```go\nm.Route(\"/teams/:team/action/:action\", \"GET,POST\", org.TeamsAction)\n```\n\n### CSRF validation is applied only to POST requests\n\nBecause the global CSRF check is limited to POST requests, state-changing operations reached via GET bypass CSRF protection entirely.\n\n`internal/context/auth.go:56-61`\n\n```go\nif !options.SignOutRequired && !options.DisableCSRF &&\n   c.Req.Method == \"POST\" && !isAPIPath(c.Req.URL.Path) {\n    csrf.Validate(c.Context, c.csrf)\n    if c.Written() {\n        return\n    }\n}\n```\n\n### TeamsAction performs state changes regardless of HTTP method\n\n`TeamsAction` does not branch on the HTTP method. Instead, it performs state-changing operations (such as adding or removing members) based solely on query parameters (`uid`, `uname`) and the `:action` path parameter.\nSince the route explicitly allows GET, the `add` action can be executed via GET.\n\n`internal/route/org/teams.go:38-83`\n\n```go\nfunc TeamsAction(c *context.Context) {\n    uid := com.StrTo(c.Query(\"uid\")).MustInt64()\n    if uid == 0 {\n        c.Redirect(c.Org.OrgLink + \"/teams\")\n        return\n    }\n\n    page := c.Query(\"page\")\n    var err error\n    switch c.Params(\":action\") {\n    case \"add\":\n        if !c.Org.IsOwner {\n            c.NotFound()\n            return\n        }\n        uname := c.Query(\"uname\")\n        var u *database.User\n        u, err = database.Handle.Users().GetByUsername(c.Req.Context(), uname)\n        // ...\n        err = c.Org.Team.AddMember(u.ID)\n        page = \"team\"\n    }\n}\n```\n\n### Adding a user to the Owners team grants organization owner privileges\n\nWhen a user joins the **Owners** team, `OrgUser.IsOwner` is set to `true`. Therefore, adding a user to the Owners team directly results in granting organization owner–equivalent privileges.\n\n`internal/database/org_team.go:566-576`\n\n```go\nou := new(OrgUser)\nif _, err = sess.Where(\"uid = ?\", userID).\n    And(\"org_id = ?\", orgID).Get(ou); err != nil {\n    return err\n}\nou.NumTeams++\nif t.IsOwnerTeam() {\n    ou.IsOwner = true\n}\nif _, err = sess.ID(ou.ID).AllCols().Update(ou); err != nil {\n    return err\n}\n```\n\n### Related issue: organization member actions are also state-changing via GET\n\nFor reference, organization member management endpoints are also exposed as GET routes that perform state changes without CSRF protection.\n\n`internal/cmd/web.go:382`\n\n```go\nm.Get(\"/members/action/:action\", org.MembersAction)\n```\n\n`MembersAction` similarly does not branch on HTTP method and performs state-changing operations (public/private toggle, remove, leave) based on query parameters and the `:action` path parameter.\n\n`internal/route/org/members.go:31-71`\n\n```go\nfunc MembersAction(c *context.Context) {\n    uid := com.StrTo(c.Query(\"uid\")).MustInt64()\n    if uid == 0 {\n        c.Redirect(c.Org.OrgLink + \"/members\")\n        return\n    }\n\n    org := c.Org.Organization\n    var err error\n    switch c.Params(\":action\") {\n    case \"private\":\n        err = database.ChangeOrgUserStatus(org.ID, uid, false)\n    case \"public\":\n        err = database.ChangeOrgUserStatus(org.ID, uid, true)\n    case \"remove\":\n        err = org.RemoveMember(uid)\n    case \"leave\":\n        err = org.RemoveMember(c.User.ID)\n    }\n}\n```\n\n---\n\n## Steps to Reproduce\n\n1. Prepare a target user account to be added (e.g., `attacker`).\n\n2. Confirm that the victim user is an **owner** of the target organization (e.g., `org3`) and is logged in.\n\n3. Cause the victim’s browser to perform a **top-level navigation** to the following URL:\n\n   ```\n   http://localhost:10880/org/org3/teams/owners/action/add?uid=1&uname=attacker\n   ```\n<img width=\"2019\" height=\"322\" alt=\"image\" src=\"https://github.com/user-attachments/assets/342a627a-04e8-47bd-818a-9c2b05a75446\" />\n\n\n4. After the request completes, verify that the `attacker` user can access:\n\n   ```\n   http://localhost:10880/org/org3/settings\n   ```\n\n   confirming that organization owner privileges have been obtained.\n\n<img width=\"2010\" height=\"285\" alt=\"image\" src=\"https://github.com/user-attachments/assets/03945bb1-e9c5-4e42-ad3a-9f6d63b7d86d\" />\n\n\n<img width=\"2016\" height=\"893\" alt=\"image\" src=\"https://github.com/user-attachments/assets/55d7db13-52cf-471b-a6d3-aa4186c8b547\" />\n\n\n\n\n---\n\n## Impact\n\nSuccessful exploitation allows an attacker to obtain **organization owner privileges**, resulting in:\n\n* Full control over organization repositories, settings, and members\n* Unauthorized access to private repositories (confidentiality impact)\n* Modification or deletion of repositories and settings (integrity impact)\n* Repository deletion or disruption leading to service unavailability (availability impact)\n\n## Affected packages\n\n- `gogs.io/gogs < 0.14.3`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `gogs.io/gogs 0.14.3`","depth":"twilight","depthScore":48,"depthScoreParts":{"impact":48.4,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}