GHSA-6p9m-q3jp-47h4: Gogs: LFS dedupe path leaks private repo content across tenants
Summary
Git LFS storage is content-addressed by OID alone (<LFS-root>/<oid[0]>/<oid[1]>/<oid>) but per-repo authorization lives in the lfs_object table keyed (repo_id, oid). serveUpload skips re-uploading when the OID file already exists on disk and inserts a new (repo_id, oid) row pointing at it without verifying the request body hashes to the OID being claimed. Any user with write access to one repo can bind their repo to an OID owned by a private repo and download the original bytes via their own download endpoint.
Details
Dedupe shortcut at internal/lfsx/storage.go:79-82:
if fi, err := os.Stat(fpath); err == nil {
_, _ = io.Copy(io.Discard, rc)
return fi.Size(), nil // ← returns success with no hash check
}
Hash verification at internal/lfsx/storage.go:106-108 only runs in the *new-file* branch — the dedupe path returns earlier.
serveUpload (internal/route/lfs/basic.go:78-114) trusts that success and inserts the per-repo binding:
_, err := h.store.GetLFSObjectByOID(c.Req.Context(), repo.ID, oid) // per-repo
if err == nil { /* already linked, drain & return 200 */ }
written, err := s.Upload(oid, c.Req.Request.Body)
err = h.store.CreateLFSObject(c.Req.Context(), repo.ID, oid, written, s.Storage())
CreateLFSObject is an unconditional INSERT on (repo_id, oid) with no check that the OID is referenced by the requesting repo's git history.
serveDownload at internal/route/lfs/basic.go:42-72 only consults the per-repo row, then streams from the shared content-addressed file.
Suggested fix
1. In LocalStorage.Upload, when os.Stat(fpath) == nil, hash the request body via io.TeeReader and ErrOIDMismatch on disagreement — same code path as the new-file branch already uses. The "client retries after partial failure" use case still works; the retry just has to send the correct content.
2. Optional second layer: in serveUpload, refuse CreateLFSObject unless the OID is referenced by an LFS pointer in the requesting repo's refs.
PoC
Tested against gogs at HEAD d757132
Details
Original advisory: https://github.com/advisories/GHSA-6p9m-q3jp-47h4
Exploitation outlook
EPSS (FIRST.org) estimates each CVE’s probability of exploitation in the next 30 days — here is the CSIRTS.com read on those numbers.
- Low exploitation riskCVE-2026-528120.22% 30-day exploitation probability — currently an unlikely target, but scores change as exploit code circulates. Riskier than 12% of all scored CVEs.
Referenced CVEs
| CVE | CSIRTS overview | External |
|---|---|---|
| CVE-2026-52812 | coverage & exploitation status | NVD · CVE.org |
More from GitHub Security Advisories
- mediumGHSA-xm43-3m56-w3wf: Ghost: Paid gift memberships obtainable at minimal cost via the donations feature2026-08-04
- mediumGHSA-chgm-3698-jm42: Ghost: Member existence leak via magic link sign-in response2026-08-04
- highGHSA-xpp7-93x6-v29m: XSS in Ghost's ActivityPub client2026-08-04
- mediumGHSA-7mpp-r37j-x5wh: Ghost: Session Fixation in Ghost Admin2026-08-04
- mediumGHSA-cjc9-q5gf-327p: Ghost: Theme Upload Path Traversal2026-08-04