作者:启明星辰ADLab

1. 前言

WebKit 是一个开源的浏览器引擎,不仅应用在很多主流浏览器(Safari,Chrome,UCbrowser,QQBrowser等)上,而且支持Android,iOS,Windows,Linux,macOS等多种平台上的有关web渲染引擎的应用。

启明星辰ADLab对WebKit引擎进行漏洞挖掘和代码审计工作,发现webkit的若干漏洞,都已提交厂商进行修复。

本文将介绍webkit架构和各模块功能,并详细分析webkit各模块的漏洞案例,对 WebKit浏览器漏洞面进行一个较为全面的阐述。

2. Webkit架构和模块

下图是WebKit的架构和模块图。

WebKit中的每个模块都可能成为漏洞的重灾区,下面我们选取WebKit模块中的漏洞案例进行详细分析,其中包括了今年ADLab提交的漏洞。

2.1 WebCore :CVE-2018-12293 等多个整数溢出

CVE-2018-12293是ADLab在6月份提交的WebCore渲染引擎相关的整数溢出漏洞,该漏洞存在于ImageBufferCario.cpp 的WebCore::getImageData函数中。该函数根据可控的width和height属性的乘积创建数组result:

当width和height足够大,它们的乘积就会导致整数溢出;当传递给创建函数的length大小为0时,缓存的创建并不会受影响,根据ArrayBuffer.cpp:108,申请数组的大小是0时程序会将分配的长度设置为1。

在页面渲染之前,会先根据前面创建的数组以及width的大小计算destRows缓存的偏移:

显然此时destRows已经是越界索引,但后面的循环过程会不断地向其后填充颜色值,导致堆溢出:

漏洞补丁中修复了多处潜在的整数溢出。针对本漏洞,检查width和height属性的乘积是否溢出;检查横向坐标以及纵向坐标结束的位置的溢出;另外,加入对行数和列数的检查,预防其他的溢出问题:

2.2 runtime:CVE-2016-4622 回调导致越界访问

JavaScriptCore的runtime是为JavaScript的运行提供支撑的代码。在JavaScriptCore中标有“JSC_HOST_CALL”的函数均与JavaScript函数对应。例如arrayProtoFuncSlice与Array对象的slice()函数对应,其调用方法形如:

函数参数可以用一个表达式代替,因此slice函数内部可以形成一个回调,形如:

漏洞CVE-2016-4622产生的根本原因是没有妥善处理可能的回调。代码 逻辑中首先获取了Array的长度,然后在获取slice()的end参数时动态地改变了该Array的长度,使得end > length,但之后使用fastSlice时仍然根据end参数进行内存拷贝,导致越界访问。

针对本漏洞,开发者在fastSlice之前添加了检查Array的长度的代码,若其已被修改则不再使用fastSlice。这样就修复了这个漏洞。

2.3 JIT:CVE-2017-2547 DFG优化导致边界检查失败

为了平衡JavaScript的功能与运行效率,WebKit的JavaScriptCore选择了分层JIT编译优化的策略。JavaScriptCore由Lexer、Parser、LLInt、Baseline JIT、DFG JIT以及FTL JIT组成。其工作流程如下图:

CVE-2017-2547是WebKit JIT优化漏洞中的典型,这个漏洞发生在DFG JIT的边界检查中。DFG对数组边界检查时,首先存储数组访问的最大边界值和最小边界值的范围,然后遍历节点,以便每次访问时总是检查访问数组索引的最大值和最小值的节点是否越界,从而删除其他不必要的访问节点的检查。这在逻辑上是合乎情理的:

但当输入是整数值的时候,代码逻辑会进入下面的if模块。其中最小节点被清0;在后续添加数组边界检查时,只添加了对最大边界的检查,而忽略了最小边界。因此,同时使用负值索引和正值索引访问数组,负值部分的访问会直接绕过DFG生成代码的数组边界检查,达到越界访问的效果。

漏洞的补丁加入了针对“最小边界为负值”的判断。如果满足条件,DFG会直接OSR回退:

2.4 Garbage Collection:CVE-2017-2491 垃圾回收使用错误导致Use-After-Free

垃圾回收过程中,程序从所有的根出发寻找对象被引用情况,如果某对象存在引用则被标记为活动对象,否则相应的内存空间应该被释放。WebKit中定义的根包括进程栈、JavaScript执行环境栈、MarkedArgumentBuffer。

漏洞CVE-2017-2491涉及对垃圾回收的错误使用。在CachedCall中漏洞代码使用Vector<JSValue>类型的变量存储函数调用所需参数。例如正则表达式的replace函数,传入其中的参数可能是大量的,这时代码只能使用Vector或类似的结构来接收参数。但内存压力触发垃圾回收时,Vector无法被垃圾回收算法标记,导致相关内存被错误释放。

之后,开发者使用MarkedArgumentBuffer替换Vector对象,因此该漏洞得到修复:

2.5 WebAssembly:CVE-2018-4121 wasm文件解析导致越界写

WebAssembly是一种新兴的Web技术,它尝试通过字节码标准解决JavaScript的运行效率问题。WebAssembly模块在WebKit中受JavaScriptCore控制,其字节码通过wasm文件传递。wasm文件类似PE文件有不同的区段,例如Type、Import、Function、Table、Code、Data等。WebAssembly要求每种区段在wasm文件中最多出现一次。

漏洞CVE-2018-4121的PoC是一个wasm文件,其中含有两个Function段。WebKit JavaScriptCore在解析PoC时,只为Function段准备了一个Vetctor来保存函数列表的签名,且使用uncheckedAppend分两次把不同Function段的内容附加到同一个Vector。如果第一次可以把Vector装满,第二次就能导致越界写发生。

WebAssembly要求Known区段的所有种类的id顺序递增,例如Function区段的id为3,Import区段的id为2,则Function区段必须位于Import区段的后面;Custom区段不属于Known,其id为0,因此可以不遵循上述递增规则。代码在validateOrder函数想当然地表达了这个规则:

代码会使Custom区段后面的任意区段通过检查,这是漏洞产生的根本原因。

为了修补这个漏洞,开发者通过定义不存在的区段Section::Begin(id定义为0),把Custom区段和最初始的情况区别开来:

同时使用previousKnownSection代替previousSection,这样validateOrder函数的previousKnownSection参数不再包含Custom区段,只处理Known区段来保证其遵循id的“顺序递增”规则,从而修补了这个漏洞:

参考链接

[1] https://webkit.org/
[2] https://github.com/WebKit/webkit
[3] https://trac.webkit.org/wiki/JavaScriptCore
[4] https://webkit.org/blog/3362/introducing-the-webkit-ftl-jit/
[5] https://www.geeksforgeeks.org/mark-and-sweep-garbage-collection-algorithm/
[6] https://webassembly.org/docs/binary-encoding/


启明星辰积极防御实验室(ADLab)

ADLab成立于1999年,是中国安全行业最早成立的攻防技术研究实验室之一,微软MAPP计划核心成员。截止目前,ADLab通过CVE发布Windows、Linux、Unix等操作系统安全或软件漏洞近400个,持续保持国际网络安全领域一流水准。实验室研究方向涵盖操作系统与应用系统安全研究、移动智能终端安全研究、物联网智能设备安全研究、Web安全研究、工控系统安全研究、云安全研究。研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。


源链接

Hacking more

...