Page MenuHomePhabricator

CentralAuth CSRF
Closed, ResolvedPublic

Description

The CSRF vulnerability in CentralAuth is still not fixed.

Copying from the «Bug 23076 strikes again» mail to private-l:
The Wikimedia setup is still vulnerable to bug 23076, the CSRF bug which potentially allowed to log in third parties with the attacker account executing its javascript.

we created the hole with CentralAuth. The image loading is another login entry point, and it isn't protected by a cookie session nor can they easily be.
This is harder to exploit than bug 23076 or bug 23371, since the attacker can't give out the credentials on the malicious page and let the user autolog. The server would need to log in itself at request time and pass through the login image url it gets. At much, he could login different people on each domain. Tokens can only be used once and expire at 10 minutes so it needs to request a new one per victim. Note that this can't be fixed by just reducing the timeout.
Also, users already logged in are also vulnerable, being relogged into the new account.

In order to protect the image auto login token, Special:Userlogin should point with a token to a page on the target server which sets the login, redirects back with a session identifying token, then get redirected again to the real image at the target.
Since all wikis live in the same cluster, this last redirect could be skipped, with the original wiki setting the session for the target one. But it has to be carefully done. Otherwise the attacker could create a new session and attach itself to the user login.

Another option is to completely ditch image autoloading and have the user pass through Userlogin posting to a login server. If the user is logged there, it is redirected back logged in (you still need a token on that url). That has additional advantages, since it would solve "my wiki is not included" claims (bug 14407), skip third party cookies issues (bug 14736), autoaccount creation privacy (bug 19161) and all login actions could be performed by SSL (bug 12884).
On the other hand, now that users are used to automatic login almost everywhere, they may not be willing to press an extra button when they change projects.


Version: unspecified
Severity: normal

Details

Reference
bz40747

Event Timeline

bzimport raised the priority of this task from to High.Nov 22 2014, 12:58 AM
bzimport set Reference to bz40747.
bzimport added a subscriber: Unknown Object (MLST).

Platonides, thank you for reporting the issue. I see what you mean, and I agree this could be exploited.

I do think your first suggestion, with the callback to the originating server, is the way to go. It's a little tricky, so I'm not sure this is something we can get out by next week, but I'll start working on it.

So basically, the protocol would look something like:

a) User performs login to Wiki1

b) Wiki1 sets the session cookie, and sets something (javascript, image, iframe) to Wiki2, and a token(1) that identifies the user.

c) Browser passes token(1) to Wiki2, using token(1) the User's id can be derived

d) If token(1) looks valid, Wiki2 starts a "pending" session for the browser, and returns a session cookie to the browser. A token(2) that identifies the user id to Wiki1 and the anonymous session id of Wiki2 is passed back as well.

e) The browser sends token(2) back to Wiki1; the user is logged into Wiki1, so the request is tied to the original user.

f) If the identity of token(2) matches the logged in session on Wiki1, Wiki1 can either:

f1) Tell Wiki2 (using something in token(2)) that the ID is verified-- update a shared memcache object, most likely.

f2) Pass back a token(3) that identifies the user_id, and can be sent to Wiki2 so that Wiki2 can upgrade the user's session from "pending" to an authenticated session for the given user_id.

I want to try attacking this protocol a good bit before I implement, but that's my initial thought. If anyone sees a problem with it, or has a better idea, please update.

That scheme sounds good to me. f1 could presumably be done by writing to the user's global session with CentralAuthUser::setSession(). Then CentralAuthHooks::onUserLoadFromSession() would be patched to disallow logins for users with "pending" sessions.

Yes, Chris, that was the idea. :)

I remember to think about implementing it with different classes for the different variants.

It may be reusable for OAuth/OpenID solutions.
It can be used to answer the "Is this really user X?" question for generic sites with a couple of tweaks, I'm just unsure if it would fit into any of those protocols (or if we would want to use them).

Finally, I'm not a fan of doing so many redirects in the login process, but they are needed.

An option would be to perform them lazily: Stop at (d). When the user visits Wiki2 for the first time, it sees the pending session, redirects to Wiki1 (which verifies it's still logged in the wiki as the expected user) and finally redirects you back.
This is based in that you won't be visiting all the domains on the session, so we can skip most redirects, and perform them only when needed.

Another would be to daisy chain the redirects, in which case there's only one redirect back to Wiki1 instead of N (N = # of domains). The problem is that the image loading is now slower, with N+1 redirects, instead of N parallel requests of 2-3 redirects each.

I think that we could do the following:

A) User Alice logs in into kg.wikipedia.org.

B) kgwiki sets its local session

C) The current images system is used for all domains, which then set a cookie session saying "this is Alice, kgwiki knows it".
(it is saved into a shortlived token, then copied into each session)

D) When Alice visits a wiki where she doesn't have a local session, such as http://gl.wiktionary.org/wiki/biblioteca it sees the global cookie, creates a local session and redirects her to https://kg.wikipedia.org/wiki/Special:VerifyIdentity with a message-token asking "I'm glwikt, is this really Alice? Place result in session XYZ"

E) kgwiki sees she's still logged in there and redirects her back to https://gl.wiktionary.org/wiki/biblioteca

F) She sees https://gl.wiktionary.org/wiki/biblioteca logged in.

Note:
Less requests in initial login (one http request per domain)

We can now set the central cookie on *.wikimedia.org since the central cookie is no longer equivalent to account access (solves bug 14407)

As we are redirecting the user anyway, we can redirect her to the https at step D (bug 29898).

Further session logins (ie. glwikt) can also be tied to the original one (kgwiki) and get a global logout in one step (Alice then goes to enwiki and logs out there, the glwikt cookie she still has is now useless).

The local session created in D is sent by http once, so it should be destroyed and recreated in F.

There's a little window where an eavesdropper could elevate itself between that cookie is elevated and Alice browser loads again glwikt, though. (worth going to https first?)

At F there must also be a local unsecure cookie pointing out that the user is logged in at https, so redirect to https directly, not through kgwiki.

Created attachment 11200
Three-stage session setup

This is basically what I outlined in my description, except after looking into it, I realized that we need to do the third redirect back to the attached wiki, so that the Username/Token cookies can be set (if the user clicked "remember me"), since Username/Token is preferred for identifying the user over the session cookie.

Also fixed a session fixation bug in this patch.

If this looks ok, we'll deploy it to the WMF cluster, add it into gerrit, and I'll add a notice about it when we we announce 1.19.3.

Attached:

I an quite sure we could skip the third redirect.
Stage 1: s/Recieved/Received/, probably worth showing a message to the user.
You don't seem to be deleting the tokens from stage 1/2/3.

I am now seeing, the behavior of sending to $this->getOutput()->addWikiText when the user expects an icon is probably not too useful. If visiting Special:AutoLogin and the user agent expects an image, we should probably show a sad icon, a skull... in case of failed authentication.

I'm not seeing anything wrong right now, but I'll check it again later.

(In reply to comment #6)

I an quite sure we could skip the third redirect.

The problem is the User and the Token cookies. We can't set them in stage 1, because the username in the Autologin token may be controlled by an attacker.

Also, once this hole is closed, I would like to go back and allow all of this to use javascript (with a noscript-image fallback), so that we can show a progress bar and nicely display the error messages if something fails. So while I don't like the extra redirect, I think we can make sure it doesn't cause any extra problems.

Stage 1: s/Recieved/Received/, probably worth showing a message to the user.

True. This is the way it's currently handled. If there's an error, the user sees a broken image, and would have to do some debugging to see the message. Again, I think it would be a great enhancement, but I would like to get the vulnerability patched first, and then add a nice javascript message.

You don't seem to be deleting the tokens from stage 1/2/3.

The memcache key is deleted right at the top: $wgMemc->delete( $tokenKey );

I am now seeing, the behavior of sending to $this->getOutput()->addWikiText
when the user expects an icon is probably not too useful. If visiting
Special:AutoLogin and the user agent expects an image, we should probably show
a sad icon, a skull... in case of failed authentication.

I'm not seeing anything wrong right now, but I'll check it again later.

Using CVE-2012-5394 to track this

Fix was deployed today. A couple reports of autologin being "funky":
(05:08:19 PM) Brooke: Autologin seems to be funky for me all day.
(05:08:35 PM) Brooke: The icons either aren't loading every time.
(05:08:57 PM) Brooke: Anyone else have this issue?
(05:09:09 PM) Danny_B|backup: +1

Most likely due to extra redirects per icon. If I can get a javascript version together tomorrow, I may add that.

MZ also opened bug 42610.

It looks like Safari may have issues with setting cookies on a 302 redirect: http://stackoverflow.com/questions/1144894/safari-doesnt-set-cookie-but-ie-ff-does, which would cause the issue.

I can't think of another solution at the moment that both fixes the csrf and doesn't use a redirect or a timeout.

I'll take a shot at implementing a javascript version of the AutoLogin tomorrow, so that will fix the issue for most people.

Seem to be getting a lot of fatals related to this on the cluster.

It can easily be worked around (look at the return value of WikiMap::getWiki() looking for null. I'm not sure what the result of removing the redirect may or may not have. I note theres another usage of WikiMap::getWiki() on the page, which I guess must be similarily susceptible.

[01-Nov-2012 23:52:06] Fatal error: Call to a member function getFullUrl() on a non-object at /usr/local/apache/common-local/php-1.21wmf3/extensions/CentralAuth/specials/SpecialAutoLogin.php on line 102
Server: mw49
Method: GET
URL: http://en.wikisource.org/wiki/Special:AutoLogin?token=8793dd92b91b0782cc52ee979b0ef47f
Backtrace:
#0 /usr/local/apache/common-local/php-1.21wmf3/extensions/CentralAuth/specials/SpecialAutoLogin.php(102): SpecialAutoLogin::execute()
#1 /usr/local/apache/common-local/php-1.21wmf3/includes/SpecialPage.php(599): SpecialAutoLogin->execute(NULL)
#2 /usr/local/apache/common-local/php-1.21wmf3/includes/SpecialPageFactory.php(497): SpecialPage->run(NULL)
#3 /usr/local/apache/common-local/php-1.21wmf3/includes/Wiki.php(291): SpecialPageFactory::executePath(Object(Title), Object(RequestContext))
#4 /usr/local/apache/common-local/php-1.21wmf3/includes/Wiki.php(555): MediaWiki->performRequest()
#5 /usr/local/apache/common-local/php-1.21wmf3/includes/Wiki.php(448): MediaWiki->main()
#6 /usr/local/apache/common-local/php-1.21wmf3/index.php(59): MediaWiki->run()
#7 /usr/local/apache/common-local/live-1.5/index.php(3): require('/usr/local/apac...')
#8 {main}

Ah, sorry about that. It probably won't fix the Safari issue, but it will help overall. I'll add that in. Although it's weird (to me) that we would be getting a SUL request from a wiki that doesn't have a WikiMap.

Also, it's weird that request didn't have a 'stage' in the url. Like the caller didn't get the CentralAuth update.

I can't log in to any wiki via Special:AutoLogin at the moment, all fail with "Sorry, we could not process your form submission due to a loss of session data".

I've reverted the local patch on fenari, now CentralAuth works again.

Thanks Tim. I was trying to get the javascript version working this morning, but no luck, so I was going to revert it today.

I don't want this to hold up the other fixes, so I'm going to announce the other ones today, and hopefully get this addressed next week.

Some notes on what I've been working through:

As I said in Comment #10, I think the solution needs a javascript and a noscript solution.

For the scripting solution,

  1. CentralAuthHooks writes out a <script src="url of AutoLogin on attached wiki">
  1. The attached wiki starts the session creation, sends CentralAuth Session cookies, and write back a jsonp-style call with the next token value (So SUL doesn't have to rely on a CORS trust relationship).
  1. The javascript function from the completed login page does an ajax call back to its origin to have the central wiki finish setting up the CentralAuth session. If the json comes back with a success message, the javascript adds an image on the page which points back to the attached wiki's stage 3.
  1. Attached wiki sends cookies for CentralAuth Token (if "Remember Me" was selected), and shows the image's content.

At any point in steps 2 or 3, if an error is returned, the javascript can attach a nice message notifying the user of the error.

I *think* this should work for the noscript login:

  1. CentralAuthHooks writes out an iframe instead of an image, pointing to stage 1 of AutoLogin on the attached wiki.
  1. AutoLogin stage 1 fills the iframe with a valid page (http 200), so cookies should be set from the attached wiki. Inside the iframe is an <img> tag, pointing to an "image" which is AutoLogin stage 2 on the central wiki.
  1. Stage 2 on the central wiki completes the session and redirects (http 302) the image request back to stage 3 on the attached wiki.

This should cause the cookies to get be set from the attached wiki.

Tim, what browser were you using?

Chris, is it confirmed that Safari fails to set cookies on a 302 ?
That may give problems to normal mediawiki login on safari, when there is not any html injected.

I made a small tool for detecting the browsers with this problem http://toolserver.org/~platonides/redirectcookies/redirectcookies.php (after pressing each button -different redirect types- you should be seeing a new cookie).

After preparing the next patch, we should test it in deployment-prep before going to production.

As of https://gerrit.wikimedia.org/r/#/c/64253/, this issues was (finally) fixed for good.

The SUL rewrite this year basically built on Platonides' original suggestion, so CentralAuth now has the concept of a central login wiki, which is pinged by anonymous visitors to check the status of their login. After all wikis were migrated to the new system, we were able to remove Special:AutoLogin, and this issue should be impossible to exploit now.

We'll make this public with the 1.21.3 release announcement.

(In reply to comment #10)

MZ also opened bug 42610.

Bug 41610. :-)