利用javascript压缩工具留下后门

华盟原创文章投稿奖励计划

  这不禁让我思考,能否在javascript中使用同样的后门技术。JS几乎处处可见其身影(从浏览器,到服务器,到arduino,到机器人,甚至某一天还可能在汽车上见到它的踪迹)。但是javascript是解释型语言,不是编译型语言。然而,对js进行削减和优化,以减小文件大小,提升性能,这种做法很常见。或许通过使用js压缩工具,我们就能找到插入后门的机会。

  Part I: 寻找压缩工具中的bug

  流行的js压缩工具真的有能导致安全问题的bug?

  搜寻了10分钟左右,我在Uglifyijs中就发现了一个这样的bug,UglfyJs是一个流行的js压缩工具,在JQuery中使用了它来建立脚本,同时Internet上大约70%的顶级网站都使用了JQuery。该bug在2.4.24 release版中进行了修复,bug本身原因很简单,但却不是很明显。那么让我们来看看它。

  UglifyJS做了一系列事情来精简文件大小。其中默认的压缩标记会对如下的表达式进行压缩:

!&& !&& !&& !d

  该表达式的长度是20字符,如果我们使用德摩根定律,则可以将其改写为:

!(|| b || c || d)

  而这个新的表达式只有19个字符。看起来还不错,问题在于如果其中的任何一个子表达式有非布尔型的返回值,德摩根定律并不能像预期的那样工作。比如:

!false && 1

  会返回数字1.而另一方面

!(false || !1)

  仅仅返回true.

  因此,如果我们可以欺骗js压缩工具错误的使用德摩根定律,那么我们可以使程序在压缩前后表现不一样。结果显示,让UglifyJS 2.4.23这样做并不难,因为如果使用的摩根定律后的表达式比原表达式更短,它就会使用这个表达式替代换来的表达式(UglifyJs 2.4.24对次已进行了修补,会确保在重写表达式前子表达式的值是布尔型的)。

  Part II: 在某些身份验证代码中构建后门

  很好,我们已经找到了期望的js压缩工具的bug.下面来对其进行利用。

  假设你正为某个公司工作,希望在其Node.js网站有意留下后门。而你被要求写一些服务器端的脚本来验证用户的认证token是否过期。首先,确保Node包使用uglify-js@2.4.23,(该版本有我们上面提到的bug).

  接着你写了token验证函数,插入了一堆看起来是配置和用户认证的检查操作,来迫使压缩工具来压缩的长的非布尔表达式。

function isTokenValid(user) {
    var timeLeft =
        !!config && // config object exists
        !!user.token && // user object has a token
        !user.token.invalidated && // token is not explicitly invalidated
        !config.uninitialized && // config is initialized
        !config.ignoreTimestamps && // don't ignore timestamps
        
        getTimeLeft(user.token.expiry); // > 0 if expiration is in the future
 
    // The token must not be expired
    return timeLeft > 0;
}
 
function getTimeLeft(expiry) {
  return expiry - getSystemTime();
}

  使用uglifyjs –c 运行上面的代码片段,输出结果如下:

function isTokenValid(user){var timeLeft=!(!config||!user.token||user.token.invalidated||config.uninitialized||config.ignoreTimestamps||!getTimeLeft(user.token.expiry));return timeLeft>0}function getTimeLeft(expiry){return expiry-getSystemTime()}

  在原始形式中,如果config 和user的检查通过了,在token已经过期的时候timeLeft是一个负数.在压缩后了的形式中,timeLeft必须是布尔型的(因为”!”会进行强制类型转换为布尔型)。实际上,如果在config和user检查通过的时候,timeLeft会一直为true,除非getTimeLet的返回值刚好为0.由于在javascript中true>0(由于类型强制转换),过期的认证token将会一直有效。

  Part III: 修改jQuery

  下面,让我们利用这个js压缩工具的bug来对jQuery本身进行一些修改,使其留下后门。我们将使用写本文时jquery的稳定发行版,jQuery 1.11.3.

  JQuery 1.11.3使用gurney-contrib-gulify0.3.2来进行js的压缩,这反过来又依赖于uglify-js-2.4.0.因此uglify-js@2.4.23满足这个依赖,我们可以手工改动grunt-contrib-uglify中的package.json文件来强制它使用这个版本的uglify-js。

  在jQuery中有多处使用德摩根定律进行表达式重写优化。这其中没有一处能导致出现bug,那我们就自己加吧。

  Backdoor Patch #1:

  首先,我们在jQuery的.html()方法加一个潜在的后门,这个改动看起来有些怪异,且显得多余,但我们可以让其他人相信,它并不会改变原来的操作。实际上,压缩前,单元测试通过。

  在使用uglify-js@2.4.23进行压缩后,jQuery的.html()方法会设置innerHtml为true而不是给定的值,因此一系列测试会失败。

   利用javascript压缩工具留下后门

  然而,jQuery的维护者可能正使用打了修改了的uglifyjs。事实上,uglify-js@2.4.24通过了测试,因此,这个修改看起来也不会太可疑。

  利用javascript压缩工具留下后门

  现在我们运行grunt来建立进行了这一修改的jQuery,同时使用简单的代码来触发这个漏洞

<html>
    <script src="../dist/jquery.min.js"></script>
    <button>click me to see if this site is safe</button>
    <script>
        $('button').click(function(e) {
            $('#result').html('<b>false!!</b>');
        });
    </script>
    <div id='result'></div>
</html>

  下面是使用js未压缩的jQuery,点击按钮后的运行结果:

     利用javascript压缩工具留下后门

  和预期的一样,用户被警告站点不安全。这看起来觉得讽刺,因为它并没有使用我们的js压缩工具触发的后门。下面是使用我们的用了js压缩工具的jQuery的情况:

  

利用javascript压缩工具留下后门

  现在用户会认为这个站点是安全的,即使是站点的作者试图对提醒访问者站点不安全。

  Backdoor Patch #2:

  第一个后门可能过于简单而很容易被检测到,毕竟任何使用它的人都可能会注意到一堆HTML都被设置为了字符串”true”而不是他们想设置的HTML.因此,我们的第二个后门仅仅在特殊情形下才会触发。

  

利用javascript压缩工具留下后门

  我们主要修改了jQuery.event.remove方法(在.off()方法中使用),因此调用了特殊事件移除钩子的代码在压缩后,就不会被调用到。(由于切割通常是boolen型的,它的长度通常是没有定义的,不是大于0的)。这不一定会改变网站的行为,除非开发者定义了一个这样的钩子.

  假设我们需要留下后门的网站的html代码如下:

<html>
    <script src="../dist/jquery.min.js"></script>
    <button>click me to see if special event handlers are called!</button>
    <div>FAIL</div>
    <script>
        // Add a special event hook for onclick removal
        jQuery.event.special.click.remove = function(handleObj) {
            $('div').text('SUCCESS');
        };
        $('button').click(function myHandler(e) {
            // Trigger the special event hook
            $('button').off('click');
        });
    </script>
</html>

  如果我们运行没有使用压缩的jQuery的jQuery,事件移除钩子如预期的那样被正常调用。

 利用javascript压缩工具留下后门

  但如果我们使用压缩的版本,事件移除钩子则不会被调用。

  利用javascript压缩工具留下后门

  显然,这并不是什么好消息,如果事件移除钩子做一些安全相关的功能,比如在传递用户认证token到它的时候,检查orgin是不是在白名单中。

  总结

  我展示的后门例子是人为添加的,但是事实是他们是可以存在的,这可能让js开发者担心了,尽管js 压缩工具并不像C++那样复杂,或者说重要,但他们支撑了很多在web上运行的代码。

  好的一点是uglifyjs已经为已知的bug添加了测试用例,但我任然建议那些使用了非正常认证的压缩工具的人小心。除非不得不这样做,否则不要压缩服务端的代码。并确信在浏览器上运行了测试用例,扫描了使用了压缩工具后得到的代码。

原文地址:https://hack.77169.com/201511/215830.shtm

本文原创,作者:华盟君,其版权均为华盟网所有。如需转载,请注明出处:https://www.77169.net/html/20297.html

发表评论