[imp] Horde v 5.2.22 vulnerability – obfuscation via HTML encoding – XSS payload

Pascal Rigaux pascal.rigaux at univ-paris1.fr
Mon Apr 14 16:17:29 UTC 2025


Hi,

On 24/03/2025 11:16, Jens Wahnes wrote:
> One solution I found to filter out the malicious content from emails like the one Nataša described was to tighten the code used to sanitize HTML in e-mails. This is found in the imp/lib/Mime/Viewer/Html.php file. The code in the big "switch" statement of the "_node" method, around line 435 or so, dealing with "case 'style'", can be extended to call "removeChild($node)" not only in the sub-case of 'text/css', as already present in the file, but also in the general case. 

NB:

the initial code
- is moving <style type="text/css"> tags as direct children of <head>, out of the HTML flow (and so out of <math>)
- is leaving <style> tags unchanged (hence the security issue)

the suggested change ( https://github.com/horde/imp/pull/15/commits/51c4173489477692527748f46d35b568df686868 )
- is completly ignoring <style> tags (only keeping <style type="text/css">)

I suggest also moving <style> with no "type" as direct children of <head>.
The resulting code:

         case 'style':
             switch (Horde_String::lower($node->getAttribute('type'))) {
             case '':
             case 'text/css':
                 $this->_imptmp['style'][] = str_replace(
                     array('<!--', '-->'),
                     '',
                     $node->nodeValue
                 );
                 break;
             }
             if ($node->parentNode) {
                 $node->parentNode->removeChild($node);
             }
             break;

NB: according to spec https://html.spec.whatwg.org/multipage/semantics.html#the-style-element , <style> with other "type"s should "return" (ie be ignored?).



An alternative fix is to use <iframe> sandbox attribute to disallow "allow-scripts".
We have the following change for a subset of our users, we will see if it has no drawbacks...

--- lib/Compose/Link.php
+++ lib/Compose/Link.php
@@ -134,7 +134,7 @@ class IMP_Compose_Link
       */
      public function composeLinkSimpleCallback($url)
      {
-        return "javascript:void(window.open('" . strval($url) . "','','width=820,height=610,status=1,scrollbars=yes,resizable=yes'))";
+        return strval($url);
      }

      /**
--- lib/Mime/Viewer/Html.php
+++ lib/Mime/Viewer/Html.php
@@ -94,8 +94,9 @@ class IMP_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
              if ($view == $registry::VIEW_SMARTMOBILE) {
                  $data['js'][] = '$("#imp-message-body a[href=\'#unblock-image\']").button()';
              }
+            $sandbox = 'sandbox="allow-downloads allow-popups allow-popups-to-escape-sandbox allow-same-origin"';

-            $data['data'] = '<div>' . _("Loading...") . '</div><iframe class="htmlMsgData" id="' . $uid . '" src="javascript:false" frameborder="0" style="display:none;height:auto;"></iframe>';
+            $data['data'] = '<div>' . _("Loading...") . '</div><iframe class="htmlMsgData" id="' . $uid . '" src="javascript:false" frameborder="0" style="display:none;height:auto;" '.$sandbox.'></iframe>';
              $data['type'] = 'text/html; charset=UTF-8';
              break;
          }
@@ -336,6 +337,7 @@ class IMP_Mime_Viewer_Html extends Horde_Mime_Viewer_Html
                      $clink = new IMP_Compose_Link($node->getAttribute('href'));
                      $node->setAttribute('href', $clink->link(true));
+                    $node->setAttribute('target', '_blank');
                  } elseif (!empty($this->_imptmp['inline']) &&
                            isset($url['fragment']) &&
                            empty($url['path']) &&



More information about the imp mailing list