If an attacker has some way to control an object on the Mongo server through one way or another, it is possible to cause prototype pollution on any Mongoose client. Notably, if a poorly implemented service allows a user to control the object in findByIdAndUpdate and similar functions, this bug could be triggered through the $rename operator. Another possibility is if two different services interact with the same Mongo database, it is possible that one of them allows a user to inject an arbitrary object containing a __proto__ field, which would also trigger this bug.
If used with Express and EJS, this bug can easily lead to RCE. Many other libraries have known prototype pollution exploits as well, which may cause significant impact.
Allowing user input directly into update operations seems to be a very common pattern. For example, if we ask GPT-4 a basic prompt for updating a post using Mongoose, it gives us:
Prompt:
Using Express and Mongoose, take input from the user to find a “Post” by an id, and update its fields.
Response:
In Express and Mongoose, you can use the findByIdAndUpdate method to find a “Post” by an id and update its fields. Here’s how you can do it:
const express = require('express'); const mongoose = require('mongoose'); const Post = require('./models/post'); // Assuming you have a Post model in models/post.js
const app = express();
app.use(express.json()); // for parsing application/json
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
In the above example, req.params.id is used to get the id from the URL. The req.body is used to get the updated data from the user. The { new: true } option is used to return the updated post.
When Mongoose finds documents and reads the malicious document into an object, it uses an object with a prototype. If the top level object contains a __proto__ field, it leads to overwrites of the object prototype.
// (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 + '.'); } elseif (!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 {
If used with Express and EJS, this bug can easily lead to RCE. Many other libraries have known prototype pollution exploits as well, which may cause significant impact.
We also found that we can actually exploit Mongoose itself with the prototype pollution, to cause it to bypass all query parameters when using .find(), which allows an attacker to potentially dump entire collections:
// now, when querying posts, even when an owner is specified, all posts are returned const 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();
This could also easily lead to denial of service depending on how large a Mongo collection is, and which other libraries are being used in the application.
Apache Commons Text 是一个低级库,用于执行各种文本操作,例如转义、计算字符串差异以及用通过插值器查找的值替换文本中的占位符。使用字符串替换功能时,一些可用的插值器可以触发网络访问或代码执行。这是有意的,但这也意味着如果应用程序在传递给替换的字符串中包含用户输入而未对其进行适当清理,则攻击者将允许攻击者触发这些插值器。
出于这个原因,Apache Commons Text 团队决定将配置更新为“默认情况下更安全”,从而减轻无法验证输入的影响,并且不会让攻击者访问这些插值器。但是,仍然建议用户谨慎对待不受信任的输入。
我们目前不知道有任何应用程序将不受信任的输入传递给替代者,因此在 Apache Commons Text 1.10.0 之前可能会受到此问题的影响。
此问题与Log4Shell (CVE-2021-44228)不同,因为在 Log4Shell 中,可以从日志消息正文中进行字符串插值,该正文通常包含不受信任的输入。在 Apache Common Text issue 中,相关方法明确用于执行字符串插值并明确记录在案,因此应用程序不太可能在没有适当验证的情况下无意中传递不受信任的输入。
之后对于加载的 class 调用 newInstance 进行实例化。这里首先会去调用 class 默认的 public 构造函数,如果无法访问则会去遍历所有的构造器,优先获取参数个数为 0 的构造函数并反射调用。
之后便是漏洞的一大利用点,它针对 HashMap 当中剩下的键值,先尝试获取 key 在实例化 class 当中对应 field 的 setter 方法(要求为单参数),如果可以获取到的话。会用和刚才相同的逻辑递归实例化参数值,并反射调用;如果获取不到 setter 方法,则直接反射设置 field 的值。
因此针对 raw.return 反序列化方式的利用是通过 Map 的方式来传入利用 class,可利用 class 的位置有 3 个:
if (attach) { // returns current version of Response to consumer side. result.getObjectAttachments().put(DUBBO_VERSION_KEY, Version.getProtocolVersion()); out.writeAttachments(result.getObjectAttachments()); } }
GENYMOBILE assumes no liability whatsoever resulting from the download, install and use of Xposed, EdXposed, LSPosed and Magisk. Use at your own risk.
Note
Because Xposed and EdXposed are no longer maintained, we strongly recommend not using them anymore.
Android 5.0 - 7.1
Prerequisites
Xposed framework
Xposed installer
Installation
Drag’n drop the Xposed framework zip file (xposed-vXX-sdkXX-x86.zip) to your virtual device display to flash the device.
Drag’n drop Xposed Installer APK (XposedInstaller_*.apk). This should install and launch Xposed Installer application. At this stage, it will display that the Xposed framework is installed but disabled:
Reboot the device with adb reboot command. Do not reboot from *Xposed Installer* as this will freeze the device.
Launch Xposed installer. It should display “Xposed Framework version XX is active”:
Android 8.0
Xposed only works with Android 5.0 to 7.1. For Android 8.0, you need to use Magisk + Edxposed instead.
Drag’n Drop Magisk Manager apk: Magisk-v23.0.apk. Magisk Manager will install and open. Close it for now.
Drag’n Drop Magisk_rebuilt_1c8ebfac_x86.zip and flash it.
When flashing is complete, reboot the device.
Launch Magisk Manager. It will request ROOT access, select “Remember choice forever” and click Allow:
It is possible that the popup opens in the background and is covered by Magisk Manager main window. If so press back to access the popup and allow ROOT:
You will then be prompted with an update to apply, accept it:
The device will reboot one more time. Launch Magisk Manager again, you should now be informed that Magisk is now installed in 1c8ebfac(23015) version:
Step 2: Install Riru
Important
Do not install the Riru version available in the Magisk Manager app. Use the old Riru v25 version provided in this article (see prerequisite).
Drag’n drop the Riru archive onto the instance display: riru-v25.4.4-release.zip. Do not flash it! The archive must be installed from Magisk Manager.
Launch Magisk Manager app and click on the last icon in the bottom toolbar to go to the module section:
Click “install from storage”:
Go to the Download folder from the menu:
Select the Riru archive, riru-v25.4.4-release.zip
Reboot the device
Riru version 25 should now be present in the installed modules list in Magisk Manager:
Important
Make sure NOT to update to Riru v26 as it does not work with EdXposed right now.
Step 3: Install EdXposed
You can install EdXposed framework from Magisk Manager. Go to Magisk Manager module manager:
Open the search widget and input “Edxposed”. Select Riru - EdXposed:
Install the module:
Reboot the device.
Drag’n drop Edxposed manager APK file (EdXposedManager-4.5.7-45700-org.meowcat.edxposed.manager-release.apk) to the device display.
Reboot the device
Edxposed manager should launch and display “Edxposed framework is active”:
Android 8.1 and above
Edxposed and Xposed are no longer maintained and there are no builds for Android 12 and above.
Instead, we will use LSPosed and Magisk for Android 8.1 and above.
Drag’n Drop Magisk Manager apk: Magisk-v23.0.apk. Magisk Manager will install and open. Close it for now.
Drag’n Drop the flashable archive:
Magisk_rebuilt_1c8ebfac_x86.zip if you use Android 8.1 - 10
Magisk_rebuilt_1c8ebfac_x86_64.zip if you use Android 11 and above on a PC or an old Mac Intel
Magisk_rebuilt_1c8ebfac_arm64.zip if you use a mac M1/M2
When flashing is complete, reboot the device.
Launch Magisk Manager. It will request ROOT access, select “Remember choice forever” and click Allow:
It is possible that the popup opens in the background and is covered by Magisk Manager main window. If so press back to access the popup and allow ROOT:
You will then be prompted with an update to apply, accept it:
The device will reboot one more time. Launch Magisk Manager again, you should now be informed that Magisk is now installed in 1c8ebfac(23015) version:
Step 2: Install Riru
Important
Do not install the Riru version available in the Magisk Manager app. Use the old Riru v25 version provided in this article (see prerequisite).
Drag’n drop the Riru archive onto the instance display: riru-v25.4.4-release.zip. The flashing process will fail, but this is normal. The archive must be installed from Magisk Manager.
Launch Magisk Manager app and click on the last icon in the bottom toolbar to go to the module section:
Click “install from storage”:
Go to the Download folder from the menu:
Select the Riru archive, riru-v25.4.4-release.zip
Reboot the device
Riru version 25 should now be present in the installed modules list in Magisk Manager:
Step 3: Install Riru - LSPosed
Drag and drop the LSPosed archive to the device. Do not flash it!
Open Magisk Manager, go to the plugin manager page:
click Install from storage and select LSPosed-v1.8.6-6712-riru-release.zip:
Reboot the device when prompted
Drag’n Drop LSPosed_manager.apk, LSPosed manager should open: