CVE-2018-19908: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') (CWE-78)
value: An issue was discovered in MISP 2.4.9x before 2.4.99. In app/Model/Event.php (the STIX 1 import code), an unescaped filename string is used to construct a shell command. This vulnerability can be abused by a malicious authenticated user to execute arbitrary commands by tweaking the original filename of the STIX import.
CWE description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
CVSS v3 base score: 8.8
Published: 2018-12-06T16:29Z
Repository: https://github.com/MISP/MISP
Language: PHP
Files changed: app/Model/Event.php, app/files/scripts/stix2misp.py
Commit date: 2018-12-06 11:47:14 +0100
Commit message: fix: [internal] Handle the upload of original versions of ingested files via a helper function instead of leaving it to external tools

Diff: diff --git a/app/Model/Event.php b/app/Model/Event.php
index e6b89f6f6b..34503d836a 100755
--- a/app/Model/Event.php
+++ b/app/Model/Event.php
@@ -5077,21 +5077,22 @@ class Event extends AppModel
             $shell_command = $this->getPythonVersion() . ' ' . $scriptFile . ' ' . $filename;
             $output_path = $tempFilePath . '.json';
         } else {
             throw new MethodNotAllowedException('Invalid STIX version');
         }
-        $shell_command .=  ' ' . $original_file . ' ' . escapeshellarg(Configure::read('MISP.default_event_distribution')) . ' ' . escapeshellarg(Configure::read('MISP.default_attribute_distribution')) . ' 2>' . APP . 'tmp/logs/exec-errors.log';
+        $shell_command .=  ' ' . escapeshellarg(Configure::read('MISP.default_event_distribution')) . ' ' . escapeshellarg(Configure::read('MISP.default_attribute_distribution')) . ' 2>' . APP . 'tmp/logs/exec-errors.log';
         $result = shell_exec($shell_command);
         unlink($tempFilePath);
         if (trim($result) == '1') {
             $data = file_get_contents($output_path);
             $data = json_decode($data, true);
             unlink($output_path);
             $created_id = false;
             $validationIssues = false;
             $result = $this->_add($data, true, $user, '', null, false, null, $created_id, $validationIssues);
             if ($result) {
+                $this->add_original_file($tempFilePath, $original_filename, $created_id, 'STIX 1.1');
                 return $created_id;
             }
             return $validationIssues;
         } else {
             if (trim($result) == '2') {
@@ -5641,6 +5642,57 @@ class Event extends AppModel
                 }
             }
         }
         return $eventIdList;
     }
+
+    public function add_original_file($file_path, $original_filename, $event_id, $format)
+    {
+        if (!Configure::check('MISP.default_attribute_distribution') || Configure::read('MISP.default_attribute_distribution') === 'event') {
+            $distribution = 5;
+        } else {
+            $distribution = Configure::read('MISP.default_attribute_distribution');
+        }
+        $this->MispObject->create();
+        $object = array(
+            'name' => 'original-imported-file',
+            'meta-category' => 'file',
+            'description' => 'Object describing the original file used to import data in MISP.',
+            'template_uuid' => '4cd560e9-2cfe-40a1-9964-7b2e797ecac5',
+            'template_version' => '2',
+            'event_id' => $event_id,
+            'distribution' => $distribution
+        );
+        $this->MispObject->save($object);
+        $object_id = $this->MispObject->id;
+        $file = file_get_contents($file_path);
+        $attributes = array(
+            array(
+                'type' => 'attachment',
+                'category' => 'External analysis',
+                'to_ids' => false,
+                'event_id' => $event_id,
+                'distribution' => $distribution,
+                'object_relation' => 'imported-sample',
+                'value' => $original_filename,
+                'data' => base64_encode($file),
+                'object_id' => $object_id,
+            ),
+            array(
+                'type' => 'text',
+                'category' => 'Other',
+                'to_ids' => false,
+                'uuid' => '5c08f00d-2174-4ab7-ad0d-1b1a011fb688',
+                'event_id' => $event_id,
+                'distribution' => $distribution,
+                'object_id' => $object_id,
+                'object_relation' => 'format',
+                'value' => 'STIX 1.1'
+            )
+        );
+        foreach ($attributes as $attribute) {
+            $this->Attribute->create();
+            $this->Attribute->save($attribute);
+        }
+        return true;
+    }
 }
diff --git a/app/files/scripts/stix2misp.py b/app/files/scripts/stix2misp.py
index 7592887fe2..aac9dc9780 100644
--- a/app/files/scripts/stix2misp.py
+++ b/app/files/scripts/stix2misp.py
@@ -51,20 +51,18 @@ class StixParser():
     ################################################################################
 
     # Load data from STIX document, and other usefull data
     def load_event(self, args, filename, from_misp, stix_version):
         self.outputname = '{}.json'.format(filename)
-        if len(args) > 0 and args[0]:
-            self.add_original_file(filename, args[0], stix_version)
         try:
-            event_distribution = args[1]
+            event_distribution = args[0]
             if not isinstance(event_distribution, int):
                 event_distribution = int(event_distribution) if event_distribution.isdigit() else 5
         except IndexError:
             event_distribution = 5
         try:
-            attribute_distribution = args[2]
+            attribute_distribution = args[1]
             if attribute_distribution == 'event':
                 attribute_distribution = event_distribution
             elif not isinstance(attribute_distribution, int):
                 attribute_distribution = int(attribute_distribution) if attribute_distribution.isdigit() else event_distribution
         except IndexError:
@@ -79,20 +77,10 @@ class StixParser():
     def saveFile(self):
         eventDict = self.misp_event.to_json()
         with open(self.outputname, 'wt', encoding='utf-8') as f:
             f.write(eventDict)
 
-    def add_original_file(self, filename, original_filename, version):
-        with open(filename, 'rb') as f:
-            sample = base64.b64encode(f.read()).decode('utf-8')
-        original_file = MISPObject('original-imported-file')
-        original_file.add_attribute(**{'type': 'attachment', 'value': original_filename,
-                                       'object_relation': 'imported-sample', 'data': sample})
-        original_file.add_attribute(**{'type': 'text', 'object_relation': 'format',
-                                       'value': 'STIX {}'.format(version)})
-        self.misp_event.add_object(**original_file)
-
     # Load the mapping dictionary for STIX object types
     def load_mapping(self):
         self.attribute_types_mapping = {
             "AccountObjectType": self.handle_credential,
             'AddressObjectType': self.handle_address,

Vulnerable code:         $shell_command .=  ' ' . $original_file . ' ' . escapeshellarg(Configure::read('MISP.default_event_distribution')) . ' ' . escapeshellarg(Configure::read('MISP.default_attribute_distribution')) . ' 2>' . APP . 'tmp/logs/exec-errors.log';
        if len(args) > 0 and args[0]:
            self.add_original_file(filename, args[0], stix_version)
            event_distribution = args[1]
            attribute_distribution = args[2]
    def add_original_file(self, filename, original_filename, version):
        with open(filename, 'rb') as f:
            sample = base64.b64encode(f.read()).decode('utf-8')
        original_file = MISPObject('original-imported-file')
        original_file.add_attribute(**{'type': 'attachment', 'value': original_filename,
                                       'object_relation': 'imported-sample', 'data': sample})
        original_file.add_attribute(**{'type': 'text', 'object_relation': 'format',
                                       'value': 'STIX {}'.format(version)})
        self.misp_event.add_object(**original_file)

Fixed code:         $shell_command .=  ' ' . escapeshellarg(Configure::read('MISP.default_event_distribution')) . ' ' . escapeshellarg(Configure::read('MISP.default_attribute_distribution')) . ' 2>' . APP . 'tmp/logs/exec-errors.log';
                $this->add_original_file($tempFilePath, $original_filename, $created_id, 'STIX 1.1');

    public function add_original_file($file_path, $original_filename, $event_id, $format)
    {
        if (!Configure::check('MISP.default_attribute_distribution') || Configure::read('MISP.default_attribute_distribution') === 'event') {
            $distribution = 5;
        } else {
            $distribution = Configure::read('MISP.default_attribute_distribution');
        }
        $this->MispObject->create();
        $object = array(
            'name' => 'original-imported-file',
            'meta-category' => 'file',
            'description' => 'Object describing the original file used to import data in MISP.',
            'template_uuid' => '4cd560e9-2cfe-40a1-9964-7b2e797ecac5',
            'template_version' => '2',
            'event_id' => $event_id,
            'distribution' => $distribution
        );
        $this->MispObject->save($object);
        $object_id = $this->MispObject->id;
        $file = file_get_contents($file_path);
        $attributes = array(
            array(
                'type' => 'attachment',
                'category' => 'External analysis',
                'to_ids' => false,
                'event_id' => $event_id,
                'distribution' => $distribution,
                'object_relation' => 'imported-sample',
                'value' => $original_filename,
                'data' => base64_encode($file),
                'object_id' => $object_id,
            ),
            array(
                'type' => 'text',
                'category' => 'Other',
                'to_ids' => false,
                'uuid' => '5c08f00d-2174-4ab7-ad0d-1b1a011fb688',
                'event_id' => $event_id,
                'distribution' => $distribution,
                'object_id' => $object_id,
                'object_relation' => 'format',
                'value' => 'STIX 1.1'
            )
        );
        foreach ($attributes as $attribute) {
            $this->Attribute->create();
            $this->Attribute->save($attribute);
        }
        return true;
    }
            event_distribution = args[0]
            attribute_distribution = args[1]