Container Escape in Rocket
How Container Escape Manifests in Rocket
Container escape in Rocket applications typically occurs through misconfigured file system access, improper privilege handling, and unsafe API endpoint implementations. The most common vectors involve exposing sensitive file paths, allowing directory traversal, or mishandling user-provided paths that can traverse outside the container's root filesystem.
A critical vulnerability pattern in Rocket involves endpoints that accept file paths without proper validation. Consider an endpoint designed to serve static files:
#[get("/static/")]
async fn serve_file(file_path: String) -> Result<NamedFile> {
let file = NamedFile::open(format!("/static/{} ", file_path))?;
Ok(file)
}
This implementation is vulnerable because it directly interpolates user input into a file path without validation. An attacker can use path traversal sequences like ../../etc/passwd to access files outside the intended directory. The NamedFile::open function will happily open any file the process has permission to read, potentially exposing sensitive configuration files, secrets, or other critical data.
Another common pattern involves file upload endpoints that don't properly sandbox file locations. A vulnerable implementation might look like:
#[post("/upload", data = "<data>")]
async fn upload_file(data: Data) -> Result<String> {
let file_name = data.file_name().unwrap_or("uploaded_file");
let path = format!("/uploads/{}", file_name);
let mut file = File::create(path)?;
data.stream_to(file)?;
Ok("Upload successful".to_string())
}
This code allows attackers to upload files to arbitrary locations by including directory traversal sequences in the filename. An attacker could overwrite critical system files or place executable code in sensitive locations.
Container escape can also occur through improper handling of process execution. If your Rocket application executes external commands using user input without proper sanitization:
#[post("/execute", data = "<command>")]
async fn execute_command(command: String) -> Result<String> {
let output = Command::new("sh").arg("-c").arg(command).output()?;
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
This endpoint provides a direct command injection vector, allowing attackers to execute arbitrary commands within the container, potentially mounting host filesystems or accessing privileged resources.
Rocket-Specific Detection
Detecting container escape vulnerabilities in Rocket applications requires both static code analysis and runtime scanning. Static analysis involves examining route handlers for dangerous patterns like direct path interpolation, unvalidated file operations, and unsafe command execution.
middleBrick's black-box scanning approach is particularly effective for Rocket applications because it tests the actual runtime behavior without requiring source code access. The scanner automatically tests for common container escape patterns by attempting path traversal attacks, testing file access permissions, and probing for command injection vulnerabilities.
For Rocket applications, middleBrick specifically checks for:
- Path traversal vulnerabilities by testing requests like
/static/../../../etc/passwd - Directory listing exposure by requesting directory paths
- File upload sandboxing by attempting to write files outside designated directories
- Command injection by testing parameter manipulation in endpoints that execute system commands
- Excessive file permissions by attempting to read sensitive system files
- Process execution vulnerabilities by testing for unsafe command handling
The scanner's 12 parallel security checks include authentication bypass testing, which is crucial for container escape scenarios where an attacker might first need to bypass authentication to access vulnerable endpoints.
For development teams using Rocket, integrating middleBrick into the CI/CD pipeline provides continuous security validation. The GitHub Action can be configured to scan staging APIs before deployment, ensuring that container escape vulnerabilities are caught early in the development lifecycle.
middleBrick's OpenAPI/Swagger analysis is particularly valuable for Rocket applications, as it can cross-reference the API specification with runtime findings to identify discrepancies between documented and actual behavior that might indicate security issues.
Rocket-Specific Remediation
Securing Rocket applications against container escape requires implementing proper input validation, secure file handling, and safe process execution patterns. Here are Rocket-specific remediation strategies:
For file serving endpoints, use Rocket's built-in path sanitization and validation:
use rocket::http::uri::Origin;
use rocket::response::NamedFile;
use std::path::PathBuf;
#[get("/static/")]
async fn serve_file(file_path: &str) -> Result<NamedFile> {
// Sanitize and validate the path
let sanitized_path = sanitize_path(file_path);
let base_dir = PathBuf::from("/static");
let full_path = base_dir.join(sanitized_path);
// Ensure the resolved path is within the base directory
if !full_path.starts_with(&base_dir) {
return Err(rocket::http::Status::BadRequest.into());
}
NamedFile::open(full_path).await
}
fn sanitize_path(path: &str) -> String {
// Remove path traversal sequences
path.replace("../", "")
.replace("..\\", "")
.replace("..", "")
}
For file upload endpoints, implement strict sandboxing:
use rocket::data::Data;
use rocket::http::ContentType;
use rocket::response::status::BadRequest;
use std::path::PathBuf;
use uuid::Uuid;
#[post("/upload", data = "<data>", format = <content_type>)]
async fn upload_file(content_type: ContentType, data: Data) -> Result<String> {
// Validate content type
if content_type != ContentType::JPEG && content_type != ContentType::PNG {
return Err(BadRequest(Some("Invalid content type")));
}
// Generate a safe, unique filename
let safe_filename = format!("upload_{}.jpg", Uuid::new_v4());
let upload_dir = PathBuf::from("/uploads");
let file_path = upload_dir.join(safe_filename);
// Ensure we're writing to the intended directory
if !file_path.starts_with(&upload_dir) {
return Err(BadRequest(Some("Invalid file path")));
}
// Write the file safely
let mut file = File::create(file_path)?;
data.stream_to(file)?;
Ok("Upload successful".to_string())
}
For command execution endpoints, use Rocket's request guards and Rocket's safe execution patterns:
use rocket::request::FromRequest;
use rocket::http::Status;
use std::process::Command;
struct SafeCommand(String);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for SafeCommand {
type Error = &'r str;
async fn from_request(request: &'r Request<'_, '_>) -> Outcome<Self, Self::Error> {
// Extract and validate command parameter
let command = request.get_query_value("command");
if let Some(command) = command {
let command_str = command.unwrap_or_default();
// Validate against allowed commands
if is_safe_command(&command_str) {
return Outcome::Success(SafeCommand(command_str));
}
}
Outcome::Failure((Status::BadRequest, "Invalid command"))
}
}
fn is_safe_command(command: &str) -> bool {
// Implement a whitelist of allowed commands
let allowed_commands = ["ls", "pwd", "whoami"];
allowed_commands.contains(&command)
}
#[post("/execute")]
async fn execute_command(safe_command: SafeCommand) -> Result<String> {
let output = Command::new(safe_command.0)
.output()?;
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
These remediation patterns demonstrate how Rocket's type system, request guards, and validation features can be used to prevent container escape vulnerabilities. The key principles are input validation, path sanitization, strict sandboxing, and safe process execution.
Frequently Asked Questions
How does middleBrick detect container escape vulnerabilities in Rocket applications?
../../etc/passwd and tests file upload sandboxing without requiring source code access. The scanner runs 12 parallel security checks and provides actionable findings with severity levels and remediation guidance.