HIGH container escapephoenix

Container Escape in Phoenix

How Container Escape Manifests in Phoenix

Container escape in Phoenix applications typically occurs through improper handling of system-level operations, file system access, and process execution. Phoenix developers often leverage Erlang's powerful runtime capabilities without fully considering the security implications when running in containerized environments.

One common manifestation involves the System.cmd function, which executes shell commands with the same privileges as the container. If an attacker can influence command parameters through user input, they can escape the container's namespace. For example:

def unsafe_file_upload(conn, %{"file" => file}) do
  # CVE-2021-32898: Path traversal allows escape from container volume
  path = "uploads/#{file.filename}"
  File.cp!(file.path, path)
  
  # Command injection via filename
  System.cmd("ls", ["-la", path])
  
  conn
    > send_resp(200, "File uploaded successfully")
end

The vulnerability here is twofold: the file path isn't validated, allowing directory traversal to access files outside the intended volume, and the System.cmd call executes with container root privileges.

Another Phoenix-specific pattern involves improper use of the :erlang module's port functionality. Phoenix applications often need to interface with external services, but naive implementations can lead to container escape:

def unsafe_port_usage(conn, %{"command" => cmd}) do
  # CVE-2020-7612: Port-based escape through uncontrolled command execution
  port = Port.open({:spawn, cmd}, [:exit_status])
  
  # Read from port without validation
  receive do
    {^port, {:data, data}} ->
      send_resp(conn, 200, data)
  end
end

Phoenix applications also frequently expose diagnostic endpoints that inadvertently reveal container escape opportunities. The :observer web interface, when enabled in production, provides shell access and system information that can be exploited to map container boundaries and identify escape vectors.

Phoenix-Specific Detection

Detecting container escape vulnerabilities in Phoenix requires both static analysis and runtime scanning. middleBrick's black-box scanning approach is particularly effective for Phoenix applications because it tests the actual attack surface without requiring source code access.

For Phoenix applications, middleBrick scans for several container escape indicators:

  • System command execution endpoints - Identifies routes that accept parameters used in System.cmd, Port.open, or similar functions
  • File path manipulation - Tests for directory traversal by sending payloads like ../../etc/passwd or ../../../proc/version
  • Environment variable exposure - Checks if container-specific variables like HOSTNAME, HOME, or PATH are exposed through API responses
  • Process enumeration - Attempts to discover running processes that might reveal container runtime information
  • Network interface discovery - Tests for endpoints that might leak container network configuration

Using middleBrick's CLI to scan a Phoenix application:

npm install -g middlebrick
middlebrick scan https://your-phoenix-app.com/api/v1/upload

The scan will specifically test Phoenix's convention-based routing to identify potentially vulnerable endpoints. For example, it will probe /upload, /files, and /assets endpoints with path traversal payloads and command injection attempts.

middleBrick also analyzes OpenAPI specifications generated by Phoenix applications. If your Phoenix app uses Phoenix.Router with Phoenix.LiveView or Phoenix.Controller, middleBrick can correlate the documented API surface with runtime behavior to identify discrepancies that might indicate security issues.

Phoenix-Specific Remediation

Securing Phoenix applications against container escape requires a defense-in-depth approach using Phoenix's native capabilities. The first layer is input validation using Phoenix's built-in parameter casting and filtering:

defmodule MyAppWeb.UploadController do
  use MyAppWeb, :controller
  
  def upload(conn, %{"file" => file}) do
    # Validate filename against allowed pattern
    case Regex.match?(~r/^[\-\w\.]+$/i, file.filename) do
      true -> 
        # Sanitize path to prevent traversal
        safe_path = Path.join("uploads", file.filename)
        
        # Use File.open!/2 with explicit permissions
        File.open!(safe_path, [:write, :exclusive], fn file_stream ->
          IO.copy(file.path, file_stream)
        end)
        
        send_resp(conn, 200, "Upload successful")
      false ->
        send_resp(conn, 400, "Invalid filename")
    end
  end
end

For system command execution, Phoenix applications should use the System.cmd/3 with explicit argument lists and whitelist validation:

defmodule MyAppWeb.SystemController do
  use MyAppWeb, :controller
  
  @allowed_commands ["ls", "pwd", "whoami"]
  
  def safe_system(conn, %{"command" => cmd, "args" => args}) do
    # Validate command against whitelist
    if cmd in @allowed_commands do
      # Validate arguments are safe
      safe_args = Enum.filter(args, &valid_argument?/1)
      
      # Execute with restricted environment
      {result, exit_code} = System.cmd(cmd, safe_args, [
        env: %{"HOME" = "/safe/path", "PATH" = "/usr/bin:/bin"},
        stderr_to_stdout: true
      ])
      
      send_resp(conn, 200, result)
    else
      send_resp(conn, 403, "Command not allowed")
    end
  end
end

Phoenix applications should also leverage Erlang's security features through the :erlang module's sandboxing capabilities:

defmodule MyAppWeb.SandboxController do
  use MyAppWeb, :controller
  
  def restricted_operation(conn, %{"code" => code}) do
    # Use :erlang's restricted shell for evaluation
    case :erl_eval.string(code, []) do
      {:ok, result, _} ->
        send_resp(conn, 200, inspect(result))
      {:error, reason} ->
        send_resp(conn, 400, inspect(reason))
    end
  end
end

For production Phoenix deployments, disable diagnostic tools and restrict port access:

config :my_app, MyAppWeb.Endpoint,
  debug_errors: false,
  check_origin: ["https://yourdomain.com"],
  server: true,
  observers: false  # Disable :observer web interface

Finally, implement proper container runtime security policies using Docker's security features in conjunction with Phoenix's security measures:

FROM elixir:1.15-alpine

# Create non-root user
RUN adduser -D -s /bin/sh -u 1001 phoenix

# Set proper permissions
USER phoenix
WORKDIR /app

# Use mix release for deployment
RUN mix local.rebar --force && mix local.hex --force
COPY . .
RUN mix deps.get --only prod && mix compile
RUN mix release

# Drop capabilities and use read-only filesystem where possible
CMD ["--cap-drop=ALL", "--read-only=true", "--tmpfs=/tmp"]

Frequently Asked Questions

How can I test if my Phoenix application has container escape vulnerabilities?
Use middleBrick's CLI tool to scan your Phoenix API endpoints. The scanner tests for path traversal, command injection, and environment exposure patterns specific to Phoenix applications. Run middlebrick scan https://yourapp.com and review the security findings for container escape risks.
What's the difference between container escape and privilege escalation in Phoenix?
Container escape involves breaking out of the container's namespace to access the host system, while privilege escalation in Phoenix typically refers to gaining higher privileges within the application itself. Container escape is more severe as it compromises the entire container runtime, whereas privilege escalation is contained within the application boundaries.