CVE-2021-41800 (published 2021-10-11T08:15Z)

MediaWiki before 1.36.2 allows a denial of service (resource consumption because of lengthy query processing time). Visiting Special:Contributions can sometimes result in a long running SQL query because PoolCounter protection is mishandled.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 5.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L)

Weakness information:
cwe_id: CWE-770
cwe_name: Allocation of Resources Without Limits or Throttling
description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
extended_description: Code frequently has to work with limited resources, so programmers must be careful to ensure that resources are not consumed too quickly, or too easily.  Without use of quotas, resource limits, or other protection mechanisms, it can be easy for an attacker to consume many resources by rapidly making many requests, or causing larger resources to be used than is needed. When too many resources are allocated, or if a single resource is too large, then it can prevent the code from working correctly, possibly leading to a denial of service.
         

Fix information:
repo_url: https://github.com/wikimedia/mediawiki
commit_details: repo_url: https://github.com/wikimedia/mediawiki
author: Chris Danis
author_date: 2021-09-23 16:21:05-04:00
msg: SECURITY: fix PoolCounter protection of Special:Contributions

The call to $pager->getNumRows() itself triggers execution of
the database query backing the page, so, that call must be inside
the callback given to PoolCounterWorkViaCallback.

CVE-2021-41800

Bug: T284419
Change-Id: I8b7b41a355be265389a4a8c9ea91301d4e23ae1b
repository: repo_url: https://github.com/wikimedia/mediawiki
repo_name: wikimedia/mediawiki
description: 🌻 The collaborative editing software that runs Wikipedia. Mirror from https://gerrit.wikimedia.org/g/mediawiki/core. See https://mediawiki.org/wiki/Developer_access for contributing.
repo_language: PHP
owner: wikimedia
file_changes: filename: SpecialContributions.php
change_type: ModificationType.MODIFY
diff: @@ -310,8 +310,6 @@ class SpecialContributions extends IncludableSpecialPage {
 				$limits = $this->getConfig()->get( 'RangeContributionsCIDRLimit' );
 				$limit = $limits[ IPUtils::isIPv4( $target ) ? 'IPv4' : 'IPv6' ];
 				$out->addWikiMsg( 'sp-contributions-outofrange', $limit );
-			} elseif ( !$pager->getNumRows() ) {
-				$out->addWikiMsg( 'nocontribs', $target );
 			} else {
 				// @todo We just want a wiki ID here, not a "DB domain", but
 				// current status of MediaWiki conflates the two. See T235955.
@@ -322,20 +320,24 @@ class SpecialContributions extends IncludableSpecialPage {
 					$poolKey .= 'u:' . $this->getUser()->getId();
 				}
 				$work = new PoolCounterWorkViaCallback( 'SpecialContributions', $poolKey, [
-					'doWork' => function () use ( $pager, $out ) {
-						# Show a message about replica DB lag, if applicable
-						$lag = $pager->getDatabase()->getSessionLagStatus()['lag'];
-						if ( $lag > 0 ) {
-							$out->showLagWarning( $lag );
+					'doWork' => function () use ( $pager, $out, $target ) {
+						if ( !$pager->getNumRows() ) {
+							$out->addWikiMsg( 'nocontribs', $target );
+						} else {
+							# Show a message about replica DB lag, if applicable
+							$lag = $pager->getDatabase()->getSessionLagStatus()['lag'];
+							if ( $lag > 0 ) {
+								$out->showLagWarning( $lag );
+							}
+
+							$output = $pager->getBody();
+							if ( !$this->including() ) {
+								$output = $pager->getNavigationBar() .
+									$output .
+									$pager->getNavigationBar();
+							}
+							$out->addHTML( $output );
 						}
-
-						$output = $pager->getBody();
-						if ( !$this->including() ) {
-							$output = $pager->getNavigationBar() .
-								$output .
-								$pager->getNavigationBar();
-						}
-						$out->addHTML( $output );
 					},
 					'error' => function () use ( $out ) {
 						$msg = $this->getUser()->isAnon()

programming_language: PHP