HIGH container escaperestify

Container Escape in Restify

How Container Escape Manifests in Restify

Restify is a lightweight Node.js framework for building RESTful APIs. By itself it does not create container isolation boundaries, but the handlers you attach to Restify routes can inadvertently expose paths that lead to a container escape when the container runs with elevated privileges or has sensitive mounts (e.g., the Docker socket).

The most common pattern is a route that accepts user‑supplied input and passes it directly to a Node.js child process without proper sanitization. If the container is privileged or has /var/run/docker.sock mounted, an attacker can execute arbitrary host commands, effectively breaking out of the container.

const restify = require('restify');
const { exec } = require('child_process');

const server = restify.createServer();

// Vulnerable endpoint: executes arbitrary shell commands
server.post('/run', (req, res, next) => {
  const cmd = req.body.command; // user‑controlled string
  exec(cmd, (error, stdout, stderr) => {
    if (error) {
      res.send(500, { error: error.message });
    } else {
      res.send(200, { output: stdout });
    }
    next();
  });
});

server.listen(3000, () => console.log('Restify API listening on :3000'));

In this example, a POST to /run with { "command": "rm -rf /" } would delete files inside the container. If the container is started with --privileged or mounts the Docker socket, the same endpoint could be used to run docker run -v /:/host alpine chroot /host sh and gain root access on the host — a classic container escape.

Another vector is exposing the Docker socket through a misconfigured Restify service that proxies Docker API calls. An attacker who can reach /docker/containers/create can start a new container with hostile mounts, again achieving escape.

Restify‑Specific Detection

Detecting the conditions that could lead to a container escape relies on spotting unsafe usage of Node.js child‑process APIs and checking for exposed privileged mounts. middleBrick’s unauthenticated black‑box scan includes an Input Validation check that looks for patterns where user‑controlled data reaches dangerous functions such as child_process.exec, spawn, or eval. It also flags endpoints that return detailed error messages revealing internal paths, which can aid an attacker in crafting escape payloads.

When you submit a URL to middleBrick, the scanner:

  • Enumerates all reachable Restify routes (via standard HTTP methods).
  • Analyzes request/response pairs for reflections of input in error messages, headers, or body.
  • Applies heuristic signatures for known dangerous sinks (e.g., strings passed to exec without whitelisting).
  • Reports any finding with severity, location (URL and HTTP method), and remediation guidance.

Example CLI usage:

# Scan a Restify API hosted at https://api.example.com
npx middlebrick scan https://api.example.com

The output will include a finding similar to:

Input Validation – High
Endpoint: POST /run
Description: User‑supplied "command" parameter is passed directly to child_process.exec without sanitization.
Remediation: Use execFile with a whitelist of allowed commands or avoid shell execution entirely.

If the scanner detects that the host’s Docker socket is reachable (e.g., via a REST proxy that forwards to /var/run/docker.sock), it will raise a finding under the BFLA/Privilege Escalation category, indicating a potential container escape path.

Restify‑Specific Remediation

Fixing the issue involves eliminating the unsafe flow of user input into privileged operations and applying defense‑in‑depth hardening. Below are Restify‑native strategies that address the most common escape vectors.

1. Avoid shell execution; use execFile with a whitelist

Replace exec with execFile, which does not invoke a shell, and restrict the executable to a known safe binary.

const { execFile } = require('child_process');
const ALLOWED_CMDS = ['/usr/bin/ffmpeg', '/usr/bin/convert'];

server.post('/process', (req, res, next) => {
  const { cmd, args } = req.body;
  if (!ALLOWED_CMDS.includes(cmd)) {
    return res.send(400, { error: 'Command not allowed' });
  }
  execFile(cmd, args, (error, stdout, stderr) => {
    if (error) {
      return res.send(500, { error: error.message });
    }
    res.send(200, { output: stdout });
    next();
  });
});

2. Validate and sanitize all inputs with Restify plugins

Use restify-plugins or a validation library like Joi to enforce strict schemas before any logic runs.

const restify = require('restify');
const Joi = require('joi');
const server = restify.createServer();

server.use(restify.plugins.bodyParser({ mapParams: false }));

const processSchema = Joi.object({
  cmd: Joi.string().valid('ffmpeg', 'convert').required(),
  args: Joi.array().items(Joi.string()).max(5)
});

server.post('/process', (req, res, next) => {
  const { error, value } = processSchema.validate(req.body);
  if (error) {
    return res.send(400, { error: error.details[0].message });
  }
  // safe to use value.cmd and value.args here
  next();
});

3. Drop privileges and isolate the container

Even with safe code, run the Restify service as a non‑root user and avoid mounting /var/run/docker.sock unless absolutely required. If the socket is needed, place it behind an authentication proxy and never expose it to unauthenticated API endpoints.

By combining these Restify‑specific fixes — eliminating dangerous child‑process patterns, enforcing strict input validation, and minimizing privileged container capabilities — you remove the realistic paths that could lead to a container escape.

Frequently Asked Questions

Can middleBrick fix the container escape vulnerability in my Restify API?
No. middleBrick only detects and reports security issues. It provides detailed findings with severity, location, and remediation guidance, but it does not apply patches, block traffic, or modify your code.
What specific Restify code patterns does middleBrick look for when scanning for container escape risks?
middleBrick’s Input Validation check looks for routes where user‑controlled parameters are passed directly to Node.js child‑process APIs such as child_process.exec, spawn, or eval without sanitization or whitelisting. It also flags endpoints that leak internal paths or expose privileged sockets like Docker’s, which could be abused for escape.