From 7e2ff8448ac2f801e4ffc43c1149c94a8a760800 Mon Sep 17 00:00:00 2001 From: kink Date: Sat, 2 Dec 2006 15:11:09 +0000 Subject: [PATCH] - 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. git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@11968 7612ce4b-ef26-0410-bec9-ea0150e637f0 --- ChangeLog | 4 +++ functions/mime.php | 74 ++++++++++++++++++++++++++++++++++++++++++---- src/compose.php | 27 ++++++++++++++++- src/login.php | 8 ++--- src/mailto.php | 18 ++++++----- src/redirect.php | 10 +++---- src/right_main.php | 2 +- src/webmail.php | 10 ++++--- 8 files changed, 124 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9c9b99bc..070071e4 100644 --- a/ChangeLog +++ b/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) -------------------------------------- diff --git a/functions/mime.php b/functions/mime.php index 12c46a3c..2dccda47 100644 --- a/functions/mime.php +++ b/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, ''); - if ($ret == FALSE){ + + // workaround for 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\""); diff --git a/src/compose.php b/src/compose.php index 8bc437c8..93845a4c 100644 --- a/src/compose.php +++ b/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)) { diff --git a/src/login.php b/src/login.php index 50efeb3f..14ee6030 100644 --- a/src/login.php +++ b/src/login.php @@ -148,15 +148,15 @@ if (isset($hide_sm_attributions) && !$hide_sm_attributions) { _("By the SquirrelMail Project Team")."
\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(); diff --git a/src/mailto.php b/src/mailto.php index 0ad9224f..b27b1524 100644 --- a/src/mailto.php +++ b/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); -?> \ No newline at end of file +?> diff --git a/src/redirect.php b/src/redirect.php index f48de676..17623d81 100644 --- a/src/redirect.php +++ b/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. */ diff --git a/src/right_main.php b/src/right_main.php index 0e33723e..726b41d7 100644 --- a/src/right_main.php +++ b/src/right_main.php @@ -248,7 +248,7 @@ if (isset($aMailbox['FORWARD_SESSION'])) { } // do not use &, 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; diff --git a/src/webmail.php b/src/webmail.php index c8af2939..8124e562 100644 --- a/src/webmail.php +++ b/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'); \ No newline at end of file +$oTemplate->display('footer.tpl'); -- 2.25.1