Page MenuHomePhabricator

RCE in Widgets extension (CVE-2020-35625)
Closed, ResolvedPublicSecurity

Description

Anyone with the ability to edit pages in the Widgets namespace can call any static function in any class defined in PHP or MediaWiki. Quite a few of these classes can do nefarious things, such as exfiltrate user data, change user passwords, read/write arbitrary files on the filesystem, or run arbitrary shell commands (running arbitrary shell commands or writing arbitrary files can both be used to achieve additional levels of code execution).

Proof of concept:

Page Widget:Test (requires 'editwidgets' permission to edit, by default only given to sysops and a "widget editors" group):

<!--{\MediaWiki\Shell\Shell::command('pwd')->execute()->getStdout()}-->

On some other page, invoking the widget via {{#widget:test}} will execute the above command on the shell and display the result on the page, in this case the current working directory that PHP is executing from (which is the root directory of the MediaWiki installation).

This extension is not deployed on WMF to the best of my knowledge but is deployed on a number of third-party wiki farms such as Fandom/Gamepedia.

Event Timeline

cc'ing ashley from ShoutWiki (if someone could add in some Fandom people as an FYI and so that they can patch before the issue is made public that would probably be good as well)

ashley added a subscriber: lcawte.

cc @lcawte as ShoutWiki's CTO as we have a handful of sites running this extension

if someone could add in some Fandom people as an FYI and so that they can patch before the issue is made public that would probably be good as well

Those people would probably be Grunny and/or Alexia.

This SMW query on WikiApiary returns 18 Gamepedia sites, and Gamepedia is a Wikia/Fandom property these days (though, AIUI, separate from the main Wikia codebase for the time being due to using the HydraWiki codebase as HydraWiki and main Wikia codebases are yet to be merged).

The following patch should resolve the issue. I will submit this to gerrit (effectively making this issue public) once this is reviewed and relevant people are patched.

diff -uN Widgets/extension.json WidgetsHardened/extension.json
--- Widgets/extension.json      2020-12-08 17:30:52.952984500 -0700
+++ WidgetsHardened/extension.json      2020-12-08 17:30:46.082927596 -0700
@@ -1,6 +1,6 @@
 {
        "name": "Widgets",
-       "version": "1.4.0",
+       "version": "1.4.1",
        "author": [
                "[https://www.sergeychernyshev.com Sergey Chernyshev]",
                "Yaron Koren",
@@ -73,7 +73,8 @@
        },
        "AutoloadClasses": {
                "WidgetInitializer": "WidgetInitializer.php",
-               "WidgetRenderer": "WidgetRenderer.php"
+               "WidgetRenderer": "WidgetRenderer.php",
+               "WidgetSecurity": "WidgetSecurity.php"
        },
        "callback": "WidgetInitializer::init",
        "load_composer_autoloader": true,
diff -uN Widgets/WidgetRenderer.php WidgetsHardened/WidgetRenderer.php
--- Widgets/WidgetRenderer.php  2020-12-08 17:30:52.952984500 -0700
+++ WidgetsHardened/WidgetRenderer.php  2020-12-08 17:30:46.081927588 -0700
@@ -29,26 +29,7 @@
                // registering custom Smarty plugins
                $smarty->addPluginsDir( __DIR__ . "/smarty_plugins/" );
 
-               $smarty->enableSecurity();
-               // These settings were for Smarty v2 - they don't seem to
-               // have an equivalent in Smarty v3.
-               /*
-               $smarty->security_settings = array(
-                       'IF_FUNCS' => array(
-                                       'is_array',
-                                       'isset',
-                                       'array',
-                                       'list',
-                                       'count',
-                                       'sizeof',
-                                       'in_array',
-                                       'true',
-                                       'false',
-                                       'null'
-                                       ),
-                       'MODIFIER_FUNCS' => array( 'validate' )
-               );
-               */
+               $smarty->enableSecurity( 'WidgetSecurity' );
 
                // Register the Widgets extension functions.
                $smarty->registerResource(
diff -uN Widgets/WidgetSecurity.php WidgetsHardened/WidgetSecurity.php
--- Widgets/WidgetSecurity.php  1969-12-31 17:00:00.000000000 -0700
+++ WidgetsHardened/WidgetSecurity.php  2020-12-08 17:30:46.081927588 -0700
@@ -0,0 +1,16 @@
+<?php
+
+class WidgetSecurity extends Smarty_Security {
+       public $php_handling = Smarty::PHP_REMOVE;
+       public $static_classes = null;
+       public $trusted_static_methods = null;
+       public $trusted_static_properties = null;
+       public $streams = null;
+       public $allow_constants = false;
+       public $allow_super_globals = false;
+       public $disabled_special_smarty_vars = [
+               'template',
+               'template_object',
+               'current_dir'
+       ];
+}

To apply the patch:

  1. Save the above patch file as widgets.patch
  2. cd /path/to/mediawiki/extensions/Widgets
  3. patch -p1 -u < /path/to/widgets.patch

To verify that the patch has been applied, check the version number for the Widgets extension in Special:Version; it should read 1.4.1.

Thanks for letting us know! Yeah, Gamepedia wikis are using it.

Hey, just wanted to let you know that the patch has been applied to Gamepedia wikis. It's fine from our perspective to move forward and make this issue/patch public. Thanks for the heads-up!

https://gerrit.wikimedia.org/r/c/mediawiki/extensions/Widgets/+/647749 merged into master. I'll submit a backport to REL1_35 and REL1_31 tomorrow (as those are the only two currently-supported versions of MediaWiki, don't see a real need to backport to any other branch).

Urbanecm changed the visibility from "Custom Policy" to "Public (No Login Required)".Dec 11 2020, 5:09 PM
Urbanecm changed the edit policy from "Custom Policy" to "All Users".

I published this task, as the patch is public now.

Documented on MediaWiki.org as the point of reference for wiki administrators. Thanks a lot for reporting and fixing the issue!

Ooops, I see that the patches are unmerged. I should have been more attentive. Now I officially published news about the issue without the fix in place. That's life I guess. In the future I personally would appreciate disclose only if the fix was merged. Otherwise I see no sense in doing so. Again this is just my personal opinion.

Change 648292 merged by jenkins-bot:
[mediawiki/extensions/Widgets@REL1_31] [SECURITY] Apply a stricter policy to smarty templates.

https://gerrit.wikimedia.org/r/648292

Ooops, I see that the patches are unmerged. I should have been more attentive. Now I officially published news about the issue without the fix in place. That's life I guess. In the future I personally would appreciate disclose only if the fix was merged. Otherwise I see no sense in doing so. Again this is just my personal opinion.

In master, the patch is merged. Furthermore, this issue has been public ever since the change was first uploaded to Gerrit.

Change 648289 merged by jenkins-bot:
[mediawiki/extensions/Widgets@REL1_35] [SECURITY] Apply a stricter policy to smarty templates.

https://gerrit.wikimedia.org/r/648289

Skizzerz claimed this task.

Ooops, I see that the patches are unmerged. I should have been more attentive. Now I officially published news about the issue without the fix in place. That's life I guess. In the future I personally would appreciate disclose only if the fix was merged. Otherwise I see no sense in doing so. Again this is just my personal opinion.

Yeah I was waiting until all of the backports were merged before asking for this task to be made public. I feel that it was made public a little bit too early as well. Backports to 1.31 and 1.35 have now been merged.

@sbassett I know you were involved in the previous security issue with this extension (T245850). Is there anything else we need to do for this one in order to get wiki admins notified, and do you think this merits a CVE?

Yeah I was waiting until all of the backports were merged before asking for this task to be made public. I feel that it was made public a little bit too early as well. Backports to 1.31 and 1.35 have now been merged.

Eh, once it's on gerrit, the whole world knows about it, so it's likely fine to make the related task public. The only times we (WMF) protect tasks are when:

  1. something in core or a bundled ext/skin is being held for a future security release
  2. folks are still in the process of disclosing a security issue and/or patch to relevant MediaWiki operators
  3. there is some other legal reason we cannot publicly disclose a task

@sbassett I know you were involved in the previous security issue with this extension (T245850). Is there anything else we need to do for this one in order to get wiki admins notified, and do you think this merits a CVE?

I can request a CVE for this one, sure. I'll update the task when I have this. And I'll definitely include this within the supplemental security release announcement (T263810), due out in another week or so. Re: notifying - the Security-Team would recommend a best-effort approach here, when those managing this issue feel they have done their best to contact affected MediaWiki operators.

Well done Skizzers.

Please who can work on T267132 ?

@Godman: Please don't add off-topic comments to tasks. Thanks.

sbassett renamed this task from RCE in Widgets extension to RCE in Widgets extension (CVE-2020-35625).Dec 22 2020, 8:51 PM