CVE-2020-8894 (published 2020-02-12T00:15Z)

An issue was discovered in MISP before 2.4.121. ACLs for discussion threads were mishandled in app/Controller/ThreadsController.php and app/Model/Thread.php.

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

Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/MISP/MISP
commit_details: repo_url: https://github.com/MISP/MISP
author: iglocska
author_date: 2020-02-08 10:34:23+01:00
msg: fix: [security] discussion thread ACL issues fixed

- as reported by Dawid Czarnecki
repository: repo_url: https://github.com/MISP/MISP
repo_name: MISP/MISP
description: MISP (core software) - Open Source Threat Intelligence and Sharing Platform
repo_language: PHP
owner: MISP
file_changes: filename: ThreadsController.php
change_type: ModificationType.MODIFY
diff: @@ -63,67 +63,32 @@ class ThreadsController extends AppController
 
     private function __view($thread_id, $eventView, $post_id)
     {
+        $conditions = array('id' => $thread_id);
         if ($eventView) {
-            $id = $thread_id;
-            // Show the discussion
-            $this->Thread->Behaviors->unload('SysLogLogable.SysLogLogable');
-            $params = array('conditions' => array('event_id' => $id),
-                    'recursive' => -1,
-                    'fields' => array('id', 'event_id', 'distribution', 'title', 'sharing_group_id', 'org_id')
-            );
-            $thread = $this->Thread->find('first', $params);
-            if (!empty($thread)) {
-                if (!$this->_isSiteAdmin()) {
-                    if ($thread['Thread']['distribution'] == 0 && $thread['Thread']['org_id'] != $this->Auth->user('org_id')) {
-                        throw new MethodNotAllowedException('Invalid Thread.');
-                    }
-                    if ($thread['Thread']['distribution'] == 4) {
-                        if (!$this->Thread->SharingGroup->checkIfAuthorised($this->Auth->user(), $thread['Thread']['sharing_group_id'])) {
-                            new NotFoundException('Invalid thread.');
-                        }
-                    }
-                }
-                $thread_id = $thread['Thread']['id'];
-            } else {
-                $thread_id = 0;
+            $event_id = $thread_id;
+            if (!$this->request->is('ajax')) {
+                $this->redirect(array('controller' => 'events', 'action' => 'view', $event_id));
             }
-            $this->set('currentEvent', $id);
+            $conditions = array('event_id' => $event_id);
+            $this->set('currentEvent', $event_id);
+            $this->set('event_id', $event_id);
+            $this->set('context', 'event');
         } else {
-            $this->Thread->recursive = -1;
-            $this->Thread->id = $thread_id;
-
-            //If the thread doesn't exist, throw exception
-            if (!$this->Thread->exists()) {
+            $this->set('context', 'thread');
+        }
+        $this->set('myuserid', $this->Auth->user('id'));
+        $thread = $this->Thread->find('first', array(
+            'conditions' => $conditions,
+            'recursive' => -1
+        ));
+        if (empty($thread)) {
+            if (!$eventView) {
                 throw new NotFoundException('Invalid thread.');
             }
-            $thread = $this->Thread->read();
-
-            // If the thread belongs to an event, we have to make sure that the event's distribution level hasn't changed.
-            // This is also a good time to update the thread's distribution level if that did happen.
-            if (!empty($thread['Thread']['event_id'])) {
-                $this->loadModel('Event');
-                $this->Event->id = $thread['Thread']['event_id'];
-                $this->Event->recursive = -1;
-                $this->Event->read(array('id', 'distribution', 'org_id', 'sharing_group_id'));
-                if ($this->Event->data['Event']['distribution'] != $thread['Thread']['distribution']) {
-                    $this->Thread->saveField('distribution', $this->Event->data['Event']['distribution']);
-                }
-                if ($this->Event->data['Event']['sharing_group_id'] != $thread['Thread']['sharing_group_id']) {
-                    $this->Thread->saveField('sharing_group_id', $this->Event->data['Event']['sharing_group_id']);
-                }
-                $this->set('event_id', $thread['Thread']['event_id']);
-            }
-
-            // If the user shouldn't be allowed to see the event send him away.
-            if (!$this->_isSiteAdmin()) {
-                if ($thread['Thread']['distribution'] == 0 && $thread['Thread']['org_id'] != $this->Auth->user('org_id')) {
-                    throw new MethodNotAllowedException('Invalid Thread.');
-                }
-                if ($thread['Thread']['distribution'] == 4) {
-                    if (!$this->Thread->SharingGroup->checkIfAuthorised($this->Auth->user(), $thread['Thread']['sharing_group_id'])) {
-                        new NotFoundException('Invalid thread.');
-                    }
-                }
+        } else {
+            $thread_id = $thread['Thread']['id'];
+            if (!$this->Thread->checkIfAuthorised($this->Auth->user(), $thread_id)) {
+                throw new NotFoundException('Invalid thread.');
             }
         }
         if ($thread_id) {
@@ -166,15 +131,6 @@ class ThreadsController extends AppController
                 $this->set('thread', $thread);
             }
         }
-        if ($eventView) {
-            $this->set('context', 'event');
-            if (!$this->request->is('ajax')) {
-                $this->redirect(array('controller' => 'events', 'action' => 'view', $id));
-            }
-        } else {
-            $this->set('context', 'thread');
-        }
-        $this->set('myuserid', $this->Auth->user('id'));
         if ($this->request->is('ajax')) {
             $this->layout = 'ajax';
             $this->render('/Elements/eventdiscussion');
@@ -185,7 +141,7 @@ class ThreadsController extends AppController
     {
         $this->loadModel('Posts');
         $this->loadModel('SharingGroup');
-        $sgids = $this->SharingGroup->fetchAllAuthorised($this->Auth->user);
+        $sgids = $this->SharingGroup->fetchAllAuthorised($this->Auth->user());
         $conditions = null;
         if (!$this->_isSiteAdmin()) {
             $conditions['AND']['OR'] = array(
@@ -206,7 +162,7 @@ class ThreadsController extends AppController
         }
         $conditions['AND'][] = array('Thread.post_count >' => 0);
         $this->paginate = array(
-                'conditions' => array($conditions),
+                'conditions' => $conditions,
                 'fields' => array('date_modified', 'date_created', 'org_id', 'distribution', 'title', 'post_count', 'sharing_group_id'),
                 'contain' => array(
                     'Post' =>array(

programming_language: PHP
filename: Thread.php
change_type: ModificationType.MODIFY
diff: @@ -38,4 +38,51 @@ class Thread extends AppModel
             return true;
         }
     }
+
+    // convenience method to check whether a user can see an event
+    public function checkIfAuthorised($user, $id)
+    {
+        if (!isset($user['id'])) {
+            throw new MethodNotAllowedException('Invalid user.');
+        }
+        $this->id = $id;
+        if (!$this->exists()) {
+            return false;
+        }
+        $thread = $this->find('first', array(
+            'conditions' => array('id' => $id),
+            'recursive' => -1
+        ));
+        if (!empty($thread['Thread']['event_id'])) {
+            $event = $this->Event->fetchEvent($user, array(
+                'eventid' => $thread['Thread']['event_id'],
+                'metadata' => true
+            ));
+            if (empty($event)) {
+                return false;
+            }
+            $event = $event[0];
+            // update the distribution if it diverged from the event
+            if (
+                $event['Event']['distribution'] != $thread['Thread']['distribution'] ||
+                $event['Event']['sharing_group_id'] != $thread['Thread']['sharing_group_id']
+            ) {
+                $this->Behaviors->unload('SysLogLogable.SysLogLogable');
+                $thread['Thread']['distribution'] = $event['Event']['distribution'];
+                $thread['Thread']['sharing_group_id'] = $event['Event']['sharing_group_id'];
+                $this->save($thread);
+            }
+            return !empty($event);
+        }
+        if ($user['Role']['perm_site_admin']) {
+            return true;
+        }
+        if ($thread['Thread']['org_id'] == $user['org_id'] || ($thread['Thread']['distribution'] > 0 && $thread['Thread']['distribution'] < 4)) {
+            return true;
+        }
+        if ($thread['Thread']['distribution'] == 4 && $this->SharingGroup->checkIfAuthorised($user, $thread['Thread']['sharing_group_id'])) {
+            return true;
+        }
+        return false;
+    }
 }

programming_language: PHP