Executive Summary
CVE-2026-3854 is a critical remote code execution (RCE) vulnerability discovered by Wiz Research in GitHub's internal git infrastructure, affecting both GitHub.com and GitHub Enterprise Server (GHES). Any authenticated user with push access to a repository could exploit it with a single git push command, potentially gaining code execution on backend storage nodes and accessing private repositories belonging to other organizations. GHES patches were released on March 10, 2026; public disclosure followed on April 28, 2026 — if you run a self-hosted GHES instance and have not yet upgraded, treat this as an emergency patch.
1. What Is This Vulnerability?
At its core, CVE-2026-3854 is a header injection vulnerability in babeld, GitHub's internal git proxy service. When a user runs git push -o <option> (push options), git passes arbitrary string values to the server side. The problem: babeld copied those values verbatim into a semicolon-delimited internal header called X-Stat — without stripping or escaping the semicolon character that also serves as the field delimiter.
Because downstream services parsed the X-Stat header following a last-write-wins rule (the last field with a given name wins), an attacker could inject their own field values simply by embedding semicolons and field names inside a push option string.
The Three-Step Exploitation Chain
What makes this vulnerability exceptional is the chained injection that escalates from header manipulation all the way to arbitrary code execution:
Step 1 — Sandbox Bypass via rails_env Injection
By injecting a rails_env field value of a non-production environment name (e.g., development), the attacker switches the internal request path from the hardened production code path to an unsandboxed development path. The production path enforces strict filesystem and hook execution restrictions; the development path does not.
Step 2 — Hook Directory Hijack via custom_hooks_dir Injection
With the sandbox bypassed, the attacker injects a custom_hooks_dir value pointing to an attacker-controlled directory (or one reachable via path traversal). This tells the git hook runner to load hook scripts from that location instead of the legitimate repository hooks directory.
Step 3 — Path Traversal Hook Execution
Finally, the attacker injects a repo_pre_receive_hooks field containing a crafted hook entry whose script field uses path traversal sequences (../../) to reference an executable the attacker has planted or can otherwise control. The hook runner executes it as part of the pre-receive lifecycle, achieving arbitrary code execution on the storage node.
Attack Vector
git push -o "key=value;rails_env=development;custom_hooks_dir=/attacker/path;repo_pre_receive_hooks=../../../attacker/hook" origin main
No special tooling is required beyond a standard git client. Exploitation requires only push access to any repository on the instance — a privilege available to every developer or contributor on GitHub.
Real-World Impact
On GitHub.com (cloud), the vulnerability was discovered and patched on the same day (March 4, 2026) before any known exploitation in the wild. However, Wiz Research confirmed that shared storage nodes backing GitHub.com held repositories from millions of users and organizations — meaning a successful exploit on .com could have exposed private code from unrelated tenants sharing the same node. For GHES self-hosted instances, the impact is full server takeover: all repositories, secrets, webhooks, Actions runners, and stored credentials.
2. Who Is Affected?
| Platform | Affected Versions | Status |
|---|---|---|
| GitHub.com (cloud) | All versions prior to March 4, 2026 | Silently patched March 4, 2026 |
| GHES 3.14.x | < 3.14.24 | Patch: 3.14.24 |
| GHES 3.15.x | < 3.15.19 | Patch: 3.15.19 |
| GHES 3.16.x | < 3.16.15 | Patch: 3.16.15 |
| GHES 3.17.x | < 3.17.12 | Patch: 3.17.12 |
| GHES 3.18.x | < 3.18.6 | Patch: 3.18.6 |
| GHES 3.19.x | < 3.19.3 | Patch: 3.19.3 |
Prerequisites for exploitation: An attacker must have a GitHub account with push access to at least one repository on the target instance. This is a low bar — a free account with a single public repo is sufficient. Internal threat actors or supply chain attackers with developer-level access are also in scope.
Not affected: Organizations using GitHub.com (cloud) exclusively are protected by GitHub's server-side patch deployed March 4, 2026. No action is required beyond confirming you are using the cloud-hosted service and not a GHES instance.
3. How to Detect It (Testing)
Check Your GHES Version First
# SSH into your GHES appliance
ghe-version
# Output example: GitHub Enterprise Server 3.17.10
# If lower than the patched version for your branch — you are vulnerable.
Manual Testing Steps
- Step 1: Identify your GHES version via the admin console at
https://<GHES_HOST>/setup/settingsor viaghe-versionon the appliance. - Step 2: Cross-reference against the patched version table above. If your version is lower than the patch baseline for your branch, you are vulnerable.
- Step 3 (Canary test — safe, non-destructive): With a test repository on the GHES instance, attempt a push with a benign push option containing a semicolon:
git push -o "test=hello;world" origin <branch>. On a vulnerable instance, this push will succeed without error. On a patched instance, it should either succeed with the value treated as literal or be rejected depending on the validation method applied.
⚠️ Do not attempt live exploitation of the full injection chain on production instances. The canary semicolon test above is sufficient to confirm susceptibility without risk.
Log-Based Indicators of Prior Exploitation
# On GHES appliance — search audit log for unusual push options
sudo grep -E 'push_options.*[;]' /var/log/github-audit.log | tail -100
# Look for push options containing injection keywords
sudo grep -E 'rails_env|custom_hooks_dir|repo_pre_receive_hooks' /var/log/github-audit.log
Any hits containing rails_env, custom_hooks_dir, or repo_pre_receive_hooks in push option values are strong indicators of exploitation attempts.
Automated Scanning
The vulnerability itself is a configuration/version issue rather than an application-layer flaw, so traditional scanners (ZAP, Burp) are not the right tool. Instead:
- Tool: Tenable Nessus / Qualys / Trivy (for container-based GHES)
- Check: Use the GHES version detection plugin/check to confirm whether the installed version is below the patched baseline.
- Expected output: A finding flagged against CVE-2026-3854 if the version is unpatched.
Alternatively, use the GitHub Security Advisory API or gh api CLI:
# Using GitHub CLI to check advisory
gh api /advisories --jq '.[] | select(.cve_id == "CVE-2026-3854")'
Code Review Checklist (for teams building babeld-like systems)
- All user-supplied input passed into delimited internal headers is stripped of delimiter characters before insertion
- Internal header parsing uses fixed, validated field names only — never trusting field names from user-controlled sources
- The last-write-wins pattern in header parsing is not applied to security-sensitive fields (environment settings, hook directories, etc.)
- Push option values are validated against an allowlist of safe characters before being forwarded downstream
4. How to Fix It (Mitigation)
Step-by-Step Remediation for GHES Administrators
- Confirm your current GHES version using
ghe-versionor the admin console. - Download the appropriate hotpatch from the GitHub Enterprise releases page. Identify the patched version for your branch (see table in Section 2).
- Apply the patch:
# Upload hotpatch to GHES admin console, or via CLI: ghe-upgrade <path-to-hotpatch.pkg> - Verify the upgrade by running
ghe-versionagain and confirming the version reflects the patched release. - Review audit logs for exploitation indicators (see Section 3) to determine whether the instance was targeted prior to patching.
- Rotate sensitive credentials if log review suggests prior exploitation: GitHub Actions secrets, deploy keys, OAuth tokens stored on the instance, and any secrets accessible to repositories on the affected node.
Immediate Compensating Control (If Patching Is Delayed)
If you cannot patch immediately, restrict push access on the GHES instance to reduce the attacker surface:
- Require all repository push access to go through protected branch rules with required reviews.
- Disable push options (
receive.advertisePushOptions = false) at the git config level if your workflows do not depend on them:# On GHES appliance (SSH) ghe-config app.git.receive-advertise-push-options false ghe-config-apply - This does not fully eliminate the attack surface but raises the bar.
Code Fix Example (for babeld-style systems)
Vulnerable pattern (before fix):
// User input copied verbatim into semicolon-delimited header
xStatHeader := fmt.Sprintf("repo=%s;rails_env=%s;%s", repoID, env, userPushOptions)
Fixed pattern (after fix):
// Sanitize user input — strip delimiters and reject injection keywords
safePushOptions := sanitizePushOptions(userPushOptions)
func sanitizePushOptions(opts string) string {
// Remove semicolons and any field names matching internal keywords
disallowed := regexp.MustCompile(`[;]|rails_env|custom_hooks_dir|repo_pre_receive_hooks`)
return disallowed.ReplaceAllString(opts, "")
}
xStatHeader := fmt.Sprintf("repo=%s;rails_env=%s;%s", repoID, env, safePushOptions)
Configuration Hardening
- Restrict who has push access to repositories on your GHES instance. Use GHES organization roles and protected branches aggressively.
- Enable GHES audit log streaming to a SIEM so that anomalous push patterns are alerted on in real time.
- Enable IP allowlisting on the GHES admin console to reduce unauthenticated network exposure.
5. How to Test the Fix (Validation)
Regression Test Scenarios
- Scenario A (Fix verification): After upgrading, repeat the canary semicolon push test (
git push -o "test=hello;world" origin <branch>). Confirm no injection is processed — the push should succeed with the value treated as a safe literal string, and no anomalous fields appear in the audit log. - Scenario B (Attack simulation blocked): Attempt a push with an injected
rails_envvalue. Confirm the push either fails with a validation error or succeeds with the injected field stripped and not reflected in backend processing. - Scenario C (Normal functionality preserved): Confirm that legitimate push options used by your CI/CD pipelines (e.g., merge request options, skip-ci flags) continue to work as expected after patching.
Security Test Cases
Test Case 1: Verify delimiter injection is blocked
- Precondition: GHES upgraded to patched version
- Steps:
git push -o "legit=value;injected=evil" origin main - Expected Result: Push succeeds (if options are allowed), but no
injected=evilfield appears in downstream header processing; audit log shows only the sanitized value
Test Case 2: Verify rails_env injection is blocked
- Precondition: GHES upgraded to patched version
- Steps:
git push -o "x=y;rails_env=development" origin main - Expected Result: The
rails_envvalue in any internal headers reflects the server's configured environment, not the attacker-supplieddevelopmentvalue
Test Case 3: Verify hook directory injection is blocked
- Precondition: GHES upgraded to patched version
- Steps:
git push -o "x=y;custom_hooks_dir=/tmp/attacker" origin main - Expected Result: Hook execution uses the legitimate repository hooks directory; no hooks are run from
/tmp/attacker
Automated Validation Test
import subprocess
import re
def test_push_option_injection_blocked(repo_path, remote="origin", branch="main"):
"""
Sends a git push with an injected push option.
Pass condition: push completes OR is rejected, but no injection marker
appears in the GHES audit log within 5 seconds.
"""
result = subprocess.run(
["git", "push", "-o", "test=canary;rails_env=development", remote, branch],
cwd=repo_path,
capture_output=True, text=True
)
# Log the exit code and output for inspection
print(f"Exit code: {result.returncode}")
print(f"Stdout: {result.stdout}")
print(f"Stderr: {result.stderr}")
# Expect: the push succeeds or fails cleanly, not with an exploitation signal
assert "rails_env" not in result.stdout.lower(), "Injection keyword reflected — potential vulnerability!"
print("PASS: No injection keyword reflected in push response.")
# Run the test
test_push_option_injection_blocked("/path/to/local/repo")
6. Prevention & Hardening
Best Practices
- Patch promptly: GitHub has a rapid disclosure timeline (same-day fix on
.com, 6-day gap before GHES patch, ~55 days before public disclosure). Subscribe to GitHub Enterprise release notes and apply security hotpatches within 24 hours of release. - Principle of least privilege for push access: Treat push access to any repository as a privileged operation. Use CODEOWNERS, protected branches, and required code reviews to limit who can push directly.
- Secrets hygiene: Rotate GitHub Actions secrets, deploy keys, and OAuth tokens on a regular schedule — and immediately after any security incident or suspected breach.
- Audit log monitoring: Stream GHES audit logs to your SIEM in real time. Alert on push operations with unusual characters or known injection keywords in push option values.
- Vendor security subscription: Subscribe to GitHub's security advisories mailing list and monitor the GHES release page for hotpatches.
Monitoring & Detection
# Real-time alert rule (example for Splunk/SIEM ingest of GHES audit logs)
# Alert if any push option contains a semicolon or known injection keyword
index=ghes_audit action=git.push
| where match(push_options, "[;]") OR match(push_options, "rails_env|custom_hooks_dir|repo_pre_receive_hooks")
| stats count by user, repo, push_options, _time
| where count > 0
| alert
For EDR/XDR coverage on GHES appliance nodes, monitor for:
- Unexpected process spawns from git hook runner processes
- File creation events in unusual directories (
/tmp, world-writable paths) by git-related processes - Outbound network connections from git hook child processes
Supply Chain Awareness
CVE-2026-3854 is a reminder that your CI/CD infrastructure is a high-value target. Treat your GHES instance with the same urgency as production application servers: network-segment it, limit external access, enforce MFA for all accounts, and maintain a regular patching cadence. The discovery of this flaw through AI-augmented reverse engineering of closed-source binaries also signals that attackers have sophisticated tooling — defenders must match that pace.
References
- CVE Entry: CVE-2026-3854 at NVD
- GitHub's Official Blog Post: Securing the git push pipeline — The GitHub Blog
- Wiz Research Breakdown: GitHub RCE Vulnerability: CVE-2026-3854 Breakdown — Wiz Blog
- The Hacker News Coverage: Researchers Discover Critical GitHub CVE-2026-3854 RCE Flaw
- GHES Patch Downloads: GitHub Enterprise Releases
- SentinelOne Vulnerability DB: CVE-2026-3854 — SentinelOne
- CyCognito Analysis: Emerging Threat: CVE-2026-3854 — CyCognito Blog