Gogs Authenticated RCE via Argument Injection in git rebase --exec (Unpatched, CVSSv4 9.4, GHSA-qf6p-p7ww-cwr9) — Threadlinqs Intelligence
As of 2026-05-30, Gogs Authenticated RCE via Argument Injection in git rebase --exec (Unpatched, CVSSv4 9.4, GHSA-qf6p-p7ww-cwr9) is a critical-severity vulnerability threat, tracked by Threadlinqs Intelligence with 9 detection rules (Splunk SPL, Microsoft KQL, Sigma) and 18 indicators of compromise.
Threat ID: TL-2026-0615 · Severity: CRITICAL · CVSS: 9.4 · Status: ACTIVE · Category: VULNERABILITY
Rapid7 Labs disclosed an unpatched CWE-88 argument injection (CVSSv4 9.4) in Gogs self-hosted Git service. An authenticated user crafts a malicious branch name beginning with --exec= that is passed
Overview
Rapid7 Labs senior security researcher Jonah Burgess (CryptoCat) disclosed an unpatched argument injection vulnerability in Gogs, a Go-based self-hosted Git service with roughly 50,000 GitHub stars and over 5,000 forks. The vulnerability — tracked as GHSA-qf6p-p7ww-cwr9 and rated CVSSv4 9.4 (Critical) — allows any authenticated user to achieve remote code execution (RCE) by submitting a pull request with a malicious base branch name that injects the --exec flag into the underlying git rebase invocation. The latest release at the time of research, Gogs 0.14.2, and the development tip 0.15.0+dev (commit b53d3162) are both confirmed vulnerable; all prior versions supporting the 'Rebase before merging' merge style are likely affected. At the time of public disclosure (May 28, 2026) no vendor patch exists, despite responsible disclosure on March 17, 2026 and multiple follow-ups through May 2026.
Root cause
The Merge() function in internal/database/pull.go executes the rebase merge style by directly invoking git rebase via process.ExecDir (a thin wrapper over exec.Command), passing the pull request's base branch as a positional argument WITHOUT the POSIX `--` end-of-options separator:
process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
"git", "rebase", "--quiet", pr.BaseBranch, remoteHeadBranch)
pr.BaseBranch is taken from the user-controlled URL parameter in internal/route/repo/pull.go (parsed from the `*` catch-all via strings.Split(c.Params("*"), "...")). Both base and head refs are validated through RevParse (which calls git rev-parse --verify <ref>) before the PR is created; RevParse rejects refs that do not resolve to a valid Git object but does NOT sanitize against argument injection. Because the attacker first pushes a real branch with the malicious name, RevParse succeeds, the value is persisted, and is later passed verbatim to git rebase. Git's argument parser then interprets the leading `--exec=...` token as the --exec flag rather than a branch name, and git rebase executes the supplied value through `sh -c` after each replayed commit.
Exploit primitive
Git branch names may legally contain `$`, `{`, `}`, `=`, and `-`. The attacker creates a branch named e.g.:
--exec=touch${IFS}/tmp/rce_proof
When used as pr.BaseBranch the resulting command becomes:
git rebase --quiet '--exec=touch${IFS}/tmp/rce_proof' 'head_repo/feature'
`${IFS}` is expanded by the shell to a space at sh -c execution time, sidestepping Git's prohibition on literal spaces in branch names. For payloads requiring characters illegal in Git refs (`:`, `~`, `^`, `?`, `*`, `[`, `\`, `//`), the attacker base64-encodes the command and decodes inline:
--exec=echo${IFS}<b64>|base64${IFS}-d|sh
On Windows, the `|` character is forbidden in NTFS filenames (Git stores refs as files under refs/heads/), so the public Metasploit module switches to file-based delivery: the attacker commits a small script (e.g. `.abcdef`) and `.abcdef.bat` to the repository and uses the short branch name `--exec=sh${IFS}.abcdef`. To bypass MSYS2 sh's mangling of `$`, `&`, and backticks in the payload, the script invokes `cmd.exe //c .abcdef.bat` so the PowerShell payload runs natively without shell metacharacter interpretation.
Execution flow during Merge()
The MergeStyleRebase code path runs these git commands sequentially in a temp directory:
1. git clone -b '<malicious>' <repo> <tmp> -> succeeds; `-b` consumes --exec=... as the branch value
2. git remote add head_repo <repo> && git fetch head_repo -> succeeds
3. git rebase --quiet '<malicious>' 'head_repo/feature' -> RCE fires here; --exec=<cmd> parsed as flag, command runs via sh -c
4. git checkout -b <tmpBranch> -> succeeds (server-generated timestamp branch)
5. git checkout '<malicious>' -> fails: git interprets --exec=... as an invalid option for checkout
Step
Weaknesses (CWE)
CWE-88, CWE-77, CWE-78, CWE-94
Target sectors: technology, software-development, education, open-source, financial, government, research
Target regions: Global, North America, Europe, Asia-Pacific
Detections & IOCs
As of 2026-07-28, this threat has 9 detection rule(s) across Splunk SPL, Microsoft KQL and Sigma, and 18 indicator(s) of compromise. Detection query text and full IOC values are available to authenticated users and programmatically via the Threadlinqs MCP server (Purple tier). View plans.
VULNERABILITY, CRITICAL, threat intelligence, cybersecurity, T1595, T1592, T1593, T1583, T1587, T1588, T1588, T1585, T1190, T1078