Page MenuHomePhabricator

PushToWatch: classic CSRF (CVE-2020-35626)
Closed, ResolvedPublicSecurity

Description

While working on T268627: PushToWatch extension does not show anything in page footer as well as fixing some unrelated issues in that unmaintained codebase, I realized that the form (which currently requires no authentication whatsoever, thus rendering the extension 100% unsuitable for a public, world-editable wiki) shown on the page footer doesn't have an edit token, rendering the extension vulnerable to cross-site request forgeries.

Quick patch (on top of all the patches I currently have open in gerrit against this extension):

diff --git a/src/PushToWatch.php b/src/PushToWatch.php
index 9a7f9a2..dd3e6bf 100644
--- a/src/PushToWatch.php
+++ b/src/PushToWatch.php
@@ -85,6 +85,7 @@ class PushToWatch {
                                $output .= Html::rawElement( 'form', [ 'method' => 'post' ],
                                        wfMessage( 'pushtowatch' )->escaped() .
                                        wfMessage( 'word-separator' )->parse() .
+                                       Html::hidden( 'wpPushToWatchToken', RequestContext::getMain()->getUser()->getEditToken() ) .
                                        Html::submitButton( '', [ 'style' => 'display:none' ] ) .
                                        // @todo FIXME: give this element class="mw-autocomplete-user" and add the
                                        // 'mediawiki.userSuggest' ResourceLoader module to output to enable
@@ -107,14 +108,21 @@ class PushToWatch {
         */
        public static function onSkinAddFooterLinks( Skin $sk, string $key, array &$footerLinks ) {
                $title = $sk->getRelevantTitle();
+               $request = $sk->getRequest();
                $output = '<hr />';
+               $isTokenOK = $sk->getUser()->matchEditToken( $request->getVal( 'wpPushToWatchToken' ) );
 
                try {
-                       $user = $sk->getRequest()->getText( 'pushtowatch_user' );
-                       // FIXME: This destroys all usernames that contain other characters!
-                       $user = preg_replace( "#[^a-z]#i", '', $user );
-                       if ( $user ) {
-                               self::addToWatch( $title, $user );
+                       if ( $request->wasPosted() && $isTokenOK ) {
+                               $user = $request->getText( 'pushtowatch_user' );
+                               // FIXME: This destroys all usernames that contain other characters!
+                               $user = preg_replace( "#[^a-z]#i", '', $user );
+                               if ( $user ) {
+                                       self::addToWatch( $title, $user );
+                               }
+                       } elseif ( $request->wasPosted() && !$isTokenOK ) {
+                               // CSRF attempt or something...
+                               $output .= Html::errorBox( $sk->msg( 'sessionfailure' )->parse() );
                        }
                } catch ( Exception $e ) {
                        $output .= Html::errorBox( $skin->msg( 'pushtowatch-error', $user )->parse() );

Event Timeline

lgtm, although i have no idea why its stripping non alphabetical characters from the username.

Urbanecm changed the visibility from "Custom Policy" to "Public (No Login Required)".Nov 25 2020, 10:45 PM
Urbanecm changed the edit policy from "Custom Policy" to "All Users".

Change 644501 had a related patch set uploaded (by SBassett; owner: Jack Phoenix):
[mediawiki/extensions/PushToWatch@REL1_35] SECURITY: Fix CSRF vulnerability by requiring a valid edit token

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

Change 644501 abandoned by SBassett:
[mediawiki/extensions/PushToWatch@REL1_35] SECURITY: Fix CSRF vulnerability by requiring a valid edit token

Reason:
Apparently not applicable to REL1_35

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

sbassett renamed this task from PushToWatch: classic CSRF to PushToWatch: classic CSRF (CVE-2020-35626).Dec 22 2020, 8:49 PM