;). GitHub forgot to clean semicolons out of one user-controllable input - so an attacker could sneak extra fake fields into that header, override security settings, and trick the system into running their own code on GitHub's servers. All from a regular git push.01The Core Idea
At its heart this is a classic header-injection bug hiding inside an internal protocol most users never see. The "envelope" is an internal HTTP header called X-Stat. Its fields look like key=value;key=value;key=value. When a key appears twice, the last one wins - and the attacker controls the last one.
That's exactly what happened here. Push options sent by the client are interpolated into X-Stat verbatim. No semicolon escaping. No allow-list. Each downstream service trusts the header as if it came from a privileged caller upstream.
02How the Attack Chain Works
The full exploit is three injections collapsed into a single push:
The exploit, on screen
What it actually looks like when run against a vulnerable GitHub Enterprise Server:
$ git push -o '<injected fields>' origin master Enumerating objects: 3, done. Counting objects: 100% (3/3), done. Writing objects: 100% (3/3), 250 bytes | 250.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 remote: uid=500(git) gid=500(git) groups=500(git) ← arbitrary code, running as the git user To github.com:user/repo.git abc1234..def5678 master -> master
03The Header, Forged
What the downstream service thinks it received vs. what's actually in there. Highlighted fields are injected by the attacker; struck-through values are the legitimate ones that get overridden because the parser keeps the last value for a key.
One legitimate field - push_option - became four illegitimate ones, two of which silently took precedence over real environment configuration. The hook runner happily exec'd whatever path the last fields pointed to.
04Why It Mattered
The blast radius depends on where the bug was triggered. The same primitive lands very differently on shared infrastructure versus a self-hosted appliance.
git user there has filesystem access to every repository on that node - meaning millions of repos belonging to other organizations and users could have been read.05What To Do
GitHub.com is already fixed - no action needed there. But for self-hosted GHES, the patch is the only thing standing between an authenticated user and full root on your appliance.
- ✓GitHub.comNothing to do. Mitigated within 6 hours of disclosure by GitHub’s security team.
- !GHES ≤ 3.19.1 - patch nowUpgrade to
3.19.3or one of the backported fixes:3.14.24·3.15.19·3.16.15·3.17.12·3.18.6. - →For internal-services teamsAudit any internal protocol where one service trusts another’s headers without validation. The X-Stat pattern is everywhere - anywhere a string concat builds an “envelope”, an injection lurks.
06Disclosure Timeline
From discovery to vendor fix in a single day; public disclosure followed once enough customers had upgraded.
07The Bigger Lesson
The pattern here goes way beyond GitHub. Modern systems are built from many services, often in different languages, sharing data through internal protocols. Each service makes reasonable assumptions about that data. But reasonable assumptions stacked together can become dangerous gaps.
Here, one service assumed user-controlled push options were safe to embed verbatim. Another assumed every field in X-Stat came from a trusted source. The hook runner assumed rails_env only ever held "production" in production. Each was fine on its own - fatal in combination.
Worth noting: Wiz used AI-augmented reverse engineering (IDA MCP) to analyze GitHub's closed-source binaries. They call this one of the first critical vulnerabilities discovered with that workflow - a hint at where security research is heading.