Shellshock in Feathersjs
How Shellshock Manifests in Feathersjs
Shellshock (CVE-2014-6271) is a vulnerability in the Bash shell that allows arbitrary command execution when specially crafted environment variables are processed. In a Feathersjs application the risk appears when developer code takes user‑controlled data (e.g., request headers, query strings, or payload fields) and places it into the environment before spawning a Bash shell. A common pattern is a service hook that logs request information and then executes a shell command for debugging or administrative tasks.
// feathers-service/hooks/shell-logger.js
const { exec } = require('child_process');
module.exports = function (context) {
// context.params.headers may contain User-Agent, Referer, etc.
const ua = context.params.headers['user-agent'] || '';
// Unsafely echo the header into a shell command
exec(`echo \"User-Agent: ${ua}\"`, (err, stdout, stderr) => {
if (err) console.error(err);
else console.log(stdout);
})
return context;
};
If the Feathers service is exposed without authentication, an attacker can send a request with a Shellshock payload in the User-Agent header:
GET /users HTTP/1.1
Host: api.example.com
User-Agent: () { :; }; /bin/cat /etc/passwd
...
Because the header value is interpolated directly into the command string and the environment is inherited by the spawned Bash process, the payload executes, leading to command injection. This maps to OWASP API Top 10 A03:2021 (Injection) and is exploitable on any Feathersjs endpoint that uses child_process.exec (or spawn with shell:true) and mixes unsanitized request data into the command or environment.
Feathersjs-Specific Detection
Detecting Shellshock in a Feathersjs API requires observing whether user‑supplied data can reach a Bash interpreter. middleBrick performs unauthenticated, black‑box testing and can reveal this issue without source access. When scanning an endpoint, middleBrick injects Shellshock‑style payloads into HTTP headers (User‑Agent, Referer, X‑Forwarded‑For, Cookie) and into query string parameters, then looks for signs of command execution in the response (e.g., contents of /etc/passwd, error messages, or timing differences).
To run a scan from the terminal:
npx middlebrick scan https://api.example.com/users
The output will include a finding under the "Command Injection" category if the payload is executed. Example finding:
- Endpoint: GET /users
- Parameter: Header User-Agent
- Payload: () { :; }; /bin/cat /etc/passwd
- Evidence: Response contains "root:x:0:0:root:/root:/bin/bash"
- Severity: High
- Remediation Guidance: Avoid interpolating user data into shell commands; use
execFilewith an argument array and{shell:false}; validate and whitelist input.
If you have access to the source code, you can also search for patterns like exec(\`.*${.*}\` or spawn(.* , {shell:true}) within Feathers service files or hooks. middleBrick’s dashboard will track the finding over time, showing whether the score improves after a fix.
Feathersjs-Specific Remediation
The most reliable remediation is to eliminate the use of a Bash shell for tasks that can be performed with Node.js built‑in APIs. If a shell command is unavoidable, follow these Feathersjs‑specific steps:
- Replace
execwithexecFile: Use an array for arguments and setshell:falseso that the input is never interpreted by Bash. - Never inherit the full process environment when spawning a child process. Pass a clean environment object:
- Validate and whitelist input at the Feathers hook level. Use a schema validator (e.g.,
ajvor Feathers’ built‑invalidatehook) to reject any header that does not match an expected pattern. - Keep the underlying Bash patched on the host operating system. Even with safe coding, an outdated Bash could be exploited if any other process inadvertently invokes it.
// Fixed hook
const { execFile } = require('child_process');
module.exports = function (context) {
const ua = context.params.headers['user-agent'] || '';
// Sanitize: allow only alphanumerics, spaces, hyphens and underscores
const safeUa = ua.replace(/[^\w\s-]/g, '');
execFile('echo', ['User-Agent: ' + safeUa], (err, stdout, stderr) => {
if (err) console.error(err);
else console.log(stdout);
})
return context;
};
execFile('echo', ['User-Agent: ' + safeUa], {
env: { PATH: process.env.PATH } // only what you need
}, callback);
// feathers-service/hooks/validate-ua.js
const { BadRequest } = require('@feathersjs/errors');
module.exports = function () {
return async context => {
const ua = context.params.headers['user-agent'];
if (!/^[\w\s\-]{1,100}$/.test(ua)) {
throw new BadRequest('Invalid User-Agent header');
}
return context;
};
};
After applying these changes, rerun middleBrick:
npx middlebrick scan https://api.example.com/users
The Scanner should now report a passing score (A or B) for the Command Injection check, confirming that the Shellshock vector has been eliminated.
Frequently Asked Questions
Can middleBrick detect Shellshock if the Feathersjs service only runs in a container without Bash?
Is it sufficient to only update Bash on the host to protect my Feathersjs API?
child_process.execFile with {shell:false}—is required for comprehensive protection.