VM2沙箱逃逸漏洞分析
2023-5-15 18:7:22 Author: govuln.com(查看原文) 阅读量:16 收藏

更多安全资讯和分析文章请关注启明星辰ADLab微信公众号及官方网站(adlab.venustech.com.cn)

01
漏洞概述
4月7日,seongil-wi在github上披露了Node.js模块vm2 的沙箱逃逸漏洞(CVE-2023-29017),CVSSv3评分为10.0,漏洞定级为严重,影响版本为3.9.14之前。随后Xion又在修复的vm2 3.9.15版本中披露了同级别的另一沙箱逃逸漏洞(CVE-2023-29199)。启明星辰ADLab在第一时间对漏洞进行了分析并给出修复建议。沙箱对Node.js 宿主对象的操作进行代理,以达到在Node.js 中执行受信任的代码。如果Node.js的宿主对象未经沙箱处理直接使用,可执行任意代码,从而绕过沙箱保护。
02
漏洞分析与修复
2.1 漏洞CVE-2023-29017

(1)漏洞分析

该漏洞通过函数Error.prepareStackTrace 绕过沙箱。当异常发生时,函数Error.prepareStackTrace可用来定制输出栈的信息。

当在vm2 沙箱环境下执行代码时,vm2通过vm配置程序运行的虚拟环境,将Node.js环境中的全局变量eval、Function等进行代理:

对宿主环境中的对象和代理对象进行映射,对于宿主对象的操作实际上操作的是代理对象,这种机制可阻断宿主环境中不可信的代码对不安全模块或对象的访问。

如在沙箱的环境下获得当前进程的process对象,会出现该对象没有定义的错误,一定程度上阻止了沙箱中代码访问一些可能带来安全隐患的对象:

在沙箱环境的配置中设置了Error.prepareStackTrace 中的getter 和setter方法,使该方法执行时的调用栈对象为私有的调用栈:
问题在于沙箱程序并没有考虑变量sst是否是沙箱处理过的对象,如果是宿主对象,那么该变量可直接在方法prepareStackTrace执行的时候直接使用,也就是漏洞的导火索。
在正常情况下,当执行错误时(line285),会在当前沙箱环境下抛出异常,前述的sst是安全的。
但当执行的异常是在一个异步函数中出现的异常,line285 直接返回的ret是一个promise对象,只不过该对象的状态是rejected,这样就跳过了line288在沙箱环境下抛出的异常。
在跳出了沙箱环境后,Node.js会处理Promise rejected的情况 ,使用reason创建错误直接抛出:
这时在prepareStackTrace中捕获的异常栈信息sst就不是沙箱中的对象,即宿主对象。由于reason是沙箱对象,创建的err也是沙箱对象,即error信息的对象是安全的:
当prepareStackTrace执行时,使用宿主对象sst索引到的Function正是原始的对象,获得原始的Function的对象,即可创建任意代码来执行。

(2)漏洞修复

在漏洞补丁中,传递给prepareStackTrace的栈帧数组,使用ensureThis函数确保该数组是经过proxy处理的对象。由于当前环境是在沙箱内,所以创建的数组也是安全的。

2.2 漏洞CVE-2023-29199

(1)漏洞分析

不同于Error.prepareStackTrace传递Node.js宿主对象,该漏洞利用沙箱在对要执行的代码预处理的过程绕过沙箱。
在代码执行前,沙箱使用transform函数遍历代码的AST,对于catch语句块中会插入代码,调用handleException 确保捕获的错误对象是受沙箱保护的:
在插入前的代码如:
执行插入后会在catch body中插入:
在随后的transform中会将$tmpname 替换为tmpname:
tmpname的值为VM2_INTERNAL_TMPNAME:
替换后的catch块为:

aVM2_INTERNAL_TMPNAME=VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL.handleException(aVM2_INTERNAL_TMPNAME);

沙箱本意是想保护a$tmpname变量,经过上述转化,使得保护的值为aVM2_INTERNAL_TMPNAME,使得变量a$tmpname这个宿主对象未经保护可直接使用。为了保证程序正常执行,在POC中定义了变量aVM2_INTERNAL_TMPNAME:
同第一个漏洞,通过a$tmpname 可以索引到Function对象,可执行任意代码。

(2)漏洞修复

在3.9.16的修复中,插入的代码放在了一个函数中,即将code转化为coder,直接对catch变量进行沙箱保护:
在后续的获取代码的循环中,省去了对变量名的替换,这样就避免了保护对象错位的问题:
03
漏洞验证
在两个漏洞的验证中,都是使用前述的process对象在本地创建了新的文件,成功绕过了沙箱,如下图所示。

参考链接:

[1]https://gist.github.com/seongil-wi/2a44e082001b959bfe304b62121fb76d
[2]https://nvd.nist.gov/vuln/detail/CVE-2023-29017
[3]https://mp.weixin.qq.com/s/LiFjTw2tQpQaPuLH
[4]https://mp.weixin.qq.com/s/Q7thK-z_X71SQvllp
[5]https://nodejs.org/api/vm
[6]https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
[7]https://github.com/patriksimek/vm2/commit/d534e5785f38307b70d3aac1945260a261a94d50
[8]https://github.com/patriksimek/vm2/security/advisories/GHSA-xj72-wvfv-8985

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

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



文章来源: https://govuln.com/news/url/pDrQ
如有侵权请联系:admin#unsafe.sh