How I Hacked Github Once Again

Original author: Egor Homakov
  • Transfer
This is the story of how I combined 5 Low-severity bugs into one big bug, with which it was possible to read / write to private turnips on Github ( again ).

A few days ago, a github launched a bounty program . For 4 hours I made such a URL after visiting which I got access to your github account and repositories. Want to know how?

I started by checking out Github OAuth .

Bug 1. Bypass redirect_uri validation with /../

It's simple - you can send /path1/../path2 to overwrite the previous path (path traversal).

Bug 2. There is no redirect_uri validation when receiving a token.

The first bug in itself is worthless. OAuth2 has built-in protection that there is a corresponding redirect for each released code, and when exchanging the code for a token, you need to give the same uri that was used at the beginning. Simply put, if the code for site / callback is returned, then to receive the token you need to send site / callback.

Oddly enough, the github implemented the check incorrectly. You could issue the code for /path1/../path2 and then use it on / path1. That is, the code leaked through referrers remained valid even for a real callback. With the help of these two bugs, it would be possible to merge codes through referrers on sites with a login function through Github. A similar bug was in vk.com.

Bug 3. Pictures on the hist.

image
I started watching official github clients - Education, Pages, Speakerdeck, Gist. The first two did not use OAuth in fact, the third did not enter the bounty program, but the histogram was very suitable. He was a "pre-approved" client, that is, by default it is installed for all users.
But you couldn’t just insert it, since the Camo-proxy of the github will replace this with the local URL, and the referrer will not leak to your server. To get around this protection I used a rather new trick

///host.com is parsed as a path by all server libraries including rubies, but browsers parsed it as a host and load host.com instead of github.com///host.com

Our url exploit now looks like this:

github.com/login/oauth/authorize?client_id=7e0a3cd836d3e544dbd9&redirect_uri=https%3A%2F%2Fgist.github.com%2Fauth%2Fgithub%2Fcallback/../../../homakov/8820324&response_type=code


image
As soon as the user loads this address, the github will automatically redirect to my gist with a picture on my server:
Location: gist.github.com/auth/github/callback/../../../homakov/8820324?code=CODE

The browser loads gist.github.com/homakov/8820324?code=CODE

And then, when asked for our picture, he merges the referrer.

Once we get the victim's CODE, we can open gist.github.com/auth/github/callback?code=CODE - voila. We are logged in as a victim on the histo and have access to his private histo.

Bug 4. Token is stored in cookies.

image
This is an OAuth antipattern, it is highly recommended not to store / show the token to the browser, but the gist stores it in the session rail. Which as we know is just base64 encoded and signed cookie.
image
Here it is - github_token. Now we can make requests directly, bypassing the site of the hist. But the token has scope = gists and apart from the gists I can not read anything. Though…

Bug 5. Automatic approval of any scope for official customers.

Finishing touch. Since the gist is the official client of the github, you don’t see the “Approve these scopes” dialog and the github makes approval automatically for you. So I can just send Then use the merged CODE to login to the victim's account, read the cookie, take the github_token from there and here I can make API calls completely invisibly to the user - after all, the token belongs to Gist! Stealth mod is a kind of crime without a trace. The reward was $ 4,000. In general, I am available for work, for example.

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





image


Also popular now: