快两个月没写博客,因为期间一直在补充浏览器和Windows漏洞的基础知识,这方面资料很多,所以感觉没什么好写的。经过前面,积累到了很多知识,所以这篇文章来分析一下lokihardt曾经提交的chakra漏洞。

Project Zero : https://bugs.chromium.org/p/project-zero/issues/detail?id=1702

CVE-2019-0539

对于JIT引擎的基础知识我就不展开了,需要补充的可以直接看elli0tn0phacker师傅的这篇文章:Chakra漏洞调试笔记2——OpCode Side Effect

-w1007

根据 lokihardt 的描述可以知道,NewScObjectNoCtor 和 InitProto 这两个 opcode 被认为是没有side effect的,但是可以通过设置 prototype 的方式,将 prototype 的对象类型进行转换,从而造成类型混淆漏洞。下面用 NewScObjectNoCtor 这个 opcode 做测试。

首先确认如何让JIT生成 NewScObjectNoCtor 这个 opcode,其实很简单,自己写一个函数,然后 new 自己的函数即可。

查看生成的IR:

根据之前case里的描述就能知道,可以通过设置test函数的prototype让其指向一个对象,然后当JIT进入NewScObjectNoCtor函数时,内部将prototype指向的对象的类型进行转换。

将test.prototype设置成对象o。

通过调试可以发现如下调用栈,JIT进入NewScObjectNoCtor函数后,经过一些列的操作,最终调用 AdjustSlots 将test的prototype(对象o)的类型进行了转换。

-w1011

为什么类型转换可以造成漏洞?

我们知道现在对象o是一个inline slots布局的对象,内存布局如下,红框里的表示属性o.a和属性o.b的值。

继续看:

我们将对象o传给opt函数,在o.b = 1时,对象o的布局是inline slots,但是执行new test()后,NewScObjectNoCtor这个opcode将对象o转换成了auxSlots。

转换成auxSlots后:

可以看到红框的位置原来存储的是属性o.a的值,但是转换布局后成了auxSlots指针。但是JIT没有意识到对象o的布局已经做了转换,在执行o.a = value 时仍然采用inline slots方式写入属性值,从而将auxSlots指针覆盖。

查看lowerer阶段,可以看到JIT并没有做任何类型检查,而是直接将value赋值给了o.a,从而造成了漏洞:

观察内存布局,看到auxSlots指针已经被替换成了0x1234。

然后 print(o.a)就会造成异常。

补丁分析:

补丁方式很简单,通过KillObjectHeaderInlinedTypeSyms删除Object的Symbol,删除后在o.a = value;时就必须要再次进行类型检查,从而防止类型混淆。
-w1307

对比补丁前后:

在补丁前jit直接将value赋值给o.a,但是补丁后需要进行一系列的类型检查,成功防止了漏洞。