欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

JavaScript engine exploit(一)

来源:本站整理 作者:佚名 时间:2019-08-12 TAG: 我要投稿

写这篇文章的目的主要是希望记录下自己学习的过程,也将自己的学习经历分享给希望学习JS引擎漏洞利用的初学者。
我刚开始学习的是JSC(JavaScriptCore),这是Safari浏览器所使用的内核WebKit的JS引擎。我选择这个目标入手的原因主要是这个比较简单,很多大佬给出的JS引擎的研究难度基本都是:
V8(Chrome)> Spidermonkey(Firefox)> JacaScriptCore(Safari)> Chakra(Edge)
因为Chakra快被微软抛弃了,所以我还是选择了JSC。
 
0x01 JS引擎的一些基础知识
在学习JS引擎的漏洞利用之前,对JS引擎的一些内部运行原理做一个了解肯定是必要的。当然,也不必把所有原理都了解,由于我是通过调试特定的漏洞来进行学习(大部分初学者都是如此),所以我们只需要了解到与这个漏洞相关的一些知识就足够了。推荐去看saelo在2016年发的paper:Attacking javascript engines
我在下面也引述文章中的部分内容。
JavaScript engine overview
总体来看,JavaScript引擎包括了三个部分:
一个基础的编译器,至少需要包含一个JIT(Just-in-time)。
一个JavaScript VM,用来运行JavaScript代码。
一个JavaScript Runtime,用来提供一些内置Objects和Functions。
我们不需要关心编译器的内部原理,因为跟我们的漏洞利用关系不大(至少看起来关系不大),我们只需要把编译器看作一个 “输入源码,输出字节码”的黑盒就行了。
The VM, Values, and NaN-boxing
VM通常都包含一个可以直接执行字节码的解释器。VM被实现为stack-based的机器(相对于register-based来讲),从而可以通过栈来对值进行操作。对于具体的操作码的实现可能看起来和下面一样:
CASE(JSOP_ADD)
{
  MutableHandleValue lval = REGS.stackHandleAt(-2);
  MutableHandleValue rval = REGS.stackHandleAt(-1);
  MutableHandleValue res = REGS.stackHandleAt(-2);
  if (!AddOperation(cx, lval, rval, res))
    goto error;
  REGS.sp--;
}
END_CASE(JSOP_ADD)
这段代码截取自Firefox的JS引擎Spidermonkey,而JSC使用汇编实现的类似功能,看起来就没有上面这么直观。对JSC的实现感兴趣的可以去看这个文件Webkit/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm。
通常初级JIT(first stage JIT or called baseline JIT)负责减轻一些解释器的调度开销,而高级JIT(higher stage JIT)则会做一些比较复杂的优化操作。有点类似于我们平常所使用的AOT(ahead-of-time)编译器,就比如gcc。优化型JIT(也就是前面提到的高级JIT)通常都是推测型的,意思就是它们会基于一些推测来进行优化,比如它会认为一个变量是而且一直是数字类型。当然这种推测也可能出错,当遇到出错的时候JIT就会回退到推测之前的状态。
JavaScript是动态类型语言,因此类型信息与运行时的变量有关,而不是编译时的变量。JavaScript类型系统定义了几个元类型(number, string, boolean, null, undefined, symbol)和对象(array, function)。需要注意的是JavaScript没有像其他语言一样包含‘类’的概念。取而代之的是JavaScript使用了所谓“基于原型的继承(prototype-based-inheritance)”,每个对象都有一个指向prototype对象的引用,这个prototype对象包含了指向它的对象的属性。
出于性能考虑(快速拷贝,适应64位的寄存器架构等),所有主流的JavaScript引擎在表示一个Value的时候都不超过八字节。一些JS引擎,比如v8,会使用tagged pointers来表示值,它会使用最低有效位来标识一个值是指针还是立即数。JSC和Spidermonkey则使用了另一种叫做NaN-boxing的概念。在NaN-boxing中使用了多种位模式来表示NaN(Not-a-Number),所以可以将这些位模式来编码其他的值,以下是IEEE 754的规则总结:
形式
指数
小数部分

0
0
非规约形式
0
大于0小于1
规约形式



大于等于1小于2
无穷

0
NaN

非0
这些多余的位模式足够用来编码整型和指针了,也因为使用了NaN-boxing,对于64位平台而言,目前只有48位用于寻址。
JSC使用的这个方案在Webkit/Source/JavaScriptCore/runtime/JSCJSValue.h中有很好的解释,引用如下:
            /*
      ...
      * The top 16-bits denote the type of the encoded JSValue:
      *
      *     Pointer {  0000:PPPP:PPPP:PPPP
      *              / 0001:****:****:****
      *     Double  {         ...

[1] [2] [3] [4] [5]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载