Back to Articles
critical

CRITICAL: GitHub Enterprise Server RCE via a Single Git Push (CVE-2026-3854)

Wiz researchers disclosed CVE-2026-3854 on April 28, a critical remote code execution flaw in GitHub Enterprise Server that turns a single authenticated git push into full server compromise. Roughly 88 percent of internet-exposed Enterprise Server instances were unpatched at disclosure. GitHub.com and Enterprise Cloud are already fixed, but self-hosted admins must upgrade to 3.14.25, 3.15.20, 3.16.16, 3.17.13, 3.18.7, 3.19.4, or 3.20.0.

By Danny Mercer, CISSP — Lead Security Analyst Apr 30, 2026
Is your business exposed? Our McKinney-based security team can assess your risk for free.
Share:

If you maintain a self-hosted GitHub Enterprise Server, today is the day you stop reading and go pull the patch. Wiz researchers publicly disclosed CVE-2026-3854 on April 28, a critical remote code execution flaw that turns an ordinary git push into a full takeover of the GHES backend. At the time of public disclosure, roughly 88 percent of internet-exposed Enterprise Server instances had not yet applied the fix, which means a sizable chunk of corporate source code, build pipelines, and signing infrastructure is sitting on a vulnerable box waiting for someone with a script and patience to come knocking.

The bug carries a CVSS score of 8.7 and lives in the part of GitHub's plumbing that almost nobody looks at, the internal headers GitHub uses to pass push metadata between services. When you run git push -o some_option=some_value, GitHub takes that option and stuffs it into an internal header called X-Stat before forwarding the push to the service that actually decides whether to accept it. The X-Stat header packs multiple fields into one string using a delimiter character, and that delimiter happened to be one that user input could legally contain. Push options went through with no sanitization, the downstream service parsed the resulting blob as a list of trusted internal fields, and an attacker who understood the format could inject any field they wanted with last-write-wins semantics.

That on its own would be ugly enough for an information disclosure or privilege escalation finding. What lifts CVE-2026-3854 into "drop everything and patch this now" territory is the way three injected fields chain together into clean RCE. The first injection targets the rails_env field. GitHub's pre-receive hook binary checks rails_env to decide whether to execute hooks inside a sandbox or to run them directly on the host. Production environments get the sandbox. Anything else, including a value an attacker dreams up on the spot, runs hooks straight on the file system as the git user. Step one is therefore convincing the binary you are not in production, you are wherever you want to be.

The second injection targets custom_hooks_dir, which controls where the hook runner looks for scripts to execute. Override it with a path you control and the runner happily reads from your chosen directory. The third injection targets repo_pre_receive_hooks, the field that tells the runner which hook entries to invoke. Wiz dropped a path traversal payload into that field, joining the attacker-controlled base directory with a traversal sequence and pointing the runner at any binary already sitting on the box. The end result is arbitrary command execution as the git service account, which on a GitHub Enterprise Server is an account with read and write access to every hosted repository and the keys to most of the internal secrets store.

Wiz Research reported the issue to GitHub on March 4, 2026. GitHub's security team validated it in roughly 40 minutes and shipped a fix to GitHub.com about 75 minutes after the initial report, which is genuinely impressive turnaround for a service handling tens of millions of pushes per day. The cloud variants, including GitHub Enterprise Cloud, GitHub Enterprise Cloud with Data Residency, and GitHub Enterprise Cloud with Enterprise Managed Users, were patched as part of that same rollout. Customers running those services have nothing to do.

The story is different for self-hosted Enterprise Server. GitHub released patched builds across the entire supported version range, and any administrator running 3.14.25, 3.15.20, 3.16.16, 3.17.13, 3.18.7, 3.19.4, or 3.20.0 is in the clear. Anything older is still vulnerable, and Help Net Security's scan data put 88 percent of public Enterprise Server fingerprints below those numbers on the morning of disclosure. If your patch cycle for GHES tends to drift because you have a thousand build jobs depending on a stable upstream, this is the week to break the cycle.

Exploitation in the wild is a wash so far. GitHub's forensic team combed telemetry for the anomalous code path that the chained injection triggers and found exactly one source, the Wiz researchers running their own proof of concept against their own test instance. The company stated explicitly that no customer data was accessed, modified, or exfiltrated as a result of the bug. That is the right answer at the cloud tier, but it tells you nothing about your own GHES box. Anybody with push rights on a single repository on your server could have run the chain, and unless your environment has audit logging configured to capture push options, you may not be able to tell whether they did.

The detection story is straightforward enough to bolt on this afternoon. GitHub's advisory recommends reviewing /var/log/github-audit.log for push operations whose options contain a semicolon character, since the delimiter that made the injection possible was, of all things, a semicolon. Legitimate users do not put semicolons in push options. If you find any, you have either a researcher in your org running their own test or an actual attempt to exploit, and either way you want to know. SOC teams running a SIEM should add a high-confidence rule for git push -o traffic carrying semicolons against any internal Enterprise Server logs and treat hits as a P1 ticket until proven otherwise. Network teams who terminate TLS in front of GHES can layer in a request inspection rule on the X-Stat header at the proxy as defense in depth, though that catches a slightly different population of malformed requests.

Patching is the only complete fix. Mitigations like disabling push options at the repository level reduce the attack surface but leave the underlying parser bug in place, and any future researcher who finds another way to land the bad delimiter in the right place would reopen the wound. The clean path is to upgrade GHES to the version on the supported track for your release line and verify that the relevant binaries match. Restart the GitHub services after upgrade. Pull a fresh audit baseline. Rotate any secrets that lived on the server, including SSH host keys, runner registration tokens, integration tokens for external systems like Jira or Slack, and any cached credentials in the GitHub Actions runners that talk to your registry. If the box was compromised before you applied the patch, those secrets need to be considered burned regardless of what your audit logs say.

There is a quieter lesson buried in the Wiz writeup that is worth pulling out for any team that builds backend services. GitHub's parser problem was not exotic. It was a delimiter chosen years ago that nobody flagged when a feature shipped that let users supply data destined for the same channel. Push options were added to git in 2017. The internal X-Stat header has been around longer than that. The two systems met somewhere along the way and nobody reran the threat model, and a chain of three innocent looking field overrides led to RCE on the busiest source code platform on earth. If your service architecture relies on string-formatted internal headers, this is your reminder to either move to a binary or length-prefixed format that cannot be confused by content, or to treat every byte that comes from a user as if it might contain your delimiter, because eventually it will.

For MSPs and managed security providers, this disclosure is a layup conversation with any client that runs self-hosted GitHub. A 30-minute call to verify the GHES version, confirm patch cadence, and walk through audit log review covers the immediate ask. The bigger sale is the recurring service that prevents the next version of this story, namely a managed patching contract for development infrastructure, attached to vulnerability monitoring on the public-facing surface and quarterly access reviews on who actually has push rights to what. Most clients underestimate how many internal users have push to a repo on the Enterprise Server, and the audit alone tends to surface findings worth a line item on its own. Bundle in dark web monitoring for stolen GitHub credentials and you have a tidy productized package for any client whose source code is the family jewels.

References

Concerned about this threat?

Our security team can assess your exposure and recommend immediate actions.

Get a Free Assessment →