CVE-2021-32774: Cross-Site Request Forgery (CSRF) (CWE-352)
value: DataDump is a MediaWiki extension that provides dumps of wikis. Prior to commit 67a82b76e186925330b89ace9c5fd893a300830b, DataDump had no protection against CSRF attacks so requests to generate or delete dumps could be forged. The vulnerability was patched in commit 67a82b76e186925330b89ace9c5fd893a300830b. There are no known workarounds. You must completely disable DataDump.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 5.4
Published: 2021-07-20T01:15Z
Repository: https://github.com/miraheze/DataDump
Language: PHP
Files changed: includes/DataDumpPager.php, includes/specials/SpecialDataDump.php
Commit date: 2021-07-07 21:40:02 -0400
Commit message: Merge pull request from GHSA-29mh-4vhv-x8mr

* Add CSRF token check for generating dumps

* Update SpecialDataDump.php

* Add token for action=download

* Fix indendation

* Fix indendation

* fix indentation

* Convert to forms

* fix link generation

* don't check on download pt1

* don't check on download pt2

* rm stray code

* rm accidentally added character

* rm stray whitespace

* rm unused variable

* standardize

Co-authored-by: R4356th <61620631+R4356th@users.noreply.github.com>
Co-authored-by: The-Voidwalker <voidwalker.wikipedia@gmail.com>
Co-authored-by: R4356th <radman.siddiki@gmail.com>
Diff: diff --git a/includes/DataDumpPager.php b/includes/DataDumpPager.php
index c81dbcd..621d567 100644
--- a/includes/DataDumpPager.php
+++ b/includes/DataDumpPager.php
@@ -73,19 +73,39 @@ class DataDumpPager extends TablePager {
 			case 'dumps_size':
 				$formatted = htmlspecialchars(
 					$this->getLanguage()->formatSize( isset( $row->dumps_size ) ? $row->dumps_size : 0 ) );
 				break;
 			case 'dumps_delete':
-				$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
-
 				$query = [
 					'action' => 'delete',
 					'type' => $row->dumps_type,
 					'dump' => $row->dumps_filename
 				];
-
-				$formatted = $linkRenderer->makeLink( $this->pageTitle, wfMessage( 'datadump-delete-button' )->text(), [], $query );
+				$link = $this->pageTitle->getLinkURL( $query );
+				$element = Html::element(
+					'input', 
+					[
+						'type' => 'submit',
+						'title' => $this->pageTitle,
+						'value' => wfMessage('datadump-delete-button')->text()
+					]
+				);
+				$token = Html::element(
+					'input',
+					[
+						'type' => 'hidden',
+						'name' => 'token',
+						'value' => $this->getUser()->getEditToken()
+					]
+				);
+				$formatted = Html::openElement(
+					'form',
+					[
+						'action' => $link,
+						'method' => 'POST'
+					]
+				) . $element . $token . Html::closeElement('form');
 				break;
 			default:
 				$formatted = "Unable to format $name";
 				break;
 		}
@@ -202,10 +222,12 @@ class DataDumpPager extends TablePager {
 			$user = $this->getContext()->getUser();
 
 			$perm = $dataDumpConfig[$type]['permissions']['generate'];
 			if ( !$this->permissionManager->userHasRight( $user, $perm) ) {
 				throw new PermissionsError( $perm );
+			} elseif ( !$user->matchEditToken( $this->getContext()->getRequest()->getText( 'wpEditToken' ) ) ) {
+				return;
 			}
 
 			if ( $this->getGenerateLimit( $type ) ) {
 				$fileName = $dbName . '_' . $type . '_' .
 					bin2hex( random_bytes( 10 ) ) .
diff --git a/includes/specials/SpecialDataDump.php b/includes/specials/SpecialDataDump.php
index 89ea552..49a6ae9 100644
--- a/includes/specials/SpecialDataDump.php
+++ b/includes/specials/SpecialDataDump.php
@@ -28,10 +28,12 @@ class SpecialDataDump extends SpecialPage {
 		$this->checkPermissions();
 
 		$out = $this->getOutput();
 	
 		$request = $this->getRequest();
+		
+		$user = $this->getUser();
 
 		$dataDumpConfig = $this->config->get( 'DataDump' );
 		if ( !$dataDumpConfig ) {
 			$out->addWikiMsg( 'datadump-not-configured' );
 			return;
@@ -50,11 +52,15 @@ class SpecialDataDump extends SpecialPage {
 			$type = $request->getVal( 'type' );
 
 			if ( $action === 'download' && $dump ) {
 				$this->doDownload( $dump );
 			} elseif ( $action === 'delete' && $type && $dump ) {
-				$this->doDelete( $type, $dump );
+				if ( $user->matchEditToken($request->getVal('token'))) {
+					$this->doDelete( $type, $dump );
+				} else { 
+					$out->addWikiMsg( 'sessionfailure' );
+				}
 			}
 		}
 
 		$pager = new DataDumpPager( $this->getContext(), $this->getPageTitle() );
 

Vulnerable code: 				$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();


				$formatted = $linkRenderer->makeLink( $this->pageTitle, wfMessage( 'datadump-delete-button' )->text(), [], $query );
				$this->doDelete( $type, $dump );
Fixed code: 				$link = $this->pageTitle->getLinkURL( $query );
				$element = Html::element(
					'input', 
					[
						'type' => 'submit',
						'title' => $this->pageTitle,
						'value' => wfMessage('datadump-delete-button')->text()
					]
				);
				$token = Html::element(
					'input',
					[
						'type' => 'hidden',
						'name' => 'token',
						'value' => $this->getUser()->getEditToken()
					]
				);
				$formatted = Html::openElement(
					'form',
					[
						'action' => $link,
						'method' => 'POST'
					]
				) . $element . $token . Html::closeElement('form');
			} elseif ( !$user->matchEditToken( $this->getContext()->getRequest()->getText( 'wpEditToken' ) ) ) {
				return;
		
		$user = $this->getUser();
				if ( $user->matchEditToken($request->getVal('token'))) {
					$this->doDelete( $type, $dump );
				} else { 
					$out->addWikiMsg( 'sessionfailure' );
				}