vm2沙盒逃逸分析
上个月底写了一半给咕了..看到GKCTF有道题涉及到了vm,顺便放出来吧
前段时间hfctf有一道vm2沙盒逃逸的题目,于是顺便研究下vm2的实现和沙盒逃逸的原理
vm
vm2是基于nodejs的内置模块vm的,所以先看看vm
代码示例
vm能够在一个新的V8虚拟环境中运行代码,看一个文档上的例子
1 | const vm = require('vm'); |
contextify
概念
vm中有一个很重要的概念contextify,大致意思是当vm.createContext()
被调用时,指定的contextObject
(示例代码中的context
)会与一个新的V8实例联系在一起,这个过程称为contextifying the object
具体实现
经过测试可以发现,vm会将一个指定的对象作为新的环境的全局对象(相当于正常情况下的global
对象),这个对象拥有context
的所有属性,同时还自动添加了一些内置的全局变量,比如Object
和Function
,这些属性不会被添加到context
上
在沙盒中可以用this
访问该对象,手动给this
添加的属性会被添加到context
上
1 | const vm = require('vm'); |
这些添加的属性与global
中的同名属性是不同的对象(内存中位置不同)
1 | const vm = require('vm'); |
沙盒逃逸
虽然存在contextify,vm还是可以很轻松的逃逸出去,因为this.__proto__
指向的是主环境的Object.prototype
1 | const vm = require('vm'); |
- 第一步
this.constructor.constructor
通过继承链最终拿到主环境的Function
this.constructor.constructor('return process')()
构造了一个函数并执行,拿到主环境的process
变量- 通过
process.mainModule.require
导入child_process
模块,命令执行
vm2
代码示例
1 | const {VM} = require('vm2'); |
可以看到vm沙盒逃逸的payload已经打不通了
vm2实现
这里只关注/lib/main.js 中的VM
类和/lib/contextify.js
具体的代码就不贴了,太多了也没法贴…简单说一下
- 使用vm创建新的v8虚拟环境
- 在执行用户代码前先在新的V8虚拟环境中运行/lib/contextify.js
- 将
this
的__proto__
修改为新环境中的Object.prototype
,这也是为什么vm的exp打不通了 - 引入了proxy,分为两种:
- Contextify
- Decontextify
- 将
发现vm2根本写不清楚…先咕了吧…