GHSA-CV22-72PX-F4GH

Vulnerability from github – Published: 2026-02-17 18:42 – Updated: 2026-02-19 21:14
VLAI?
Summary
Gogs has an Authorization Bypass Allows Cross-Repository Label Modification in Gogs
Details

Summary

A broken access control vulnerability in Gogs allows authenticated users with write access to any repository to modify labels belonging to other repositories. The UpdateLabel function in the Web UI (internal/route/repo/issue.go) fails to verify that the label being modified belongs to the repository specified in the URL path, enabling cross-repository label tampering attacks.

Details

The vulnerability exists in the Web UI's label update endpoint POST /:username/:reponame/labels/edit. The handler function UpdateLabel uses an incorrect database query function that bypasses repository ownership validation:

Vulnerable Code (internal/route/repo/issue.go:1040-1054):

func UpdateLabel(c *context.Context, f form.CreateLabel) {
    l, err := database.GetLabelByID(f.ID)  // ❌ No repository validation
    if err != nil {
        c.NotFoundOrError(err, "get label by ID")
        return
    }

    // ❌ Missing validation: l.RepoID != c.Repo.Repository.ID
    l.Name = f.Title
    l.Color = f.Color
    if err := database.UpdateLabel(l); err != nil {
        c.Error(err, "update label")
        return
    }
    c.RawRedirect(c.Repo.MakeURL("labels"))
}

Root Cause:

  1. The function calls database.GetLabelByID(f.ID) which internally passes repoID=0 to the ORM layer
  2. According to code comments in internal/database/issue_label.go:147-166, passing repoID=0 causes the ORM to ignore repository restrictions
  3. No validation checks whether l.RepoID == c.Repo.Repository.ID before updating
  4. The middleware reqRepoWriter() only validates write access to the repository in the URL path, not the label's actual repository

Inconsistency with Other Functions:

  • NewLabel: Correctly sets RepoID = c.Repo.Repository.ID
  • DeleteLabel: Correctly uses database.DeleteLabel(c.Repo.Repository.ID, id)
  • API EditLabel: Correctly uses database.GetLabelOfRepoByID(c.Repo.Repository.ID, id)

  • *Only UpdateLabel in Web UI uses the vulnerable pattern*

PoC

Prerequisites:

  • Two user accounts: Alice (attacker) and Bob (victim)
  • alice has written access to repo-a
  • Bob owns repo-b with labels

Step 1: Identify Target Label ID

  1. Login as bob, navigate to bob/repo-b/labels
  2. Open browser DevTools (F12) → Network tab
  3. Click edit on any label
  4. Observe the form data: id=
  5. Example: id=1

Step 2: Execute Attack

# Login as alice, get session cookie
# Open DevTools → Application → Cookies → i_like_gogs
# Copy the cookie value

# Send malicious request
curl -X POST "http://localhost:3000/alice/repo-a/labels/edit" \
  -H "Cookie: i_like_gogs=<ALICE_SESSION_COOKIE>" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "id=1&title=HACKED-BY-ALICE&color=%23000000"

# Expected response: 302 Found (redirect)

Step 3: Verify Impact

  1. Login as bob
  2. Navigate to bob/repo-b/labels
  3. Observe: Label "P0-Critical" is now "HACKED-BY-ALICE" with black color

Impact

  1. Issue Classification Disruption: Modify critical labels (e.g., "P0-Critical" → "P3-Low") causing urgent issues to be deprioritized

  2. Security Issue Concealment: Change "security" labels to "documentation" to hide vulnerability reports from security teams

  3. Workflow Sabotage: Alter labels used in CI/CD automation, breaking deployment pipelines

  4. Mass Disruption: Batch modifies all labels across multiple repositories using ID enumeration

Recommended Fix:

func UpdateLabel(c *context.Context, f form.CreateLabel) {
    l, err := database.GetLabelOfRepoByID(c.Repo.Repository.ID, f.ID)
    if err != nil {
        c.NotFoundOrError(err, "get label of repository by ID")
        return
    }
    // Now label ownership is validated at database layer
    l.Name = f.Title
    l.Color = f.Color
    if err := database.UpdateLabel(l); err != nil {
        c.Error(err, "update label")
        return
    }
    c.RawRedirect(c.Repo.MakeURL("labels"))
}
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.13.4"
      },
      "package": {
        "ecosystem": "Go",
        "name": "gogs.io/gogs"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.14.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-25229"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-284"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-02-17T18:42:08Z",
    "nvd_published_at": "2026-02-19T07:17:45Z",
    "severity": "MODERATE"
  },
  "details": "### **Summary**\nA broken access control vulnerability in Gogs allows authenticated users with write access to any repository to modify labels belonging to other repositories. The `UpdateLabel` function in the Web UI (`internal/route/repo/issue.go`) fails to verify that the label being modified belongs to the repository specified in the URL path, enabling cross-repository label tampering attacks.\n\n### **Details**\nThe vulnerability exists in the Web UI\u0027s label update endpoint `POST /:username/:reponame/labels/edit`. The handler function `UpdateLabel` uses an incorrect database query function that bypasses repository ownership validation:\n\n**Vulnerable Code** (`internal/route/repo/issue.go:1040-1054`):\n\n```plain\nfunc UpdateLabel(c *context.Context, f form.CreateLabel) {\n    l, err := database.GetLabelByID(f.ID)  // \u274c No repository validation\n    if err != nil {\n        c.NotFoundOrError(err, \"get label by ID\")\n        return\n    }\n\n    // \u274c Missing validation: l.RepoID != c.Repo.Repository.ID\n    l.Name = f.Title\n    l.Color = f.Color\n    if err := database.UpdateLabel(l); err != nil {\n        c.Error(err, \"update label\")\n        return\n    }\n    c.RawRedirect(c.Repo.MakeURL(\"labels\"))\n}\n```\n\n**Root Cause**:\n\n1. The function calls `database.GetLabelByID(f.ID)` which internally passes `repoID=0` to the ORM layer\n2. According to code comments in `internal/database/issue_label.go:147-166`, passing `repoID=0` causes the ORM to ignore repository restrictions\n3. No validation checks whether `l.RepoID == c.Repo.Repository.ID` before updating\n4. The middleware `reqRepoWriter()` only validates write access to the repository in the URL path, not the label\u0027s actual repository\n\n**Inconsistency with Other Functions**:\n\n+ `NewLabel`: Correctly sets `RepoID = c.Repo.Repository.ID`\n+ `DeleteLabel`: Correctly uses `database.DeleteLabel(c.Repo.Repository.ID, id)`\n+ API `EditLabel`: Correctly uses `database.GetLabelOfRepoByID(c.Repo.Repository.ID, id)`\n\n- ****Only `UpdateLabel` in ****Web UI**** uses the vulnerable pattern****\n\n### **PoC**\n**Prerequisites**:\n\n+ Two user accounts: Alice (attacker) and Bob (victim)\n+ alice has written access to repo-a\n+ Bob owns repo-b with labels\n\n**Step 1: Identify Target Label ID**\n\n1. Login as bob, navigate to bob/repo-b/labels\n2. Open browser DevTools (F12) \u2192 Network tab\n3. Click edit on any label\n4. Observe the form data: id=\u003cLABEL_ID\u003e\n5. Example: id=1\n\n**Step 2: Execute Attack**\n\n```plain\n# Login as alice, get session cookie\n# Open DevTools \u2192 Application \u2192 Cookies \u2192 i_like_gogs\n# Copy the cookie value\n\n# Send malicious request\ncurl -X POST \"http://localhost:3000/alice/repo-a/labels/edit\" \\\n  -H \"Cookie: i_like_gogs=\u003cALICE_SESSION_COOKIE\u003e\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"id=1\u0026title=HACKED-BY-ALICE\u0026color=%23000000\"\n\n# Expected response: 302 Found (redirect)\n```\n\n**Step 3: Verify Impact**\n\n1. Login as bob\n2. Navigate to bob/repo-b/labels\n3. Observe: Label \"P0-Critical\" is now \"HACKED-BY-ALICE\" with black color\n\n### **Impact**\n1. **Issue Classification Disruption**: Modify critical labels (e.g., \"P0-Critical\" \u2192 \"P3-Low\") causing urgent issues to be deprioritized\n\n2. **Security Issue Concealment**: Change \"security\" labels to \"documentation\" to hide vulnerability reports from security teams\n\n3. **Workflow**** Sabotage**: Alter labels used in CI/CD automation, breaking deployment pipelines\n\n4. **Mass Disruption**: Batch modifies all labels across multiple repositories using ID enumeration\n\n**Recommended Fix**:\n\n```plain\nfunc UpdateLabel(c *context.Context, f form.CreateLabel) {\n    l, err := database.GetLabelOfRepoByID(c.Repo.Repository.ID, f.ID)\n    if err != nil {\n        c.NotFoundOrError(err, \"get label of repository by ID\")\n        return\n    }\n    // Now label ownership is validated at database layer\n    l.Name = f.Title\n    l.Color = f.Color\n    if err := database.UpdateLabel(l); err != nil {\n        c.Error(err, \"update label\")\n        return\n    }\n    c.RawRedirect(c.Repo.MakeURL(\"labels\"))\n}\n```",
  "id": "GHSA-cv22-72px-f4gh",
  "modified": "2026-02-19T21:14:43Z",
  "published": "2026-02-17T18:42:08Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/gogs/gogs/security/advisories/GHSA-cv22-72px-f4gh"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-25229"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gogs/gogs/commit/643a6d6353cb6a182a4e1f0720228727f30a3ad2"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/gogs/gogs"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Gogs has an Authorization Bypass Allows Cross-Repository Label Modification in Gogs"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…