Author: Badcode, sebao (知道創宇404安全實驗室)

Date: 2017-03-17

0x00 漏洞概述

1. 漏洞簡介

Roundcube 是一款被廣泛使用的開源的電子郵件程序,在全球范圍內有很多組織和公司都在使用。 在服務器上成功安裝 Roundcube 之后,它會提供給用戶一個web 接口,通過驗證的用戶就可以通過 Web 瀏覽器收發電子郵件。 1.1.8 版本之前和 1.2.4 版本之前的 Roundcube 郵件正文展示中存在存儲型跨站腳本漏洞。官方已發布升級公告

2. 影響版本

1.1.x < 1.1.8 1.2.x < 1.2.4

0x01 漏洞復現

Payload來自.mario

payload

使用telnet 以html的形式發送一封包含 payload 的郵件到目標郵箱中,或者直接使用Roundcube發送一份html的郵件,發送的時候用burpsuite抓包將_message的值改成payload即可(也可使用其他方式發送郵件, 要注意的是 payload 是否會被轉義)。

telnet

當用戶查看郵件即可觸發跨站腳本漏洞。

0x02 漏洞分析

首先看一下整個處理的流程,當用戶查看郵件即action=show時,index.php會包含/program/steps/mail/show.inc,這里會調用$OUTPUT->send('message',false),跳到/program/include/rcmail_output_html.php,在前端的輸出頁面就是在這里產生的。在這里會調用xml_command()這個函數循環遍歷模板輸出所需的數據(label,button,message headers,message body等),拼接返回。其中取message body時會跳到rcmail_message_body函數。

先看下rcmail_message_body函數,在/program/steps/mail/fun.inc1169-1329行。

function rcmail_message_body($attrib)
{
  ......

                // fetch part body
                $body = $MESSAGE->get_part_body($part->mime_id, true);

                // message is cached but not exists (#1485443), or other error
                if ($body === false) {
                    rcmail_message_error($MESSAGE->uid);
                }

                $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix',
                    array('part' => $part, 'prefix' => ''));

                $body = rcmail_print_body($body, $part, array('safe' => $safe_mode, 'plain' => !$RCMAIL->config->get('prefer_html')));

                if ($part->ctype_secondary == 'html') {
                    $container_id = 'message-htmlpart' . (++$part_no);
                    $body         = rcmail_html4inline($body, $container_id, 'rcmBody', $attrs, $safe_mode);
                    $div_attr     = array('class' => 'message-htmlpart', 'id' => $container_id);
                    $style        = array();

                    if (!empty($attrs)) {
                        foreach ($attrs as $a_idx => $a_val)
                            $style[] = $a_idx . ': ' . $a_val;
                        if (!empty($style))
                            $div_attr['style'] = implode('; ', $style);
                    }

                    $out .= html::div($div_attr, $plugin['prefix'] . $body);
                }
                else
                    $out .= html::div('message-part', $plugin['prefix'] . $body);
            }
        }
    }
 ......

    return html::div($attrib, $out);
}

ctype_secondary的值是郵件的Content-type的第二部分(text/html),當ctype_secondary為html時,也就是郵件是html類型的,會調用rcmail_html4inline函數對郵件的內容進行處理。這就是為什么要以html的形式發送郵件的原因。跟進到rcmail_html4inline函數

function rcmail_html4inline($body, $container_id, $body_class='', &$attributes=null, $allow_remote=false)
{
    $last_style_pos = 0;
    $cont_id        = $container_id . ($body_class ? ' div.'.$body_class : '');

    // find STYLE tags
    while (($pos = stripos($body, '<style', $last_style_pos)) && ($pos2 = stripos($body, '</style>', $pos))) {
        $pos = strpos($body, '>', $pos) + 1;
        $len = $pos2 - $pos;

        // replace all css definitions with #container [def]
        $styles = substr($body, $pos, $len);
        $styles = rcube_utils::mod_css_styles($styles, $cont_id, $allow_remote);

        $body = substr_replace($body, $styles, $pos, $len);
        $last_style_pos = $pos2 + strlen($styles) - $len;
    }

    ......
    return $body;

可以看到,當郵件內容包含style標簽時,會把style標簽內的內容當成css樣式,調用mod_css_styles函數處理,過濾css樣式中的危險標簽。這也是整個漏洞觸發的關鍵點,style標簽的價值就體現在這。繼續跟進

    public static function mod_css_styles($source, $container_id, $allow_remote=false)
    {
        $last_pos = 0;
        $replacements = new rcube_string_replacer;

        // ignore the whole block if evil styles are detected
        $source   = self::xss_entity_decode($source);
        $stripped = preg_replace('/[^a-z\(:;]/i', '', $source);
        $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\(' : '');

        if (preg_match("/$evilexpr/i", $stripped)) {
            return '/* evil! */';
        }

     ......
        return $source;
    }

到了此處,可以看到,對$source調用了xss_entity_decode函數處理,后面的代碼都是對$source進行判斷和過濾。

    public static function xss_entity_decode($content)
    {
        $out = html_entity_decode(html_entity_decode($content));
        $out = preg_replace_callback('/\\\([0-9a-f]{4})/i',
            array(self, 'xss_entity_decode_callback'), $out);
        $out = preg_replace('#/\*.*\*/#Ums', '', $out);

        return $out;
    }

xss_entity_decode對css的內容進行解碼,對&lt;img/src=x onerror=alert(1)//進行解碼,返回<img/src=x onerror=alert(1)//

原路返回,返回到rcmail_message_body函數,此時$body的內容如下,最后返回到rcmail_out_html.php,拼接其他數據輸出,包含payload的頁面就被完整的輸出到前端。

body

0x03 補丁分析

patch

patch2

$content被解碼之后,對返回前的$out使用strip_tags過濾,去除 HTML 和 PHP 標簽。

0x04 修復方案

升級程序:https://roundcube.net/news/2017/03/10/updates-1.2.4-and-1.1.8-released

0x05 相關鏈接

https://www.seebug.org/vuldb/ssvid-92784

http://www.securityfocus.com/bid/96817/info

http://seclists.org/oss-sec/2017/q1/583

https://twitter.com/0x6D6172696F/status/841204796887564288


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/249/