CVE-2024-21892 - Node.js Linux Privilege Escalation via Environment Variable Handling Bug
*Published: June 2024*
Quick Summary
A new security vulnerability, CVE-2024-21892, was recently revealed in Node.js on Linux systems. This bug lets unprivileged users inject malicious environment variables, causing Node.js apps to run attacker-controlled code with elevated privileges if certain Linux capabilities are set. Even worse, you only needed *some* elevated capabilities—not just CAP_NET_BIND_SERVICE, as intended.
Let’s dig in to understand what went wrong, look at real code, and see how you can protect your systems.
What’s Supposed to Happen?
Modern servers often use capabilities to grant network or file privileges without a full root user. Node.js knows that if a process is running as root (either fully or partially—e.g., has CAP_NET_BIND_SERVICE to bind ports under 1024), it must ignore potentially unsafe environment variables such as NODE_OPTIONS, LD_PRELOAD, etc., *if* those variables were set by an unprivileged user who launched it.
In plain language:
> For better security, Node.js won’t trust certain environment variables if the current privileges could be dangerous and those env variables were set by a regular user.
This protects, for example, against a user running
NODE_OPTIONS="--require /tmp/malware.js" sudo node server.js
which would load the attacker's code with root privileges.
EXCEPTION: The only time Node.js should *not* ignore these variables is if the *ONLY* capability set is CAP_NET_BIND_SERVICE.
What’s the Bug? (CVE-2024-21892 Details)
Here's the real problem. Due to a bug, Node.js forgot to check if there are other capabilities set besides CAP_NET_BIND_SERVICE. So, if *any* other capability is present (like CAP_SYS_ADMIN), Node.js *should* treat the process as privileged and ignore the dangerous environment variables.
What does this mean?
Imagine an attacker can run a process with a capability like CAP_SYS_ADMIN (very powerful!), and they set NODE_OPTIONS. Node.js will *not* ignore that variable and will run attacker-supplied code as the privileged process.
Suppose you have access to a server where node is installed with capabilities (set via setcap)
# Give /usr/bin/node the ability to bind ports as root and manage system settings
sudo setcap 'cap_net_bind_service,cap_sys_admin+ep' /usr/bin/node
An attacker can now do
export NODE_OPTIONS="--require /tmp/malware.js"
node server.js
Since /usr/bin/node has both cap_net_bind_service and cap_sys_admin, Node.js should've ignored NODE_OPTIONS, but due to this bug, it doesn't.
/tmp/malware.js now runs with both those capabilities—in effect, *privilege escalation*.
Example malware.js
// /tmp/malware.js
const { exec } = require('child_process');
exec('id > /tmp/rooted.txt');
Afterwards
cat /tmp/rooted.txt
# Should contain the output of running as a privileged user!
Why is This Bad?
- Attackers can inject code into Node.js apps that run with system-level capabilities — even if they’re not the root user!
- The bug affects any Node.js deployment where binaries have extra capabilities, often the case for network services.
Technical Deep Dive: Where’s the Problem?
This occurs because Node.js checks for CAP_NET_BIND_SERVICE (capability to bind to ports < 1024), but doesn’t carefully check if other capabilities are also present.
In code:
The vulnerable logic looks like (conceptual)
// Pseudocode
if (has_cap_net_bind_service) {
// Allow env variables
} else {
// Ignore env variables
}
But it needs to be
if (has_only_cap_net_bind_service) {
// Allow env variables
} else {
// Ignore env variables
}
In other words, it must ONLY allow if the *sole* capability is net_bind_service.
Node.js has released fixes for all supported versions as of mid-June 2024.
- See their advisory: Node.js Security Release: June 2024
References
- Original CVE Entry — CVE-2024-21892
- Node.js Security Advisory
- NPR: Node.js ignores env variables even with dangerous capabilities (GitHub link to patch/review)
- Linux Capabilities Overview
Final Thoughts
CVE-2024-21892 is a classic example of how a subtle security misstep—like not precisely checking *all* process capabilities—can lead to serious privilege escalation in the real world. If you run Node.js on Linux, update immediately and scrub your environment for unnecessary privileges. Never trust environment variables sent from unprivileged sources when running privileged code!
Stay safe, and keep your Node.js secure!
*You may repost, but please link back to this page for attribution.*
Timeline
Published on: 02/20/2024 02:15:50 UTC
Last modified on: 02/20/2024 19:50:53 UTC