Vulnerability: LimeSurvey before v3.17.14 allows reflected XSS for escalating privileges from a low-privileged account to, for example, SuperAdmin. This occurs in application/core/Survey_Common_Action.php,
Vulnerability ID: CVE-2019-16173
CWE: CWE-79
Severity score: 2.9
Project: https://github.com/LimeSurvey/LimeSurvey
Published: 2019-09-09
File: application/core/Survey_Common_Action.php
Commit message: Fixed issue: [security] #15204: Reflected XSS vulnerabilities - thanks to J. Greil from  the  SEC  Consult Vulnerability Lab
Dev: disable pseudo params for same param to be different : throw a 403
Commit author: Denis Chenu

Vulnerable version (before fix):
 private function _addPseudoParams($params)             'id' => 'iId',             'gid' => 'iGroupId',             'qid' => 'iQuestionId',             /* Unsure we set 'iSurveyId', 'iSurveyID','surveyid' to same final survey id */             /* priority is surveyid,surveyId,sid : surveyId=1&sid=2 set sid surveyid to 1 */             'sid' => array('iSurveyId', 'iSurveyID', 'surveyid'), // Old link use sid             'surveyId' => array('iSurveyId', 'iSurveyID', 'surveyid'), // PluginHelper->sidebody : if disable surveyId usage : broke API             'surveyid' => array('iSurveyId', 'iSurveyID', 'surveyid'), private function _addPseudoParams($params)         // Foreach pseudo, take the key, if it exists,         // Populate the values (taken as an array) as keys in params         // with that key's value in the params         // (only if that place is empty)         foreach ($pseudos as $key => $pseudo) {             if (isset($params[$key])) {                 $pseudo = (array) $pseudo;                 foreach ($pseudo as $pseud) {                     if (empty($params[$pseud])) {                         $params[$pseud] = $params[$key];                     }                 }             } private function renderCentralContents($sAction, $aViewUrls, $aData = [])                         // Output                     case 'output' :                         //// TODO : http://goo.gl/ABl5t5                          $content .= $viewUrl;                          if (isset($aViewUrls['afteroutput'])) {

Fixed version (after fix):
 private function _addPseudoParams($params)             'id' => 'iId',             'gid' => 'iGroupId',             'qid' => 'iQuestionId',             /* priority is surveyid,surveyId,sid : surveyId=1&sid=2 set iSurveyId to 1 */             'sid' => array('iSurveyId', 'iSurveyID', 'surveyid'), // Old link use sid             'surveyId' => array('iSurveyId', 'iSurveyID', 'surveyid'), // PluginHelper->sidebody : if disable surveyId usage : broke API             'surveyid' => array('iSurveyId', 'iSurveyID', 'surveyid'), private function _addPseudoParams($params)         // Foreach pseudo, take the key, if it exists,         // Populate the values (taken as an array) as keys in params         // with that key's value in the params         // Chek is 2 params are equal for security issue.         foreach ($pseudos as $key => $pseudo) {             if (isset($params[$key])) {                 $pseudo = (array) $pseudo;                 foreach ($pseudo as $pseud) {                     if (empty($params[$pseud])) {                         $params[$pseud] = $params[$key];                     } elseif($params[$pseud] != $params[$key]){                         // Throw error about multiple params (and if they are different) #15204                         throw new CHttpException(403, sprintf(gT("Invalid parameter %s (%s already set)"),$pseud,$key));                     }                 }             } private function renderCentralContents($sAction, $aViewUrls, $aData = [])                         // Output                     case 'output' :                         //// TODO : http://goo.gl/ABl5t5                         $content .= $viewUrl;                          if (isset($aViewUrls['afteroutput'])) {

Patch:
patch: @@ -106,8 +106,7 @@ private function _addPseudoParams($params)
             'id' => 'iId',
             'gid' => 'iGroupId',
             'qid' => 'iQuestionId',
-            /* Unsure we set 'iSurveyId', 'iSurveyID','surveyid' to same final survey id */
-            /* priority is surveyid,surveyId,sid : surveyId=1&sid=2 set sid surveyid to 1 */
+            /* priority is surveyid,surveyId,sid : surveyId=1&sid=2 set iSurveyId to 1 */
             'sid' => array('iSurveyId', 'iSurveyID', 'surveyid'), // Old link use sid
             'surveyId' => array('iSurveyId', 'iSurveyID', 'surveyid'), // PluginHelper->sidebody : if disable surveyId usage : broke API
             'surveyid' => array('iSurveyId', 'iSurveyID', 'surveyid'),
@@ -128,13 +127,16 @@ private function _addPseudoParams($params)
         // Foreach pseudo, take the key, if it exists,
         // Populate the values (taken as an array) as keys in params
         // with that key's value in the params
-        // (only if that place is empty)
+        // Chek is 2 params are equal for security issue.
         foreach ($pseudos as $key => $pseudo) {
             if (isset($params[$key])) {
                 $pseudo = (array) $pseudo;
                 foreach ($pseudo as $pseud) {
                     if (empty($params[$pseud])) {
                         $params[$pseud] = $params[$key];
+                    } elseif($params[$pseud] != $params[$key]){
+                        // Throw error about multiple params (and if they are different) #15204
+                        throw new CHttpException(403, sprintf(gT("Invalid parameter %s (%s already set)"),$pseud,$key));
                     }
                 }
             }
@@ -286,7 +288,6 @@ private function renderCentralContents($sAction, $aViewUrls, $aData = [])
                         // Output
                     case 'output' :
                         //// TODO : http://goo.gl/ABl5t5
-
                         $content .= $viewUrl;
 
                         if (isset($aViewUrls['afteroutput'])) {
path: application/core/Survey_Common_Action.php