mail.qq.com DOM XSS

首先发现一处getcontent

http://mail.qq.com/cgi-bin/readtemplate?t=compose&check=false&getcontenturl=http://mail.qq.com/test

查看调用的函数

if(c&&/^https?:\/\/([\w]+\.)?mail\.qq\.com(\/)/i.test(c))
{
b.loadJsFile(c,false,b.document,function(){
if(!b.goAsyncContent||!b.goAsyncContent.content)
{
b.goAsyncContent={};
b.showError("\u90AE\u4EF6\u5185\u5BB9\u83B7\u53D6\u5931\u8D25");
}
});...

此处正则判断了 https://xxx.mail.qq.com/xxx
如果正则返回true,则loadJsFile(URL)

查看loadJsFile()

function loadJsFile(e, a, d, b, c, f) {
    var n = getTop(),
    j = d || document,
    h = typeof b == "function",
    l,
    m,
    k = n.loadJsFile,
    o = n.getRes(e),
    i = k._oDatas || (k._oDatas = {});
    if (QMDistributeDomain.isRelativeUrl(o)) {
        o = QMDistributeDomain.addHost(o);
    }
    if (typeof(f) == 'boolean') {
        f = {
            bAutoRemove: f
        };
    } else if (!f) {
        f = {};
    }
    if (a) {
        m = k.getLoadedScript(o, j);
        if (m) {
            if (h) {
                var p = m.getAttribute("_key_");
                if (i[p] === true) {
                    callBack.call(m, b);
                } else {
                    i[p].push(b);
                }
            }
            return m;
        }
    }
    m = j.createElement("script");
    if (!c) {
        c = {};
    }
    if (typeof(c.crossOrigin) != 'string') {
        if (c.crossOrigin !== false && k.checkCrossOrigin(o)) {
            c.crossOrigin = '*';
        } else {
            delete c.crossOrigin;
        }
    }
    if (f.bReload) {
        o += (o.indexOf('?') != -1 ? '&': '?') + 'r=' + Math.random();
    }
    n.E(c,
    function(r, q) {
        m.setAttribute(q, r);
    });
    var p = n.unikey();
    m.setAttribute("_key_", p);
    i[p] = [];
    function g() {
        var q = this,
        r = q.getAttribute("_key_");
        callBack.call(q, b);
        n.E(i[r],
        function(s) {
            s();
        });
        i[r] = true;
        if (f.bAutoRemove) {
            n.removeSelf(m);
        }
        q.onreadystatechange = q.onload = q.onerror = null;
    } (GelTags("head", j)[0] || j.documentElement).appendChild(extend(m, {
        onerror: function() {
            if (c.crossOrigin) {
                debug('crossOrigin error file:' + o);
                n.ossLogCustom('delay', 'all', 'corsError', e);
                n.LogKV({
                    sValue: 'getinvestigate|jsload|cors|jserr'
                });
                c.crossOrigin = false;
                f.bReload = true;
                n.removeSelf(m);
                m.onreadystatechange = m.onload = m.onerror = null;
                k(e, false, d, b, c, f);
            } else {
                debug('file load error:' + o);
            }
        },
        onload: g,
        onreadystatechange: function() {
            var q = this; ({
                loaded: true,
                complete: true
            } [q.readyState]) && g.call(this);
        }
    },
    {
        type: "text/javascript",
        charset: c.charset || "gb2312",
        src: c.crossOrigin ? (o.indexOf('?') > 0 ? o + '&r=o': o + '?r=o') : o
    }));
    return m;
}

是动态加载JS的函数。
但是结合前面,只能加载 *.mail.qq.com/下的文件

绕过: jsonpToJs
由于jsonp的callback 通常可以自定义,因此通常可以构造成任意JS语句
存在 *.mail.qq.com/ 下的jsonp 可以通过修改callback使得内容看起来是这样

alert()//([{title : "cgi exception"...);

由于//注释了后面的内容,因此成功alert(),进而可以构造出加载外部域JS,虽然,寻找这样一个jsonp犹如大海捞针。
domxss

**此漏洞状态为:已修复

漏洞涉及的存在风险的jsonp由于漏洞并未修复,不公开。