In June 2024, CVE-2024-42330 shed light on a subtle but powerful security flaw involving the HttpRequest object in common JavaScript libraries and environments. The vulnerability hinges on how the HTTP response headers are exposed to client-side JavaScript—specifically, how the resulting strings are parsed without sufficient validation or encoding. Unsanitized headers can secretly insert special property strings, allowing attackers to reach and manipulate hidden or internal properties of JavaScript objects—leading straight to prototype pollution.

In this exclusive long read, we’ll break down the problem, see exploit code in action, understand the real risk, and point you to more technical references. By the end, you’ll know exactly why this vulnerability deserves your attention.

What is the Vulnerability?

Imagine you send an HTTP request using the typical HttpRequest (or similar XMLHttpRequest or fetch) object in JavaScript. When a response comes back, you get the HTTP headers, usually as a set of key/value pairs in a method like getResponseHeader.

Normally, you’d expect these headers to be just harmless strings. However, if those headers include _dangerous or malformed_ values—strings that JavaScript uses for objects’ internal or hidden properties—bad things can happen. That’s *exactly* the problem with CVE-2024-42330:

> The HttpRequest object allows you to get unfiltered HTTP headers as strings, but those strings are not properly escaped or encoded for use in JavaScript. Maliciously crafted headers can result in hidden properties being set or accessed on in-memory JavaScript objects, opening the door to prototype pollution and similar attacks.

Where’s the Bug?

The server can respond with any headers it likes – including ones with special names or values. If your JavaScript code naively reads these in and adds them to an object (as is common in header-parsing libraries), JavaScript’s flexible property system can be abused to set properties like __proto__, constructor, or even newer internal fields.

Let’s see some pseudo code that looks safe, but isn’t

function getHeadersObject(request) {
    const headersString = request.getAllResponseHeaders();
    const headers = {};
    headersString.split('\n').forEach(line => {
        const [key, value] = line.split(': ');
        if (key && value) {
            headers[key.trim()] = value.trim();
        }
    });
    return headers;
}

Now imagine the server sends back the HTTP header

__proto__: [object Exploit]

That causes

headers["__proto__"] = "[object Exploit]";

In JavaScript, assigning __proto__ actually changes the prototype of headers (or possibly worse, all objects if not isolated properly!). This is the classic footgun of "prototype pollution," but here it's enabled by how HTTP header names are slurped up directly and blindly into an object.

Exploit Details

Let’s walk through a simple, practical exploit. Assume you have an environment where the response headers are exposed with no sanitization (common, for example, in some Node.js server frameworks):

`

HTTP/1.1 200 OK
Content-Type: text/html

const headers = getHeadersObject(request);

// headers.__proto__.polluted === "yes"

`

4. This can impact code anywhere that relies on plain objects, especially when object property enumeration or merging is involved.

Attack Chaining

Clever attackers may target web apps that use the polluted headers object for configuration, file paths, permissions, etc. This can result in deep application compromise.

Don’t trust HTTP header names: Always sanitize input, especially before using as object keys.

2. Block reserved words: Refuse or clean headers like __proto__, prototype, constructor, or those starting with symbols (e.g., Symbol-like fields).
3. Consider using Maps, not plain objects: Maps don’t use prototypes and can help avoid this class of bug.
4. Escape/unescape carefully: If you must pass arbitrary header names into JavaScript objects, encode them safely first.

Here’s an updated, safe snippet

function getSafeHeadersObject(request) {
    const headersString = request.getAllResponseHeaders();
    const forbidden = ['__proto__', 'prototype', 'constructor'];
    const headers = Object.create(null); // prototype-less!
    headersString.split('\n').forEach(line => {
        const [key, value] = line.split(': ');
        if (key && value && !forbidden.includes(key.trim())) {
            headers[key.trim()] = value.trim();
        }
    });
    return headers;
}

References and Learn More

- CVE Record for CVE-2024-42330
- OWASP: Prototype Pollution
- Mozilla Docs: XMLHttpRequest.getAllResponseHeaders()
- HackerOne: Prototype Pollution Exploits
- Node.js Security WG: Prototype Pollution

Conclusion

CVE-2024-42330 is a sobering reminder: *never* assume server data is safe—even when it’s just headers! The subtle bridging of HTTP protocol and JavaScript’s flexible object system here means a single unsanitized string can open your whole app to exploitation.

Patch now: sanitize headers, use safer containers, and audit your HTTP-handling code paths. Security, as always, is everyone’s job.

Timeline

Published on: 11/27/2024 12:15:21 UTC