CVE-2022-29529: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: An issue was discovered in MISP before 2.4.158. There is stored XSS via the LinOTP login field.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2022-04-20T23:15Z
Repository: https://github.com/MISP/MISP
Language: Other
Files changed: app/View/Users/login.ctp
Commit date: 2022-04-17 18:56:45 +0200
Commit message: fix: [security] XSS in LinOTP login field fixed

- fixed a stored XSS in the LinOTP login
- also fixed invalid calls to check MISP settings from a javascript scope

- as reported by Dawid Czarnecki of Zigrin Security

Diff: diff --git a/app/View/Users/login.ctp b/app/View/Users/login.ctp
index a6ec52534687..6b9414a2e606 100644
--- a/app/View/Users/login.ctp
+++ b/app/View/Users/login.ctp
@@ -43,11 +43,11 @@
                 echo $this->Form->input('otp', array('autocomplete' => 'off', 'type' => 'password', 'label' => 'OTP'));
                 echo "<div class=\"clear\">";
                 echo sprintf(
                     '%s <a href="%s/selfservice" title="LinOTP Selfservice">LinOTP Selfservice</a> %s',
                     __('Visit'),
-                    Configure::read('LinOTPAuth.baseUrl'),
+                    h(Configure::read('LinOTPAuth.baseUrl')),
                     __('for the One-Time-Password selfservice.')
                 );
             }
         ?>
             <div class="clear">
@@ -90,11 +90,14 @@ $(function() {
 function submitLoginForm() {
     var $form = $('#UserLoginForm')
     var url = $form.attr('action')
     var email = $form.find('#UserEmail').val()
     var password = $form.find('#UserPassword').val()
-    if (!empty(Configure::read('LinOTPAuth')) && Configure::read('LinOTPAuth.enabled')) {
+    var LinOTPAuth = <?= empty(Configure::read('LinOTPAuth')) ? 'False' : 'True' ?>;
+    var LinOTPAuthEnabled = <?= empty(Configure::read('LinOTPAuth.enabled')) ? 'False' : 'True' ?>;
+
+    if (LinOTPAuth && LinOTPAuthEnabled) {
         var otp = $form.find('#UserOtp').val()
     }
     if (!$form[0].checkValidity()) {
         $form[0].reportValidity()
     } else {
@@ -105,11 +108,11 @@ function submitLoginForm() {
             }
             $('body').append($('<div id="temp" style="display: none"/>').append(formHTML))
             var $tmpForm = $('#temp form#UserLoginForm')
             $tmpForm.find('#UserEmail').val(email)
             $tmpForm.find('#UserPassword').val(password)
-            if (!empty(Configure::read('LinOTPAuth')) && Configure::read('LinOTPAuth.enabled')) {
+            if (LinOTPAuth && LinOTPAuthEnabled) {
                 $tmpForm.find('#UserOtp').val(otp)
             }
             $tmpForm.submit()
         })
     }

Vulnerable code:                     Configure::read('LinOTPAuth.baseUrl'),
    if (!empty(Configure::read('LinOTPAuth')) && Configure::read('LinOTPAuth.enabled')) {
            if (!empty(Configure::read('LinOTPAuth')) && Configure::read('LinOTPAuth.enabled')) {
Fixed code:                     h(Configure::read('LinOTPAuth.baseUrl')),
    var LinOTPAuth = <?= empty(Configure::read('LinOTPAuth')) ? 'False' : 'True' ?>;
    var LinOTPAuthEnabled = <?= empty(Configure::read('LinOTPAuth.enabled')) ? 'False' : 'True' ?>;

    if (LinOTPAuth && LinOTPAuthEnabled) {
            if (LinOTPAuth && LinOTPAuthEnabled) {