CVE-2026-3854: When `git push` becomes RCE — Analyzing the GitHub Flaw
Has anyone dug into the details on CVE-2026-3854 yet? The idea that a simple authenticated git push can chain into Remote Code Execution on the server-side is nightmare fuel for anyone managing a GitHub Enterprise instance.
The Breakdown
- CVE: CVE-2026-3854
- CVSS: 8.7 (High)
- Vector: Command Injection via Git Push
- Affected: GitHub.com & GitHub Enterprise Server
The researcher disclosure highlights that this is a command injection flaw. The scariest part is the low barrier to entry: if an attacker has push access, they can potentially execute commands as the service user. It's not just about compromising the code in the repo anymore; it's about compromising the infrastructure hosting it.
For those running Enterprise Server, you need to audit write permissions immediately. If you have external contractors or third-party bots with push access, your blast radius just expanded significantly.
Here is a quick snippet to audit repository access for users with write permissions using the GitHub CLI:
# List all collaborators with push access for the current repo
gh api repos/:owner/:repo/collaborators --jq '.[] | select(.permissions.push == true) | .login'
You should also check if any of your internal scripts are interacting with GitHub in a way that might trigger this.
# Search for git push operations in recent logs (Linux example)
grep "git-receive-pack" /var/log/github/audit.log | tail -n 50
How is everyone handling the "least privilege" aspect here? Are you strictly segregating write access for infrastructure repos, or do you assume a trusted dev environment is sufficient?
We've already set up a correlation rule in our SIEM. We're looking for spikes in git-receive-pack processes that spawn sub-shells. It's a crude heuristic, but until we patch GHES, it's better than nothing.
ProcessCreate
| where FileName in~ ("git-receive-pack", "git-http-backend")
| where ParentProcessName contains "bash" or ParentProcessName contains "sh"
This is a classic case of input sanitization failure in handling packfiles. If you can't patch immediately, I'd suggest reviewing your pre-receive hooks. While they run on the server, a well-tuned hook might reject the malformed payload before it hits the vulnerable function. It's a band-aid, but it might stop a script-kiddie.
As a DevOps engineer, this terrifies me. We rely heavily on protected branches, but if the vulnerability is in the transport layer (the push itself), branch protection rules might not apply until the command executes. We are forcing MFA for all git operations starting today to reduce the risk of credential compromise leading to this exploit.
To add a detection layer before execution, we're parsing GitHub Audit Logs directly for shell metacharacters in push refs. This often catches researchers or threat actors testing the waters before a full chain attempt. You can export the logs and run a quick check:
zcat audit.log.gz | jq -r 'select(.action == "git.push") | .ref' | grep -E '[;&|`$]'
It’s noisy, but effective for catching validation attempts.
Since we can't always trust the logs immediately, I'm taking a lower-level approach with eBPF to catch the exploit chain at the kernel level before the attacker can clean up. If you're running Linux, bpftrace is lightweight and effective for this. Here's a one-liner to alert on unexpected child processes spawned by the git service user:
bpftrace -e 'tracepoint:syscalls:sys_enter_execve /comm == "git-receive-pack"/ { printf("Command executed: %s\n", str(args->filename)); }'
On the mitigation side, we've tightened the container runtime privileges for our GHES deployment. By dropping non-essential capabilities like CAP_SYS_ADMIN and enabling a read-only root filesystem where possible, we contain the blast radius even if the RCE triggers. It's not a fix, but it stops the attacker from pivoting to the host. If you're using Docker, you can enforce this in your compose file:
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
read_only: true
While detection is crucial, in OT environments where patching cycles take months, containment is king. We've isolated our GHES instances in a strictly firewalled VLAN with no outbound internet access. This prevents the RCE from calling back to C2 servers even if triggered. You can enforce this at the host level using iptables to drop traffic from the git service user:
iptables -A OUTPUT -p tcp --dport 443 -m owner --uid-owner git -j DROP
It’s a crude containment layer, but it limits the blast radius significantly.
Solid points on detection. While we hunt for the injection, let's not overlook containment. If an attacker achieves RCE, they'll likely try to establish a C2 channel. I've applied strict egress filtering to our GHES nodes, allowing outbound traffic only to specific internal proxies or update endpoints. This effectively neutralizes reverse shells.
You can validate your firewall rules by attempting an external connection from the server:
curl -I https://c2-attacker-domain.com
Verified Access Required
To maintain the integrity of our intelligence feeds, only verified partners and security professionals can post replies.
Request Access