滥用XSS过滤器:一个^符号引发的XSS漏洞(CVE-2016-3212)

本文详细分析了CVE-2016-3212漏洞,展示了如何利用IE/Edge的XSS过滤器将点号替换为^符号,从而绕过安全验证实现跨站脚本攻击,并分享了在Google服务中的实际案例和修复方案。

滥用XSS过滤器:一个^符号引发的XSS漏洞(CVE-2016-3212)

过去,我在日本的CODE BLUE信息安全会议上讨论了利用IE XSS过滤器进行XSS攻击的方法。类似的漏洞在2016年6月的补丁中作为CVE-2016-3212被修复。本文将详细解释这个漏洞的细节。

如我的幻灯片所述,通过将XSS过滤器规则应用于不相关的上下文,即使页面本身没有XSS漏洞,我们也可以利用过滤器将.替换为#的行为进行XSS攻击。

为了防止此类攻击,微软在2015年12月的补丁中更改了过滤器行为。此后,^被用作.的替换字符,而不是#。这确实可以防止上述攻击,但也带来了另一个问题。几个月后,我在Google的域中确认了利用此行为的XSS,并通过Google VRP获得了3133.7美元的奖励。

Google几乎在所有服务中都设置了X-XSS-Protection: 1;mode=block头,但并非全部。因此,我仔细检查了一些没有mode=block的页面,结果发现cloud.google.com上的Javadoc存在漏洞页面。

我放置了一个近似副本: http://vulnerabledoma.in/xxn/xss_javadoc.html

当XSS过滤器将一个.替换为^时,此页面就容易受到XSS攻击。你能找到是哪里吗?

答案是黄色部分的.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<!-- NewPage -->
<html lang="en">
<head>
<title>javadoc</title>
<script type="text/javascript">
    targetPage = "" + window.location.search;
    if (targetPage != "" && targetPage != "undefined")
        targetPage = targetPage.substring(1);
    if (targetPage.indexOf(":") != -1 || (targetPage != "" && !validURL(targetPage)))
        targetPage = "undefined";
    function validURL(url) {
        try {
            url = decodeURIComponent(url);
        }
        catch (error) {
            return false;
        }
        var pos = url.indexOf(".html");
        if (pos == -1 || pos != url.length - 5)
            return false;
        var allowNumber = false;
        var allowSep = false;
        var seenDot = false;
        for (var i = 0; i < url.length - 5; i++) {
            var ch = url.charAt(i);
            if ('a' <= ch && ch <= 'z' ||
                    'A' <= ch && ch <= 'Z' ||
                    ch == '$' ||
                    ch == '_' ||
                    ch.charCodeAt(0) > 127) {
                allowNumber = true;
                allowSep = true;
            } else if ('0' <= ch && ch <= '9'
                    || ch == '-') {
                if (!allowNumber)
                     return false;
            } else if (ch == '/' || ch == '.') {
                if (!allowSep)
                    return false;
                allowNumber = false;
                allowSep = false;
                if (ch == '.')
                     seenDot = true;
                if (ch == '/' && seenDot)
                     return false;
            } else {
                return false;
            }
        }
        return true;
    }
    function loadFrames() {
        if (targetPage != "" && targetPage != "undefined")
             top.classFrame.location = top.targetPage;
    }
</script>
</head>
<frameset cols="20%,80%" title="Documentation frame" onload="top.loadFrames()">
<frameset rows="30%,70%" title="Left frames" onload="top.loadFrames()">
<frame src="/" name="packageListFrame" title="All Packages">
<frame src="/" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
</frameset>
<frame src="/" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
<noframes>
<noscript>
<div>JavaScript is disabled on your browser.</div>
</noscript>
<h2>Frame Alert</h2>
<p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="overview-summary.html">Non-frame version</a>.</p>
</noframes>
</frameset>
</html>

<script>中,它检查通过location.search给定的字符串是否安全。例如,以下不安全的URL被阻止: http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)

那么,当黄色部分的.被替换为^时会发生什么?

让我们实际尝试一下。如果你在目标URL中放入以下字符串,页面内容会被强制匹配XSS过滤器规则,我们可以将目标的.替换为^

你可以使用未安装2016年6月补丁的IE/Edge从以下URL重现此漏洞: http://vulnerabledoma.in/xxn/xss_javadoc.html?javascript:alert(1)//"++++++++++++.i+++=

我还为已应用补丁的用户放置了替换后的页面,你可以确认相同的行为: http://vulnerabledoma.in/xxn/xss_javadoc2.html?javascript:alert(document.domain)

#^的一个关键区别是:#在JavaScript中不是运算符,但^是运算符。例如,如果页面中有a.b;,它被替换为#^a#b;是语法错误,但a^b;是有效语法。这就带来了XSS漏洞。

在2016年6月补丁之后,当XSS过滤器替换.时,即使页面没有X-XSS-Protection头,也会强制执行mode=block行为。

当我看到^显示时,我感到惊讶和厌恶,但无论如何,它终于平静下来了!

此外,在最近的补丁(2016年7月)中,微软似乎几乎杀死了利用XSS过滤器进行XSS攻击的可能性。我将在下一篇文章中详细说明这一点:)

谢谢!

发布时间:2016年7月15日星期五上午7:10
作者:Masato Kinugawa

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计