CVE-2019-14749 (published 2019-08-07T17:15Z)

An issue was discovered in osTicket before 1.10.7 and 1.12.x before 1.12.1. CSV (aka Formula) injection exists in the export spreadsheets functionality. These spreadsheets are generated dynamically from unvalidated or unfiltered user input in the Name and Internal Notes fields in the Users tab, and the Issue Summary field in the tickets tab. This allows other agents to download data in a .csv file format or .xls file format. This is used as input for spreadsheet applications such as Excel and OpenOffice Calc, resulting in a situation where cells in the spreadsheets can contain input from an untrusted source. As a result, the end user who is accessing the exported spreadsheet can be affected.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.8, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-1236
cwe_name: Improper Neutralization of Formula Elements in a CSV File
description: The product saves user-provided information into a Comma-Separated Value (CSV) file, but it does not neutralize or incorrectly neutralizes special elements that could be interpreted as a command when the file is opened by a spreadsheet product.
extended_description: User-provided data is often saved to traditional databases.  This data can be exported to a CSV file, which allows users to read the data using spreadsheet software such as Excel, Numbers, or Calc.  This software interprets entries beginning with '=' as formulas, which are then executed by the spreadsheet software.  The software's formula language often allows methods to access hyperlinks or the local command line, and frequently allows enough characters to invoke an entire script. Attackers can populate data fields which, when saved to a CSV file, may attempt information exfiltration or other malicious activity when automatically executed by the spreadsheet software.
			

Fix information:
repo_url: https://github.com/osTicket/osTicket
commit_details: repo_url: https://github.com/osTicket/osTicket
author: JediKev
author_date: 2019-06-04 13:27:44-05:00
msg: security: CSV Formula Injection

This addresses a security issue discovered by Aishwarya Iyer where a User
can change their Full Name to a windows formula and when an Agent exports a
list of Users containing said User and opens the export file, the formula
will be executed on their computer (if it's windows of course). This adds a
new validator called `is_formula()` to all text fields disallowing the use
of the following characters `= + - @` at the beginning of text. This should
mitigate CSV Formula injections for any text field that allows user-input in
the system. To further prevent CSV Formula injections this adds an escape
mechanism to the Exporter that will escape any content matching the formula
regex with a single quote (as mentioned in many posts about this subject).
repository: repo_url: https://github.com/osTicket/osTicket
repo_name: osTicket/osTicket
description: The osTicket open source ticketing system official project repository, for versions 1.8 and later
repo_language: PHP
owner: osTicket
file_changes: filename: class.export.php
change_type: ModificationType.MODIFY
diff: @@ -356,7 +356,13 @@ class CsvResultsExporter extends ResultSetExporter {
         fputs($this->output, chr(0xEF) . chr(0xBB) . chr(0xBF));
         fputcsv($this->output, $this->getHeaders(), $delimiter);
         while ($row=$this->next())
-            fputcsv($this->output, $row, $delimiter);
+            fputcsv($this->output, array_map(
+                function($v){
+                    if (preg_match('/^[=\-+@].*/', $v))
+                        return "'".$v;
+                    return $v;
+                }, $row),
+            $delimiter);
 
         fclose($this->output);
     }

programming_language: PHP
filename: class.forms.php
change_type: ModificationType.MODIFY
diff: @@ -1300,7 +1300,8 @@ class TextboxField extends FormField {
         parent::validateEntry($value);
         $config = $this->getConfiguration();
         $validators = array(
-            '' =>       null,
+            '' =>       array(array('Validator', 'is_formula'),
+                __('Content cannot start with the following characters: = - + @')),
             'email' =>  array(array('Validator', 'is_valid_email'),
                 __('Enter a valid email address')),
             'phone' =>  array(array('Validator', 'is_phone'),
@@ -1379,6 +1380,29 @@ class TextareaField extends FormField {
         );
     }
 
+    function validateEntry($value) {
+        parent::validateEntry($value);
+        if (!$value)
+            return;
+        $config = $this->getConfiguration();
+        $validators = array(
+            '' =>       array(array('Validator', 'is_formula'),
+                __('Content cannot start with the following characters: = - + @')),
+        );
+        // Support configuration forms, as well as GUI-based form fields
+        if (!($valid = $this->get('validator')) && isset($config['validator']))
+            $valid = $config['validator'];
+        if (!isset($validators[$valid]))
+            return;
+        $func = $validators[$valid];
+        $error = $func[1];
+        if ($config['validator-error'])
+            $error = $this->getLocal('validator-error', $config['validator-error']);
+        if (is_array($func) && is_callable($func[0]))
+            if (!call_user_func($func[0], $value))
+                $this->_errors[] = $error;
+    }
+
     function hasSpecialSearch() {
         return false;
     }

programming_language: PHP
filename: class.user.php
change_type: ModificationType.MODIFY
diff: @@ -248,7 +248,7 @@ implements TemplateVariable {
         //Validate the form
         $valid = true;
         $filter = function($f) use ($thisstaff) {
-            return !isset($thisstaff) || $f->isRequiredForStaff();
+            return !isset($thisstaff) || $f->isRequiredForStaff() || $f->isVisibleToStaff();
         };
         if (!$form->isValid($filter))
             $valid  = false;

programming_language: PHP
filename: class.validator.php
change_type: ModificationType.MODIFY
diff: @@ -204,6 +204,11 @@ class Validator {
         return $error == '';
     }
 
+    static function is_formula($text, &$error='') {
+        if (!preg_match('/^[^=\+@-].*$/', $text))
+            $error = __('Content cannot start with the following characters: = - + @');
+        return $error == '';
+    }
 
     /*
      * check_ip

programming_language: PHP