CSIRTS // UNIFIED SECURITY ADVISORY FEEDSYS ● ONLINE · POWERED BY INTELFUSIONS.COM

GHSA-89mr-xqfv-758m: Gogs: UploadRepoFiles writes outside repo working tree via committed parent sym

criticalCVE-2026-52811
Summary (*Repository).UploadRepoFiles checks for symlinks only on the leaf of the upload target (osx.IsSymlink(targetPath)). The siblings UpdateRepoFile, DeleteRepoFile, and GetDiffPreview use hasSymlinkInPath, which lstats every component — UploadRepoFiles is the lone outlier. An attacker with repo-write access plus a multipart upload whose filename contains a literal backslash (preserved by filepath.Base on Linux, then converted to / by pathx.Clean) redirects the write through a previously-committed directory symlink. iox.CopyFile opens the destination with os.Create (no O_NOFOLLOW), so the kernel follows the parent symlink and writes attacker bytes anywhere the gogs UID can write — ~git/.ssh/authorized_keys → SSH foothold, or <repo>.git/hooks/post-receive → next-push RCE. Windows builds are unaffected: filepath.Base treats \ as a separator (strips the multi-segment trick) and git defaults core.symlinks=false at checkout (committed mode-120000 entries become text files, not real symlinks). Details The asymmetric check at internal/database/repo_editor.go:601-612: targetPath := path.Join(dirPath, upload.Name) if osx.IsSymlink(targetPath) { // ← LEAF-ONLY return errors.Newf("cannot overwrite symbolic link: %s", upload.Name) } if err = iox.CopyFile(tmpPath, targetPath); err != nil { ... } vs. UpdateRepoFile's correct walker at internal/database/repo_editor.go:163: if hasSymlinkInPath(localPath, opts.OldTreeName) || hasSymlinkInPath(localPath, opts.NewTreeName) { return errors.New("cannot update file with symbolic link in path") } hasSymlinkInPath (internal/database/repo_editor.go:120-131) lstats every component; osx.IsSymlink (internal/osx/osx.go:35-41) is os.Lstat mode-bit on the leaf — fine inside the loop, wrong as a single call. Multi-segment upload.Name reaches the loop because: (1) c.Req.FormFile("file") returns *multipart.FileHeader whose Filename is filepath.Base(filename) — Linux only treats / as separator, so backslashes are preserved; (2) NewUpload

Details

Source
GitHub Security Advisories (INTL · database · site)
Severity
critical
Published
2026-06-23
Last updated
2026-07-21
Exploitation
Not in CISA KEV at last sync

Original advisory: https://github.com/advisories/GHSA-89mr-xqfv-758m

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.

Referenced CVEs

CVECSIRTS overviewExternal
CVE-2026-52811coverage & exploitation statusNVD · CVE.org

More from GitHub Security Advisories