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…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

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…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…