CVE-2020-25781: Missing Authorization (CWE-862)
value: 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.
CWE description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
CVSS v3 base score: 4.3
Published: 2020-09-30T21:15Z
Repository: https://github.com/mantisbt/mantisbt
Language: PHP
Files changed: core/file_api.php
Commit date: 2020-09-23 19:04:55 +0200
Commit message: 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

Diff: diff --git a/core/file_api.php b/core/file_api.php
index 2517aaba2a7..63de3e48782 100644
--- a/core/file_api.php
+++ b/core/file_api.php
@@ -216,16 +216,17 @@ function file_bug_has_attachments( $p_bug_id ) {
  * - {@see file_can_download_bugnote_attachments}
  *
  * @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';
 			$t_threshold_own = 'allow_view_own_attachments';
 			break;
@@ -238,11 +239,15 @@ 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;
 	}
 
 	$t_uploaded_by_me = auth_get_current_user_id() == $p_uploader_user_id;
@@ -260,10 +265,26 @@ function file_can_view_or_download( $p_action, $p_bug_id, $p_uploader_user_id )
  */
 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.
  *
  * @param integer $p_bug_id           A bug identifier.
  * @param integer $p_uploader_user_id The user who uploaded the attachment.
@@ -272,10 +293,26 @@ function file_can_view_bug_attachments( $p_bug_id, $p_uploader_user_id = null )
  */
 function file_can_download_bug_attachments( $p_bug_id, $p_uploader_user_id = null ) {
 	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.
  * @param integer $p_uploader_user_id A user identifier.
  * @return boolean

Vulnerable code: function file_can_view_or_download( $p_action, $p_bug_id, $p_uploader_user_id ) {
	$t_can_access = access_has_bug_level( $t_access_global, $p_bug_id );
Fixed code:  * @param int|null $p_bugnote_id        If specified, will check at bugnote level
function file_can_view_or_download( $p_action, $p_bug_id, $p_uploader_user_id, $p_bugnote_id = null ) {
	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 );
	}
/**
 * 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 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 );
}