Browse Source

- Security: close cross site scripting vulnerability in draft, compose
and mailto functionality [CVE-2006-6142].
- Security: work around an issue in Internet Explorer that would guess
the mime type of a file based on contents, not Content-Type header.

Thijs Kinkhorst 18 years ago
parent
commit
4991adee3b
8 changed files with 124 additions and 29 deletions
  1. 4 0
      ChangeLog
  2. 68 6
      functions/mime.php
  3. 26 1
      src/compose.php
  4. 4 4
      src/login.php
  5. 10 8
      src/mailto.php
  6. 5 5
      src/redirect.php
  7. 1 1
      src/right_main.php
  8. 6 4
      src/webmail.php

+ 4 - 0
ChangeLog

@@ -159,6 +159,10 @@ Version 1.5.2 - CVS
   - Add support for SpamAssassin's X-Spam-Status header (#1589520).
   - Added plugin on/off switch, which completely disables all plugins
     (optionally for one named user, otherwise for all users).
+  - Security: close cross site scripting vulnerability in draft, compose
+    and mailto functionality [CVE-2006-6142].
+  - Security: work around an issue in Internet Explorer that would guess
+    the mime type of a file based on contents, not Content-Type header.
 
 Version 1.5.1 (branched on 2006-02-12)
 --------------------------------------

+ 68 - 6
functions/mime.php

@@ -540,7 +540,11 @@ function buildAttachmentArray($message, $exclude_id, $mailbox, $id) {
         if ($where && $what) {
             $defaultlink .= '&where='. urlencode($where).'&what='.urlencode($what);
         }
-
+        // IE does make use of mime content sniffing. Forcing a download
+        // prohibit execution of XSS inside an application/octet-stream attachment
+        if ($type0 == 'application' && $type1 == 'octet-stream') {
+            $defaultlink .= '&absolute_dl=true';
+        }
         /* This executes the attachment hook with a specific MIME-type.
          * If that doesn't have results, it tries if there's a rule
          * for a more generic type. Finally, a hook for ALL attachment
@@ -1695,12 +1699,66 @@ function sq_fixatts($tagname,
 function sq_fixstyle($body, $pos, $message, $id, $mailbox){
     global $view_unsafe_images;
     $me = 'sq_fixstyle';
-    $ret = sq_findnxreg($body, $pos, '</\s*style\s*>');
-    if ($ret == FALSE){
+
+    // workaround for </style> in between comments
+    $iCurrentPos = $pos;
+    $content = '';
+    $sToken = '';
+    $bSucces = false;
+    $bEndTag = false;
+    for ($i=$pos,$iCount=strlen($body);$i<$iCount;++$i) {
+        $char = $body{$i};
+        switch ($char) {
+            case '<':
+                $sToken .= $char;
+                break;
+            case '/':
+                 if ($sToken == '<') {
+                    $sToken .= $char;
+                    $bEndTag = true;
+                 } else {
+                    $content .= $char;
+                 }
+                 break;
+            case '>':
+                 if ($bEndTag) {
+                    $sToken .= $char;
+                    if (preg_match('/\<\/\s*style\s*\>/i',$sToken,$aMatch)) {
+                        $newpos = $i + 1;
+                        $bSucces = true;
+                        break 2;
+                    } else {
+                        $content .= $sToken;
+                    }
+                    $bEndTag = false;
+                 } else {
+                    $content .= $char;
+                 }
+                 break;
+            case '!':
+                if ($sToken == '<') {
+                    // possible comment
+                    if (isset($body{$i+2}) && substr($body,$i,3) == '!--') {
+                        $i = strpos($body,'-->',$i+3);
+                        $sToken = '';
+                    }
+                } else {
+                    $content .= $char;
+                }
+                break;
+            default:
+                if ($bEndTag) {
+                    $sToken .= $char;
+                } else {
+                    $content .= $char;
+                }
+                break;
+        }
+    }
+    if ($bSucces == FALSE){
         return array(FALSE, strlen($body));
     }
-    $newpos = $ret[0] + strlen($ret[2]);
-    $content = $ret[1];
+
     /**
     * First look for general BODY style declaration, which would be
     * like so:
@@ -2389,11 +2447,15 @@ function SendDownloadHeaders($type0, $type1, $filename, $force, $filesize=0) {
 
             // This works for most types, but doesn't work with Word files
             header ("Content-Type: application/download; name=\"$filename\"");
-
+            // This is to prevent IE for MIME sniffing and auto open a file in IE
+            header ("Content-Type: application/force-download; name=\"$filename\"");
             // These are spares, just in case.  :-)
             //header("Content-Type: $type0/$type1; name=\"$filename\"");
             //header("Content-Type: application/x-msdownload; name=\"$filename\"");
             //header("Content-Type: application/octet-stream; name=\"$filename\"");
+        } else if ($isIE) {
+             // This is to prevent IE for MIME sniffing and auto open a file in IE
+             header ("Content-Type: application/force-download; name=\"$filename\"");
         } else {
             // another application/octet-stream forces download for Netscape
             header ("Content-Type: application/octet-stream; name=\"$filename\"");

+ 26 - 1
src/compose.php

@@ -69,7 +69,11 @@ sqgetGlobalVar('draft',$draft);
 sqgetGlobalVar('draft_id',$draft_id);
 sqgetGlobalVar('ent_num',$ent_num);
 sqgetGlobalVar('saved_draft',$saved_draft);
-sqgetGlobalVar('delete_draft',$delete_draft);
+
+if ( sqgetGlobalVar('delete_draft',$delete_draft) ) {
+    $delete_draft = (int)$delete_draft;
+}
+
 if ( sqgetGlobalVar('startMessage',$startMessage) ) {
     $startMessage = (int)$startMessage;
 } else {
@@ -110,6 +114,25 @@ if ( !sqgetGlobalVar('smaction',$action) )
     if ( sqgetGlobalVar('smaction_edit_new',$tmp) )   $action = 'edit_as_new';
 }
 
+/**
+ * Here we decode the data passed in from mailto.php.
+ */
+if ( sqgetGlobalVar('mailtodata', $mailtodata, SQ_GET) ) {
+    $trtable = array('to'       => 'send_to',
+                 'cc'           => 'send_to_cc',
+                 'bcc'          => 'send_to_bcc',
+                 'body'         => 'body',
+                 'subject'      => 'subject');
+    $mtdata = unserialize($mailtodata);
+    
+    foreach ($trtable as $f => $t) {
+        if ( !empty($mtdata[$f]) ) {
+            $$t = $mtdata[$f];
+        }
+    }
+    unset($mailtodata,$mtdata, $trtable);
+}
+
 /* Location (For HTTP 1.1 Header("Location: ...") redirects) */
 $location = get_location();
 /* Identities (fetch only once) */
@@ -333,6 +356,8 @@ if (sqsession_is_registered('session_expired_post')) {
 if (!isset($composesession)) {
     $composesession = 0;
     sqsession_register(0,'composesession');
+} else {
+    $composesession = (int)$composesession;
 }
 
 if (!isset($session) || (isset($newmessage) && $newmessage)) {

+ 4 - 4
src/login.php

@@ -148,15 +148,15 @@ if (isset($hide_sm_attributions) && !$hide_sm_attributions) {
                         _("By the SquirrelMail Project Team")."<br />\n";
 }
 
-if(sqgetGlobalVar('mailto', $mailto)) {
-    $rcptaddress = addHidden('mailto', $mailto);
+if(sqgetGlobalVar('mailtodata', $mailtodata)) {
+    $mailtofield = addHidden('mailtodata', $mailtodata);
 } else {
-    $rcptaddress = '';
+    $mailtofield = '';
 }
 
 $password_field = addPwField('secretkey');
 $login_extra = addHidden('js_autodetect_results', SMPREF_JS_OFF).
-               $rcptaddress .
+               $mailtofield .
                addHidden('just_logged_in', '1');
 
 session_write_close();

+ 10 - 8
src/mailto.php

@@ -38,6 +38,8 @@ $trtable = array('cc'           => 'send_to_cc',
                  'subject'      => 'subject');
 $url = '';
 
+$data = array();
+
 if(sqgetGlobalVar('emailaddress', $emailaddress)) {
     $emailaddress = trim($emailaddress);
     if(stristr($emailaddress, 'mailto:')) {
@@ -47,33 +49,33 @@ if(sqgetGlobalVar('emailaddress', $emailaddress)) {
         list($emailaddress, $a) = explode('?', $emailaddress, 2);
         if(strlen(trim($a)) > 0) {
             $a = explode('=', $a, 2);
-            $url .= $trtable[strtolower($a[0])] . '=' . urlencode($a[1]) . '&';
+            $data[strtolower($a[0])] = $a[1];
         }
     }
-    $url = 'send_to=' . urlencode($emailaddress) . '&' . $url;
+    $data['to'] = $emailaddress;
 
     /* CC, BCC, etc could be any case, so we'll fix them here */
     foreach($_GET as $k=>$g) {
         $k = strtolower($k);
         if(isset($trtable[$k])) {
             $k = $trtable[$k];
-            $url .= $k . '=' . urlencode($g) . '&';
+            $data[$k] = $g;
         }
     }
-    $url = substr($url, 0, -1);
 }
+sqsession_is_active();
 
 if($force_login == false && sqsession_is_registered('user_is_logged_in')) {
     if($compose_only == true) {
-        $redirect = 'compose.php?' . $url;
+        $redirect = 'compose.php?mailtodata=' . urlencode(serialize($data));
     } else {
-        $redirect = 'webmail.php?right_frame=compose.php?' . urlencode($url);
+        $redirect = 'webmail.php?mailtodata=' . urlencode(serialize($data));
     }
 } else {
-    $redirect = 'login.php?mailto=' . urlencode($url);
+    $redirect = 'login.php?mailtodata=' . urlencode(serialize($data));
 }
 
 session_write_close();
 header('Location: ' . get_location() . '/' . $redirect);
 
-?>
+?>

+ 5 - 5
src/redirect.php

@@ -35,8 +35,8 @@ sqGetGlobalVar('secretkey', $secretkey);
 if(!sqGetGlobalVar('squirrelmail_language', $squirrelmail_language) || $squirrelmail_language == '') {
     $squirrelmail_language = $squirrelmail_default_language;
 }
-if (!sqgetGlobalVar('mailto', $mailto)) {
-    $mailto = '';
+if (!sqgetGlobalVar('mailtodata', $mailtodata)) {
+    $mailtodata = '';
 }
 
 /* end of get globals */
@@ -161,9 +161,9 @@ if ( sqgetGlobalVar('session_expired_location', $session_expired_location, SQ_SE
     unset($session_expired_location);
 }
 
-if($mailto != '') {
-    $redirect_url  = $location . '/webmail.php?right_frame=compose.php&mailto=';
-    $redirect_url .= urlencode($mailto);
+if($mailtodata != '') {
+    $redirect_url  = $location . '/webmail.php?right_frame=compose.php&mailtodata=';
+    $redirect_url .= urlencode($mailtodata);
 }
 
 /* Write session data and send them off to the appropriate page. */

+ 1 - 1
src/right_main.php

@@ -248,7 +248,7 @@ if (isset($aMailbox['FORWARD_SESSION'])) {
         }
         // do not use &amp;, it will break the query string and $session will not be detected!!!
         $comp_uri = SM_PATH . 'src/compose.php?mailbox='. urlencode($mailbox).
-                    '&session='.$aMailbox['FORWARD_SESSION'];
+                    '&session='.urlencode($aMailbox['FORWARD_SESSION']);
         displayPageHeader($color, $mailbox, "comp_in_new('$comp_uri', $compose_width, $compose_height);", '');
     } else {
         $mailbox_cache[$account.'_'.$aMailbox['NAME']] = $aMailbox;

+ 6 - 4
src/webmail.php

@@ -31,8 +31,10 @@ if (!sqgetGlobalVar('mailbox', $mailbox)) {
 
 sqgetGlobalVar('right_frame', $right_frame, SQ_GET);
 
-if(!sqgetGlobalVar('mailto', $mailto)) {
-    $mailto = '';
+if(!sqgetGlobalVar('mailtodata', $mailtodata)) {
+    $mailtourl = 'mailtodata='.urlencode($mailtodata);
+} else {
+    $mailtourl = '';
 }
 
 // Determine the size of the left frame
@@ -95,7 +97,7 @@ switch($right_frame) {
         $right_frame_url = 'folders.php';
         break;
     case 'compose.php':
-        $right_frame_url = 'compose.php?' . $mailto;
+        $right_frame_url = 'compose.php?' . $mailtourl;
         break;
     case '':
         $right_frame_url = 'right_main.php';
@@ -116,4 +118,4 @@ $oTemplate->assign('right_frame_url', $right_frame_url);
 
 $oTemplate->display('webmail.tpl');
 
-$oTemplate->display('footer.tpl');
+$oTemplate->display('footer.tpl');