Hi security guys.
Unuseful but funny preamble
During a very, very, very tiring nightly assembly of my local Wikimedia chapter (lasted nearly ~3.5 hours!) a beautiful volunteer sitting on the right part of the desk was waiting with me to discover if she was elected in the board of that Wikimedia chapter or not. I was trying to seem relaxed in this weird situation and to feel (seem) the most calm and relaxed in the room, I started investing some time in hardening our wiki's configuration as volunteer before the planned upgrade (T268515) and that's why I was here digging in VisualEditor's REST APIs and Parsoid.
Midnight spoiler: she was elected in the board; I've found a security issue; both celebrating more volunteer workload around the corner. Yeeeh!
Anyway in short I started working from this point:
And digging VisualEditor in our semi-private wiki, it seems we are affected by an ACL issue in Parsoid.
Description of the problem
OWASP A5: it seems anonymous users can access contents of private (potentially sensitive) MediaWiki pages via Parsoid's API.
I was able to reproduce in latest MediaWiki (while I was using 1:1.35.0-1), with VisualEditor enabled and with the extension Lockdown:
I reproduced this adopting the Debian GNU/Linux MediaWiki package in buster-backports but giving an eye on the primary branch I think this issue is unrelated from that package. I say this just to remember to report the issue to its package maintainer, @Legoktm.
POC
- Take a random MediaWiki 1.35 with VisualEditor enabled and Lockdown (or whatever other user rights extension, I suppose)
- Configure your wiki to be public, but have Lockdown to have a private private, like "PrivateNamespace:"
- Try to access a private/sensitive page and look how wonderful is to have your protection in action:
https://example.com/wiki/PrivateNamespace:VerySecretPage
Access denied
- Exploit the security issue via the VisualEditor's REST API to access that private/sensitive content:
https://example.com/rest.php/testwiki.lab.reyboz.it/v3/page/html/PrivateNamespace%3AVerySecretPage
< you see the content of the private page >
Cause
I'm somehow pretty sure that the Parsoid's PageHandler#execute() at the time of writing (2020) needs a missing lookup on the PermissionManager policies.
Workaround
A quick and dirty workaround is to immediately disable VisualEditor in your private or semi-private wiki just to tighten your pants.
Another workaround is to delete every page in your wiki that contains sensitive contents.
Proposed solution
I think that in PageHandler#execute() we should introduce a lookup to the PermissionManager#userCan() method triggering the userCan hook noticed by this kind of extensions.
This is a patch I redacted for VisualEditor Parsoid's PageHandler and that fixes this issue from my perspective:
diff --git a/extension/src/Rest/Handler/PageHandler.php b/extension/src/Rest/Handler/PageHandler.php index 8695f91dc..4bbb8c4bd 100644 --- a/extension/src/Rest/Handler/PageHandler.php +++ b/extension/src/Rest/Handler/PageHandler.php @@ -80,6 +80,17 @@ class PageHandler extends ParsoidHandler { ); } + // check if the user has enough privileges to see this page + // T270456 + $title = Title::newFromText( $attribs['pageName'] ); + $user = RequestContext::getMain()->getUser(); + $permissionManager = MediaWikiServices::getInstance()->getPermissionManager(); + if( !$permissionManager->userCan( 'read', $user, $title ) ) { + return $this->getResponseFactory()->createHttpError( 403, [ + 'message' => 'Not authorized', + ] ); + } + $pageConfig = $this->tryToCreatePageConfig( $attribs ); if ( $format === FormatHelper::FORMAT_WIKITEXT ) {
Now.
In the meanwhile I would suggest some obvious things like:
- invest some time in discovering how this is widespread in Parsoid
- checkout all the Wikimedia Foundation's private or semi-private wikis (with this kind of extensions) with VisualEditor enabled
- notify local Wikimedia chapters as well (whose wikis contain very sensitive data)
- notify third parts through usual mailing list
I know I can't submit a Gerrit patch but let me know if I can do anything else.