CVE-2020-25781 (published 2020-09-30T21:15Z)

An issue was discovered in file_download.php in MantisBT before 2.24.3. Users without access to view private issue notes are able to download the (supposedly private) attachments linked to these notes by accessing the corresponding file download URL directly.

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

Weakness information:
cwe_id: CWE-862
cwe_name: Missing Authorization
description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are not applied, users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/mantisbt/mantisbt
commit_details: repo_url: https://github.com/mantisbt/mantisbt
author: Damien Regad
author_date: 2020-09-12 22:09:06+02:00
msg: Functions to check view/download ability at bugnote level

2 new File API functions:
- file_can_view_bugnote_attachments()
- file_can_download_bugnote_attachments

Prerequisite to fix issue #27039
repository: repo_url: https://github.com/mantisbt/mantisbt
repo_name: mantisbt/mantisbt
description: Mantis Bug Tracker (MantisBT)
repo_language: PHP
owner: mantisbt
file_changes: filename: file_api.php
change_type: ModificationType.MODIFY
diff: @@ -218,12 +218,13 @@ function file_bug_has_attachments( $p_bug_id ) {
  * @param string   $p_action            'view' or 'download'
  * @param int      $p_bug_id            A bug identifier
  * @param int      $p_uploader_user_id  The user who uploaded the attachment
+ * @param int|null $p_bugnote_id        If specified, will check at bugnote level
  *
  * @return bool
  *
  * @internal Should not be used outside of File API.
  */
-function file_can_view_or_download( $p_action, $p_bug_id, $p_uploader_user_id ) {
+function file_can_view_or_download( $p_action, $p_bug_id, $p_uploader_user_id, $p_bugnote_id = null ) {
 	switch( $p_action ) {
 		case 'view':
 			$t_threshold_global = 'view_attachments_threshold';
@@ -240,7 +241,11 @@ function file_can_view_or_download( $p_action, $p_bug_id, $p_uploader_user_id )
 	$t_project_id = bug_get_field( $p_bug_id, 'project_id' );
 	$t_access_global = config_get( $t_threshold_global,null, null, $t_project_id );
 
-	$t_can_access = access_has_bug_level( $t_access_global, $p_bug_id );
+	if( $p_bugnote_id === null ) {
+		$t_can_access = access_has_bug_level( $t_access_global, $p_bug_id );
+	} else {
+		$t_can_access = access_has_bugnote_level( $t_access_global, $p_bugnote_id );
+	}
 	if( $t_can_access ) {
 		return true;
 	}
@@ -262,6 +267,22 @@ function file_can_view_bug_attachments( $p_bug_id, $p_uploader_user_id = null )
 	return file_can_view_or_download( 'view', $p_bug_id, $p_uploader_user_id );
 }
 
+/**
+ * Check if the current user can view attachments for the specified bug note.
+ *
+ * @param integer $p_bugnote_id       A bugnote identifier.
+ * @param integer $p_uploader_user_id The user who uploaded the attachment.
+ *
+ * @return boolean
+ */
+function file_can_view_bugnote_attachments( $p_bugnote_id, $p_uploader_user_id = null ) {
+	if( $p_bugnote_id == 0 ) {
+		return true;
+	}
+	$t_bug_id = bugnote_get_field( $p_bugnote_id, 'bug_id' );
+	return file_can_view_or_download( 'view', $t_bug_id, $p_uploader_user_id );
+}
+
 /**
  * Check if the current user can download attachments for the specified bug.
  *
@@ -274,6 +295,22 @@ function file_can_download_bug_attachments( $p_bug_id, $p_uploader_user_id = nul
 	return file_can_view_or_download( 'download', $p_bug_id, $p_uploader_user_id );
 }
 
+/**
+ * Check if the current user can download attachments for the specified bug note.
+ *
+ * @param integer $p_bugnote_id       A bugnote identifier.
+ * @param integer $p_uploader_user_id The user who uploaded the attachment.
+ *
+ * @return boolean
+ */
+function file_can_download_bugnote_attachments( $p_bugnote_id, $p_uploader_user_id = null ) {
+	if( $p_bugnote_id == 0 ) {
+		return true;
+	}
+	$t_bug_id = bugnote_get_field( $p_bugnote_id, 'bug_id' );
+	return file_can_view_or_download( 'download', $t_bug_id, $p_uploader_user_id, $p_bugnote_id );
+}
+
 /**
  * Check if the current user can delete attachments from the specified bug.
  * @param integer $p_bug_id           A bug identifier.

programming_language: PHP
repo_url: https://github.com/mantisbt/mantisbt
commit_details: repo_url: https://github.com/mantisbt/mantisbt
author: Damien Regad
author_date: 2020-09-12 22:21:09+02:00
msg: Check ability to download attachments at bugnote level

This prevents users authorized to download attachments but not to view
private bugnotes, from accessing files attached to a private note via
`file_download.php?file_id={FILE_ID}&type=bug` (CVE-2020-25781).

Includes some minor code cleanup in file_get_visible_attachments():
- use a foreach loop
- reuse variables instead of derefenrcing array

Fixes #27039
repository: repo_url: https://github.com/mantisbt/mantisbt
repo_name: mantisbt/mantisbt
description: Mantis Bug Tracker (MantisBT)
repo_language: PHP
owner: mantisbt
file_changes: filename: file_api.php
change_type: ModificationType.MODIFY
diff: @@ -451,31 +451,21 @@ function file_get_visible_attachments( $p_bug_id ) {
 
 	$t_preview_text_ext = config_get( 'preview_text_extensions' );
 	$t_preview_image_ext = config_get( 'preview_image_extensions' );
-	$t_attachments_view_threshold = config_get( 'view_attachments_threshold' );
 
 	$t_image_previewed = false;
-	for( $i = 0;$i < $t_attachments_count;$i++ ) {
-		$t_row = $t_attachment_rows[$i];
+	foreach( $t_attachment_rows as $t_row ) {
 		$t_user_id = (int)$t_row['user_id'];
+		$t_attachment_note_id = (int)$t_row['bugnote_id'];
 
-		# This covers access checks for issue attachments
-		if( !file_can_view_bug_attachments( $p_bug_id, $t_user_id ) ) {
+		if( !file_can_view_bug_attachments( $p_bug_id, $t_user_id )
+		|| !file_can_view_bugnote_attachments( $t_attachment_note_id, $t_user_id )
+		) {
 			continue;
 		}
 
-		# This covers access checks for issue note attachments
-		$t_attachment_note_id = (int)$t_row['bugnote_id'];
-		if( $t_attachment_note_id !== 0 ) {
-			if( bugnote_get_field( $t_attachment_note_id, 'view_state' ) != VS_PUBLIC ) {
-				if( !access_has_bugnote_level( $t_attachments_view_threshold, $t_attachment_note_id ) ) {
-					continue;
-				}
-			}
-		}
-
 		$t_id = (int)$t_row['id'];
 		$t_filename = $t_row['filename'];
-		$t_filesize = $t_row['filesize'];
+		$t_filesize = (int)$t_row['filesize'];
 		$t_diskfile = file_normalize_attachment_path( $t_row['diskfile'], bug_get_field( $p_bug_id, 'project_id' ) );
 		$t_date_added = $t_row['date_added'];
 
@@ -483,14 +473,14 @@ function file_get_visible_attachments( $p_bug_id ) {
 		$t_attachment['id'] = $t_id;
 		$t_attachment['user_id'] = $t_user_id;
 		$t_attachment['display_name'] = file_get_display_name( $t_filename );
-		$t_attachment['size'] = (int)$t_filesize;
+		$t_attachment['size'] = $t_filesize;
 		$t_attachment['date_added'] = $t_date_added;
 		$t_attachment['diskfile'] = $t_diskfile;
 		$t_attachment['file_type'] = $t_row['file_type'];
-		$t_attachment['bugnote_id'] = (int)$t_row['bugnote_id'];
+		$t_attachment['bugnote_id'] = $t_attachment_note_id;
 
-		$t_attachment['can_download'] = file_can_download_bug_attachments( $p_bug_id, (int)$t_row['user_id'] );
-		$t_attachment['can_delete'] = file_can_delete_bug_attachments( $p_bug_id, (int)$t_row['user_id'] );
+		$t_attachment['can_download'] = file_can_download_bug_attachments( $p_bug_id, $t_user_id );
+		$t_attachment['can_delete'] = file_can_delete_bug_attachments( $p_bug_id, $t_user_id );
 
 		if( $t_attachment['can_download'] ) {
 			$t_attachment['download_url'] = 'file_download.php?file_id=' . $t_id . '&type=bug';

programming_language: PHP
filename: file_download.php
change_type: ModificationType.MODIFY
diff: @@ -110,7 +110,9 @@ if( $f_type == 'bug' ) {
 # Check access rights
 switch( $f_type ) {
 	case 'bug':
-		if( !file_can_download_bug_attachments( $v_bug_id, (int)$v_user_id ) ) {
+		if( !file_can_download_bug_attachments( $v_bug_id, $v_user_id )
+		|| !file_can_download_bugnote_attachments( $v_bugnote_id, $v_user_id )
+		) {
 			access_denied();
 		}
 		break;

programming_language: PHP