Page MenuHomePhabricator

CVE-2025-62658: SQL injection in WatchAnalytics through Special:ClearPendingReviews
Closed, ResolvedPublicSecurity

Description

The WatchAnalytics extension does not properly escape user-provided strings before inserting them into raw SQL, allowing for SQL injection. The clearreviews permission is required to exploit this, which is granted to the sysop group by default.

SQLI through page title field

Reproduction steps

  1. Go to /wiki/Special:ClearPendingReviews
  2. Enter any dates (e.g. 20251003184000) into the "Start Time" and "End Time" fields
  3. Enter '/**/OR/**/SLEEP(5)/**/OR/**/''/**/=/**/' into the "Page title like:" field
  4. Click "Preview"
  5. Observe that the page takes some time to load because the SLEEP(5) statement in the SQL query is executed

Cause

https://gerrit.wikimedia.org/g/mediawiki/extensions/WatchAnalytics/+/b9de8e12e1cbbf51a1d56edea04754d481e43851/specials/SpecialClearPendingReviews.php#120

SQLI through page category field

Reproduction steps

(Note that I slightly modified the SQL query locally to reproduce this since categorylinks.cl_to was removed in MW 1.44 and I'm using 1.45)

  1. Go to /wiki/Category:'/**/OR/**/SLEEP(5)/**/OR/**/''/**/%3D/**/' and create the page
  2. Go to /wiki/Special:ClearPendingReviews
  3. Enter any dates (e.g. 20251003184000) into the "Start Time" and "End Time" fields
  4. Enter '/**/OR/**/SLEEP(5)/**/OR/**/''/**/=/**/' into the "Select page category:" field
  5. Click "Preview"
  6. Observe that the page takes some time to load because the SLEEP(5) statement in the SQL query is executed

Cause

https://gerrit.wikimedia.org/g/mediawiki/extensions/WatchAnalytics/+/b9de8e12e1cbbf51a1d56edea04754d481e43851/specials/SpecialClearPendingReviews.php#117

Additional information

  • MediaWiki: 1.45.0-alpha
  • WatchAnalytics: b9de8e1
  • PHP: 8.3.14 (fpm-fcgi)
  • MySQL: 9.4.0

Event Timeline

SomeRandomDeveloper added a subscriber: Yaron_Koren.

Suggested patch:

Adding @Yaron_Koren to the task who seemingly is the only active maintainer of the extension.

@SomeRandomDeveloper - yes, I'm the closest thing this extension has to a maintainer. This looks like a great improvement! If you want correct attribution, etc. for this patch, feel free to create a Gerrit patch for it, and I will check it in; otherwise I will create it myself. Either way is fine with me.

Change #1193565 had a related patch set uploaded (by SomeRandomDeveloper; author: SomeRandomDeveloper):

[mediawiki/extensions/WatchAnalytics@master] SECURITY: Escape user input in SQL queries

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

Change #1193566 had a related patch set uploaded (by SomeRandomDeveloper; author: SomeRandomDeveloper):

[mediawiki/extensions/WatchAnalytics@REL1_44] SECURITY: Escape user input in SQL queries

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

Change #1193567 had a related patch set uploaded (by SomeRandomDeveloper; author: SomeRandomDeveloper):

[mediawiki/extensions/WatchAnalytics@REL1_43] SECURITY: Escape user input in SQL queries

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

(No backport for REL1_39 as that branch doesn't exist for this extension)

Change #1193567 merged by jenkins-bot:

[mediawiki/extensions/WatchAnalytics@REL1_43] SECURITY: Escape user input in SQL queries

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

Change #1193566 merged by jenkins-bot:

[mediawiki/extensions/WatchAnalytics@REL1_44] SECURITY: Escape user input in SQL queries

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

@SomeRandomDeveloper - thank you for these patches. The one for master is not going through because of the recent removal of the cl_to field from the categorylinks table - which presumably is also preventing commits in something like 50 other extensions that use cl_to (including some of my own). I need to modify my extensions to not query cl_to - although if you want to try your hand at fixing WatchAnalytics, feel free. (The use of cl_to in WatchAnalytics seems more involved than in most of the others.)

@SomeRandomDeveloper - thank you for these patches. The one for master is not going through because of the recent removal of the cl_to field from the categorylinks table - which presumably is also preventing commits in something like 50 other extensions that use cl_to (including some of my own). I need to modify my extensions to not query cl_to - although if you want to try your hand at fixing WatchAnalytics, feel free. (The use of cl_to in WatchAnalytics seems more involved than in most of the others.)

I'm currently looking into this and I've already fixed it in one place in WatchAnalytics, so assuming I can fix the others as well, I'll upload a patch soon

I fixed SpecialClearPendingReviews with

diff --git a/specials/SpecialClearPendingReviews.php b/specials/SpecialClearPendingReviews.php
index 60ee10f..4ab1ec4 100644
--- a/specials/SpecialClearPendingReviews.php
+++ b/specials/SpecialClearPendingReviews.php
@@ -115,13 +115,13 @@ class SpecialClearPendingReviews extends SpecialPage {
 
                if ( $category ) {
                        $quotedCategory = $dbw->addQuotes( $category );
-                       $conditions .= "c.cl_to=$quotedCategory AND ";
+                       $conditions .= "l.lt_title=$quotedCategory AND ";
                }
                if ( $page ) {
                        $conditions .= 'w.wl_title ' . $dbw->buildLike( $page, $dbw->anyString() ) . ' AND ';
                }
 
-               $tables = [ 'w' => 'watchlist', 'p' => 'page', 'c' => 'categorylinks' ];
+               $tables = [ 'w' => 'watchlist', 'p' => 'page', 'c' => 'categorylinks', 'l' => 'linktarget' ];
                $vars = [ 'w.*' ];
                $conditions .= "w.wl_notificationtimestamp IS NOT NULL AND w.wl_notificationtimestamp < $end AND w.wl_notificationtimestamp > $start";
                $join_conds = [
@@ -130,6 +130,9 @@ class SpecialClearPendingReviews extends SpecialPage {
                        ],
                        'c' => [
                                'LEFT JOIN', 'c.cl_from=p.page_id'
+                       ],
+                       'l' => [
+                               'LEFT JOIN', 'l.lt_id=c.cl_target_id'
                        ]
                ];

(feel free to take that diff, attribution doesn't really matter to me here)
but since I'm not familiar with the rest of the extension, I actually can't properly test it, so @Yaron_Koren if you could fix the other ones, that'd be great

@Yaron_Koren could you please fix CI for the extension or just force merge the patch? I would prefer not having unmerged SQLI patches on gerrit for multiple days, especially since most of your extensions use the master branch instead of release branches, so wiki admins installing this extension might clone the unfixed version

Change #1193565 merged by Paladox:

[mediawiki/extensions/WatchAnalytics@master] SECURITY: Escape user input in SQL queries

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

sbassett renamed this task from SQL injection in WatchAnalytics through Special:ClearPendingReviews to CVE-2025-62658: SQL injection in WatchAnalytics through Special:ClearPendingReviews.Oct 20 2025, 8:23 PM
sbassett triaged this task as Medium priority.
sbassett removed a project: Patch-For-Review.
sbassett changed the visibility from "Custom Policy" to "Public (No Login Required)".
sbassett changed the edit policy from "Custom Policy" to "All Users".
sbassett changed Risk Rating from N/A to Medium.