This is a story about how I connected 5 Low-severity bugs into one big bug, with which you could read / write private turnips on GitHub (
again ).
A few days ago, githab launched a
bounty program . For 4 hours, I created such a URL after visiting which I got access to your githad account and repositories. Want to know how?
I started by checking
Github OAuth .
Bug 1. Bypassing the 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 upon receiving a token.
The first bug in itself costs nothing. OAuth2 has a built-in protection that for each released code there is a corresponding redirekt_uri, and when exchanging a code for a token, you must give the same uri that was used at the beginning. Simply speaking, if the code returned to the site / callback, then to get the token, you must send the site / callback.
Oddly enough, the githabs did not implement the check correctly. You could release the code for /path1/../path2 and then use it on / path1. That is, the code that was 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 the login function through GitHab. A similar bug was in vk.com.
Bug 3. Pictures on a hist.

I started watching the official github clients - Education, Pages, Speakerdeck, Gist. The first two did not use OAuth in essence, the third was not part of the bounty program, but the gist was very suitable. He was a "pre-approved" client, that is, by default it is installed for all users.
But it was impossible to just insert

since the githab Camo proxy will replace it with a 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 Ruby, but browsers parse as host and load
host.com instead of
github.com///host.comOur 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

As soon as the user loads this address, the githab automatically redirects to my gist with a picture on my server:
Location:
gist.github.com/auth/github/callback/../../../homakov/8820324?code=CODEThe browser downloads
gist.github.com/homakov/8820324?code=CODEAnd then, when requesting our picture, it merges the referrer.
As soon as we get the CODE of the victim we can open
gist.github.com/auth/github/callback?code=CODE - voila. We are logged in as a victim on a gist and we have access to its private gists.
Bug 4. The token is stored in cookies.

This is the OAuth anti-pattern, it is not recommended to store / show the token to the browser, the recorder stores it in the session rail. Which as we know is just a base64 encoded and signed cookie.

Here it is - github_token. Now we can make inquiries directly, bypassing the site of the gist. But the token has scope = gists and I can not read anything except the gists. Although…
Bug 5. Automatic approval of any scope for official clients.
Finishing touch. Since GIST is the official client of the githab, you do not see the “Approve these scopes” dialog and the github makes the approval for you automatically. So I can just send
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
Then use the merged CODE for the login to the victim's account, read the cookie, take the github_token from there, and then I can make API calls completely invisible to the user - because the token belongs to Gist! Stealth mod is such a crime without a trace.
The reward was $ 4,000.

And in general, I am available for work, for example.