| Title | meteor https://www.meteor.com/ <=3.2.1 Inefficient Regular Expression Complexity |
|---|
| Description | # ReDoS Vulnerability in ddp-server/_clientAddress via x-forwarded-for Header
**Vulnerability Details:**
* **Type:** Regular Expression Denial of Service (ReDoS)
* **Affected Component:** `packages/ddp-server/livedata_server.js`, specifically within the `Session.prototype._clientAddress` method.
* **Vulnerable Code Snippet:**
```javascript
forwardedFor = forwardedFor.trim().split(/\s*,\s*/);
```
* **Problematic Regex:** `/\s*,\s*/`
**Condition for Exploitation:**
This vulnerability can be triggered only when the Meteor server's environment variable `HTTP_FORWARDED_COUNT` is set to an integer value greater than 0.
**Input Source & Impact:**
When the condition above is met, the code processes the `x-forwarded-for` HTTP header value. A malicious client can send a crafted `x-forwarded-for` header containing a large number of whitespace characters followed by a non-comma character. Due to catastrophic backtracking in the vulnerable regex, this can cause the server process handling the DDP connection to consume excessive CPU, leading to a Denial of Service.
**Proof of Concept (PoC):**
1. Set the `HTTP_FORWARDED_COUNT` environment variable to `1` (or higher) on the Meteor server.
2. Initiate a DDP connection to the server.
3. Include an `x-forwarded-for` HTTP header with a value consisting of a very large number of space characters followed by a non-comma character (e.g., `' '.repeat(LARGE_NUMBER) + 'a'`).
4. Observe the server process for high CPU usage and unresponsiveness.
**Suggested Fix:**
To mitigate this ReDoS risk and improve clarity, replace the regex-based split with a safer approach using `String.prototype.split` with a simple delimiter and `Array.prototype.map` for trimming:
```diff
// In packages/ddp-server/livedata_server.js, within Session.prototype._clientAddress method
// Original vulnerable line:
- forwardedFor = forwardedFor.trim().split(/\s*,\s*/);
// Suggested fix:
+ // Trim the entire string (already done before this point in the original code)
+ let parts = forwardedFor.split(',');
+ // Trim each part and filter out empty strings
+ forwardedFor = parts.map(part => part.trim()).filter(part => part.length > 0);
+
+ // Adapt the subsequent logic to use the 'parts' array instead of the direct result of split.
+ // Example assuming the original code intended to get the Nth-to-last element:
+ let clientAddress = null;
+ if (httpForwardedCount <= forwardedFor.length) {
+ clientAddress = forwardedFor[forwardedFor.length - httpForwardedCount];
+ }
+ // return clientAddress; // (Adjust based on actual return logic needed) |
|---|
| Source | ⚠️ https://github.com/meteor/meteor/pull/13721 |
|---|
| User | DayShift (UID 80963) |
|---|
| Submission | 05/03/2025 03:29 (12 months ago) |
|---|
| Moderation | 05/15/2025 09:13 (12 days later) |
|---|
| Status | Accepted |
|---|
| VulDB entry | 309029 [Meteor up to 3.2.1 livedata_server.js Object.assign forwardedFor redos] |
|---|
| Points | 20 |
|---|