# Bypassing GitHub's OAuth flow

Rails routes HEAD to GET handlers, but request.get? is false, so an authenticated cross-site HEAD to /login/oauth/authorize silently granted OAuth app permissions without CSRF or consent.

- Payout: $25,000
- Severity: critical
- Program: GitHub
- Credited: Teddy Katz
- Disclosed: 2019-11-05
- Write-up quality: 8/10
- Original: https://blog.teddykatz.com/2019/11/05/github-oauth-bypass.html
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-teddykatz-com-2019-11-05-github-oauth-bypass-html
- Tags: domain:web impact:ato impact:data-leak kind:bounty platform:blog sev:critical tech:ruby technique:chained technique:source-review vuln:auth-bypass vuln:csrf

**Target:** GitHub OAuth authorization endpoint /login/oauth/authorize
**Root cause:** Rails routes HEAD requests to GET handlers but request.get? returns false, so the controller's if request.get?/else branch treated HEAD as an authenticated POST grant. HEAD requests skip CSRF validation since they are assumed side-effect-free.
**Steps:**
1. Deobfuscate GitHub Enterprise Rails source and read the OAuth controller.
2. Note router maps /login/oauth/authorize via [:get, :post] and controller branches on request.get?.
3. Observe Rails routes HEAD like GET but request.get? is false for HEAD.
4. Send authenticated cross-site HEAD request to /login/oauth/authorize?client_id=...&scope=... from attacker page.
5. Controller falls into else branch and grants OAuth app access without CSRF token or consent page.
**How found:** Downloaded GitHub Enterprise trial, deobfuscated it, and read Rails code. Noticed the Authorize button POSTs to the same URL as the GET consent page, with the controller branching on request.get?. Recalled Rails' HEAD-to-GET routing hack and that request.get? returns false for HEAD, hypothesizing HEAD would hit the grant branch without CSRF.
**Exploitation:** Attacker hosts a page that issues a cross-site authenticated HEAD request to GitHub's /login/oauth/authorize with the attacker's OAuth app client_id and desired scopes. Victim's browser sends session cookies; no CSRF token required for HEAD; GitHub grants the app access silently, letting attacker read/modify victim's private data via the issued token.
**Insight:** When a controller branches on request.get?/request.post? but the router maps methods via :via, check whether HEAD (or other methods Rails aliases to GET) reaches the same handler with a different request.get? result — method-confusion between router and controller can turn a read endpoint into a state-changing one that skips CSRF.
