# Forging signed commits on GitHub

GitHub's internal Codespaces commit-signing API validated the author line with a regex that skipped zero-length names, letting attackers get commits signed as any user.

- Payout: $10,000
- Severity: medium
- Program: GitHub
- Credited: iter.ca
- Disclosed: 2023-11-11
- Write-up quality: 8/10
- Original: https://iter.ca/post/gh-sig-pwn/
- Library page: https://bugbountylibrary.com/r/blog%3Aiter-ca-post-gh-sig-pwn
- Tags: domain:api domain:web impact:integrity kind:bounty platform:blog sev:medium tech:github-actions technique:parser-differential technique:source-review vuln:auth-bypass vuln:business-logic

**Target:** GitHub internal API /vscs_internal/commit/sign used by Codespaces gh-gpgsign
**Root cause:** The signing endpoint extracted the author via regex /\Aauthor (.+?) <(.+)>/ which requires a non-empty name, so a zero-length author name line was skipped and a later attacker-controlled author line was validated instead. Git uses the first author line while the API used the second.
**Steps:**
1. Download GitHub Enterprise Server trial VM and deobfuscate Ruby source to learn internals
2. Observe gh-gpgsign calls https://api.github.com/vscs_internal/commit/sign with raw commit body
3. Craft commit with first author line having empty name: `author <583231+octocat@users.noreply.github.com> 1682188800 +0000`
4. Add second author line with target identity: `author username <user@example.com> 1682188800 +0000`
5. POST commit body to /vscs_internal/commit/sign; regex skips empty-name line and validates the second
6. Receive signature; git uses first author line, producing a GitHub-signed commit attributed to victim
**How found:** Researcher downloaded a GitHub Enterprise Server trial VM and deobfuscated the Ruby source to understand internals, then noticed the signing endpoint accepts arbitrary raw commit data and audited its author-validation regex, spotting that `.+?` cannot match a zero-length name.
**Exploitation:** Attacker with a Codespaces environment and GPG signing enabled calls the internal signing endpoint directly with crafted commit data containing an empty-name author line followed by a spoofed author line, obtaining a valid web-flow signature that GitHub UI displays as verified for the victim identity.
**Insight:** When a validator parses a multi-line format that downstream consumers (git) also parse, look for lines the validator's regex silently skips — empty fields, duplicate headers, or lines it can't match — and check whether the consumer picks a different line than the validator.
