Contents

GitHub RCE CVE-2026-3854: One Semicolon, Millions of Private Repositories

 

Want to learn ethical hacking? I built a complete course. Have a look!
Learn penetration testing, web exploitation, network security, and the hacker mindset:
→ Master ethical hacking hands-on
Hacking is not a hobby but a way of life!

 
Contents

GitHub RCE CVE. A semicolon broke GitHub. One character in a push option field, and a security researcher was running code on the backend servers that store private repositories from millions of users and organizations. The git service user that processes every push on those servers has filesystem access to every repository on the node, and that access does not check who the repository belongs to. Private code from banks, hospitals, governments, and individual developers all sits on the same shared infrastructure. The command that got the researcher there is something every developer already runs every day.

CVE-2026-3854, CVSS 8.7. GitHub has 150 million developers and all of that code flows through one internal pipeline. That pipeline had a flaw that any user with push access to any repository could exploit.

When code gets pushed to GitHub, it does not just land on a server. It travels through a chain of four internal services before anything gets stored. The first is a proxy called babeld, which receives the incoming SSH connection. babeld forwards the authentication details to a second service called gitauth, which checks whether the user has the right to push to that repository and decides what rules apply to the session: file size limits, branch naming rules, and so on. gitauth sends all of that back to babeld, which packages it into a single internal message called the X-Stat header and passes everything to the next service in line. That header is just a line of text where every setting is separated by a semicolon:

1
X-Stat: repo_id=12345; large_blob_rejection_enabled=bool:true; push_option_0=myvalue; push_option_count=1

The services downstream read it by splitting on semicolons and loading everything into a lookup table. If the same key appears twice, the later value wins. That last detail is what made the whole thing exploitable.

Git has a feature called push options. By adding -o to a git push command, arbitrary text strings can be passed to the server as hints. GitHub uses them for things like skipping CI runs or triggering specific pipelines. Whatever text gets passed lands in the X-Stat header as push_option_0, push_option_1, and so on.

babeld took those strings and placed them directly into the header without checking for semicolons first. A semicolon inside a push option value breaks out of that field and starts a new one. And because the last value for any given field wins, anything written after a real security setting will silently overwrite it. The security settings from gitauth are already in the header. Writing new ones after them is enough to replace them.

The attack chains three injections together in a single command:

1
git push -o "x;rails_env=development;custom_hooks_dir=/tmp/attacker;repo_pre_receive_hooks=[{\"hook_id\":1,\"name\":\"pwn\",\"script\":\"../../../../../../bin/id\"}]" origin main

The first injected field changes rails_env. This is an environment variable that controls which execution path the pre-receive hook takes when it runs. When set to a production value, hooks run inside a sandbox with restricted access. When set to anything else, they run directly with no restrictions. The second field, custom_hooks_dir, points the server to a different directory when looking for hook scripts to execute. The third, repo_pre_receive_hooks, defines a hook with a script path that uses ../ sequences to walk up the filesystem directory tree and land on any binary the server has access to.

What came back from GitHub’s own servers:

1
remote: uid=500(git) gid=500(git) groups=500(git)

That is the git service user executing code on GitHub’s own infrastructure.

On GitHub Enterprise Server, the version that companies run internally on their own hardware, this means complete server compromise. The git user has read and write access to every repository on the instance, every secret stored in the service configuration, and the ability to move through the internal network.

Reaching GitHub.com required one more step. The same pipeline on the public platform has an additional flag in the X-Stat header that disables the custom hooks path by default. That flag was also sitting in the header, also injectable through the same mechanism. Once it was set, the full chain worked identically. The Wiz Research team landed on a shared storage node inside GitHub.com and found the git service user had access to repository index entries belonging to millions of other users and organizations sharing that node. They confirmed the access would have worked and stopped there without reading anyone else’s content.

Wiz Research, the cloud security firm behind the discovery, had tried to analyze GitHub’s internal pipeline before. GitHub’s internal services are compiled binaries with no public source code, and reverse engineering them, taking raw processor instructions and working backwards to understand what the software does, historically took weeks of manual work across multiple services written in different languages. That effort had previously been too large to finish.

This time the team used a tool called IDA MCP. IDA Pro is the software security researchers use to reverse engineer compiled binaries. MCP, the Model Context Protocol, is a standard that connects AI language models to external tools so the AI can operate them directly. By connecting IDA Pro to an AI through MCP, the team could automatically decompile binaries, trace the exact path user input takes through every service, and identify every place that input arrives without being validated. What had been weeks of slow manual work became fast enough to finish. Wiz Research security researchers Sagi Tzadik, Nir Ohfeld, Ronen Shustin, Hillai Ben-Sasson, Yuval Avrahami, and Noam Malron call this Round 2 of their GitHub research. AI is what made it possible.

Wiz reported the vulnerability to GitHub on March 4, 2026. GitHub validated it and deployed a fix to GitHub.com in under two hours. Patches for all supported GitHub Enterprise Server versions followed on March 10. GitHub called the finding rare enough to earn one of the highest rewards in the history of their bug bounty program.

GitHub also confirmed that nobody had exploited this before Wiz found it. The exploit forces the server to execute a code path that is never reached during normal operations. GitHub logged that path and searched their full telemetry history. It had never triggered before. The exploit flagged itself the moment it ran, which gave GitHub a reliable way to verify nothing had happened before the fix.

At the time of public disclosure on April 28, 2026, 88% of GitHub Enterprise Server instances were still running vulnerable versions. The patch had been available for seven weeks.

Anyone running GitHub Enterprise Server should check their current version now:

1
ghe-version

GitHub recommends upgrading to the latest available patch for your version branch. The minimum fixed versions at time of disclosure:

  • → 3.14.25 or later
  • → 3.15.20 or later
  • → 3.16.16 or later
  • → 3.17.13 or later
  • → 3.18.8 or later
  • → 3.19.4 or later
  • → 3.20.0 or later

To check whether this was exploited before the patch, look for semicolons in push option values in the audit log:

1
grep -E "push_option.*;" /var/log/github-audit.log

Any semicolons in push option values that are not part of a known workflow integration are worth a closer look.

GitHub.com and GitHub Enterprise Cloud are already patched.

The pattern here goes beyond GitHub. Multiple services, each written in a different language, each making reasonable assumptions about the data the previous service had already validated. babeld assumed push option strings were safe to embed verbatim. The services downstream assumed every field in the X-Stat header was placed there by a trusted source. The pre-receive hook assumed a specific variable could only ever hold one value on a live system. Every one of those assumptions was reasonable on its own. Together they formed a path from one unsanitized character to unsandboxed code execution on infrastructure serving 150 million developers.

This is one of the first critical vulnerabilities ever found in closed-source production systems through AI-assisted reverse engineering. What used to take weeks now takes hours.

CVE-2026-3854 is built on the same foundations that show up in every penetration test: exploiting input that reaches a server without being validated, escalating access once inside, and moving through infrastructure to reach what actually matters. My ethical hacking course covers all of it from scratch:

Join my complete ethical hacking course

Hacking is not a hobby but a way of life. 🎯

Sources: Wiz Research: CVE-2026-3854 Breakdown · GitHub Security Blog: Securing the git push pipeline · GitHub Advisory Database: GHSA-64fw-jx9p-5j24

 

→ Stay updated!

Get the latest posts in your inbox every week. Ethical hacking, security news, tutorials, and everything that catches my attention. If that sounds useful, drop your email below.

By Bulls Eye

Jolanda de koff • emaildonate

My name is Jolanda de Koff and on the internet, I'm also known as Bulls Eye. Ethical Hacker, Penetration tester, Researcher, Programmer, Self Learner, and forever n00b. Not necessarily in that order. Like to make my own hacking tools and I sometimes share them with you. "You can create art & beauty with a computer and Hacking is not a hobby but a way of life ...

I ♥ open-source and Linux