Sceawere
Vulnerability Detail
CVE-2026-82417UPDATED Verified Sceawere Triage Sources: NVD / CISA KEV
qs Prototype Pollution Denial of Service
Vulnerability Metadata
- Severity
- Medium
- Score / CVSS
- 5.3
- Creation Date
- 4h ago
- Vendor
- ljharb
- Product
- qs
- Attack Type
- CWE-248 Uncaught Exception
- Vector String
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
- Attack Complexity
- LOW
Narrative and Response
Description
### Summary `qs.stringify` throws a `TypeError` when it serializes an object whose own `constructor` property has a truthy, non-callable `isBuffer` member. `utils.isBuffer` duck-types buffers by calling `obj.constructor.isBuffer(obj)` after checking only that the property is truthy, so a value such as `{ constructor: { isBuffer: "x" } }` makes the call throw `TypeError: obj.constructor.isBuffer is not a function`. ### Details `lib/stringify.js:127` calls `utils.isBuffer` on every non-primitive value it serializes. `utils.isBuffer` (`lib/utils.js:332`) reads `obj.constructor.isBuffer` and invokes it without verifying that it is a function. `constructor` and `isBuffer` are ordinary property names, so any object carrying them as own properties reaches the unchecked call. Such an object can be built from untrusted input. `qs.parse("x[constructor][isBuffer]=y", { plainObjects: true })` or `{ allowPrototypes: true }` keeps the `constructor` key as an own property (the default parse options drop it), and `JSON.parse("{\"a\":{\"constructor\":{\"isBuffer\":\"x\"}}}")` produces the same shape with no qs option involved. Express 4 with its default `query parser` setting and body-parser with `extended: true` both call `qs.parse` with `allowPrototypes: true`, so on those stacks `req.query` and `req.body` can carry the shape directly. #### PoC ```js var qs = require("qs"); qs.stringify(qs.parse("x[constructor][isBuffer]=y", { plainObjects: true })); qs.stringify(JSON.parse("{\"a\":{\"constructor\":{\"isBuffer\":\"x\"}}}")); // TypeError: obj.constructor.isBuffer is not a function // at Object.isBuffer (lib/utils.js:332:78) // at stringify (lib/stringify.js:127:45) ``` #### Fix `lib/utils.js`, applied in e83d321 on `main` and released as v6.16.0: ```diff - return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); + return !!(obj.constructor && typeof obj.constructor.isBuffer === "function" && obj.constructor.isBuffer(obj)); ``` Real `Buffer`, `safer-buffer`, and browserify `buffer` polyfill instances serialize exactly as before; only the throw is removed. ### Affected versions `>=2.2.5 <6.16.0`, fixed in v6.16.0. The unguarded duck-type was introduced in 3768a75 and first shipped in v2.2.5 (September 2014). v2.2.4 and earlier used `Buffer.isBuffer` and are not affected. Every release from v2.2.5 through v6.15.3 contains the unguarded call. ### Impact An unauthenticated request can make any code path that re-serializes attacker-influenced data with `qs.stringify` (for example, rebuilding a query string from `req.query` for a redirect or an upstream request, or serializing a parsed JSON body) throw synchronously. In a typical Node.js HTTP framework the throw is caught by the framework error boundary and the affected request returns a 500; the process survives and other requests are unaffected. Where the call runs outside an error boundary, such as an `async` Express 4 handler (where the throw becomes an unhandled promise rejection) or a background job, the process exits, so the impact in that case depends on the application error handling rather than on qs.
Executive Summary
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Technical Details
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Mitigations
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
References
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Additional Metadata
{
"score": "5.3",
"pubDate": "2026-08-30T00:16:34.657Z",
"pubdate": "2026-08-30T00:16:34.657Z",
"executiveSummary": "A denial-of-service vulnerability exists in the qs library due to an improper type check when handling the constructor property during object serialization.\nThe vulnerability allows an unauthenticated attacker to supply a crafted object containing a non-callable property at obj.constructor.isBuffer, which triggers a TypeError during execution of qs.stringify.\nThis affects versions >=2.2.5 and <6.16.0 of the qs package.\nThe impact is significant for applications that re-serialize user-controlled input, such as query parameters or JSON bodies, as it leads to synchronous process crashes or unhandled promise rejections.\nIn environments like Express 4 using default configurations, the risk is elevated because the framework may preserve these malicious property shapes during parsing.\nSuccessful exploitation requires no authentication and relies on the application's propensity to serialize attacker-influenced data structures.",
"technicalDetails": "The root cause of this vulnerability lies in the implementation of the utility function utils.isBuffer located in lib/utils.js. The function attempts to determine if an object is a Buffer instance by checking the truthiness of obj.constructor.isBuffer and then unconditionally invoking it as a function.\nBecause the check only verifies that the property exists and is truthy, an attacker can pass an object containing an 'own' property where constructor.isBuffer is set to a non-callable value (e.g., a string or boolean).\nWhen qs.stringify encounters such an object during the serialization process (specifically at lib/stringify.js:127), it invokes utils.isBuffer. The subsequent attempt to execute a non-callable property as a function results in a TypeError: obj.constructor.isBuffer is not a function.\nThe attack flow involves injecting a nested object structure, such as 'x[constructor][isBuffer]=y', via query parameters or JSON payloads. In systems configured to allow prototype modification or those utilizing parsers with permissive options like 'allowPrototypes: true' or 'plainObjects: true', this malicious structure is successfully parsed into a standard JavaScript object.\nOnce this object reaches a downstream call to qs.stringify, the serialization engine attempts to validate the buffer status of the object, triggering the exception.\nThe impact depends on the execution context of the application. In standard synchronous request handlers, the exception may be caught by existing middleware, resulting in a 500 Internal Server Error response. However, in asynchronous contexts—such as async/await patterns in Express 4—this synchronous exception can result in an unhandled promise rejection.\nUnhandled rejections in Node.js can lead to the termination of the process, effectively causing a denial-of-service condition for the entire application, not just the specific request path.\nThe vulnerability affects all versions of qs from v2.2.5 through v6.15.3. It was introduced via a change in commit 3768a75 and was addressed in v6.16.0 by adding a typeof check to ensure the property is a function before invocation."
}