Shellshock in Actix
How Shellshock Manifests in Actix
Shellshock, the GNU Bash vulnerability (CVE-2014-6271), allows remote code execution through specially crafted environment variables. While Actix itself is a Rust framework and doesn't use Bash directly, Shellshock vulnerabilities can manifest in Actix applications through several attack vectors.
The most common scenario occurs when Actix applications invoke external processes using system calls. Consider this vulnerable Actix handler:
async fn execute_script(req: HttpRequest) -> impl Responder {
let script_name = req.query_string();
let output = Command::new("bash")
.arg("-c")
.arg(format!("./scripts/{}.sh", script_name))
.output()
.await?;
HttpResponse::Ok().body(output.stdout)
}
An attacker could craft a request like ?script_name=() { :;}; echo VULNERABLE, exploiting the Bash function export feature. When Bash parses the environment, it executes the malicious payload before running the intended script.
Another manifestation appears in Actix applications that use CGI-like patterns or shell out to system utilities:
async fn run_utility(query: web::Query<Params>) -> impl Responder {
let cmd = format!("grep -r '{}' /data", query.pattern);
let output = Command::new("/bin/sh")
.arg("-c")
.arg(cmd)
.output()
.await?;
HttpResponse::Ok().body(output.stdout)
}
Here, unvalidated user input flows directly into a shell command, creating a classic command injection point that Shellshock can exploit.
Actix applications often integrate with Docker or container orchestration where environment variables from HTTP requests might propagate to containerized processes:
async fn deploy_container(req: HttpRequest) -> impl Responder {
let config = req.json::ContainerConfig>().await?;
let mut cmd = Command::new("docker");
cmd.arg("run")
.arg(config.image)
.env("DEPLOY_USER", config.user)
.env("DEPLOY_CONFIG", config.config);
// Shellshock payload could be in config.user or config.config
let output = cmd.output().await?;
HttpResponse::Ok().body(output.stdout)
}
If an attacker controls environment variables passed to Docker, they could inject Shellshock payloads that execute when the container's entrypoint script runs.
Actix-Specific Detection
Detecting Shellshock vulnerabilities in Actix applications requires examining both code patterns and runtime behavior. Here's how to identify potential issues:
Code Pattern Analysis
Search for these dangerous patterns in your Actix codebase:
grep -r "Command::new(" . | grep -E "(bash|sh|dash|zsh)"
grep -r "env(" . | grep -E "(req|query|body|json)"
grep -r "output().await" . | grep -E "(Command|tokio::process)"
Look specifically for these vulnerable patterns:
// VULNERABLE: Direct shell invocation
Command::new("sh").arg("-c").arg(user_input).output().await
// VULNERABLE: Environment variable injection
Command::new("process")
.env("VAR", user_input)
.output().await
// VULNERABLE: System call wrappers
std::process::Command::new("bash")
.args(args)
.spawn()
Runtime Testing
Test your Actix endpoints with Shellshock payloads:
curl -X POST http://localhost:8080/api/execute \
-H "Content-Type: application/json" \
-d '{"script": "() { :;}; echo SHELLSHOCK_DETECTED"}'
Monitor logs for the detection string. Also test with environment variable injection:
curl -X GET "http://localhost:8080/api/search?query=() { :;}; echo VULNERABLE"
Static Analysis with middleBrick
middleBrick's API security scanner can detect Shellshock-related vulnerabilities in Actix applications by analyzing:
- Command injection patterns in route handlers
- Unsafe environment variable usage
- External process invocation without input validation
- CGI-like parameter handling
The scanner examines your API endpoints' attack surface and identifies where user-controlled data flows into potentially dangerous operations. For Actix applications, it specifically flags:
- HTTP parameters that reach shell invocations
- JSON bodies that construct command strings
- Query parameters used in system calls
- Environment variable injection points
middleBrick's continuous monitoring can alert you when new endpoints introduce these vulnerabilities, providing severity ratings and remediation guidance.
Actix-Specific Remediation
Securing Actix applications against Shellshock vulnerabilities requires eliminating unsafe shell invocations and properly validating inputs. Here are Actix-specific remediation strategies:
Replace Shell Invocation with Direct API Calls
Instead of using shell commands, use Rust crates that provide safe APIs:
// VULNERABLE - Shell invocation
async fn list_files(req: HttpRequest) -> impl Responder {
let dir = req.query_string();
let output = Command::new("ls")
.arg("-la")
.arg(dir)
.output()
.await?;
HttpResponse::Ok().body(output.stdout)
}
// SECURE - Use Rust std::fs
async fn list_files(path: web::Path<String>) -> impl Responder {
let dir = sanitize_path(&path);
match std::fs::read_dir(dir) {
Ok(entries) => {
let files: Vec<_> = entries
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().to_string())
.collect();
HttpResponse::Ok().json(files)
}
Err(e) => HttpResponse::InternalServerError().body(e.to_string())
}
}
fn sanitize_path(input: &str) -> String {
// Remove path traversal and special characters
input.replace("../", "")
.replace(";", "")
.replace("|", "")
.replace("&", "")
}
Safe Process Execution with Argument Escaping
When external processes are unavoidable, use safe argument handling:
use tokio::process::Command;
async fn execute_safe(script: web::Path<String>) -> impl Responder {
let script_name = sanitize_filename(&script);
// Use args() instead of shell -c for proper escaping
let output = Command::new("/bin/bash")
.args(["-c", &format!("./scripts/{}.sh", script_name)])
.kill_on_drop(true)
.output()
.await?;
HttpResponse::Ok().body(output.stdout)
}
fn sanitize_filename(name: &str) -> String {
// Allow only alphanumeric, hyphen, underscore, dot
name.chars()
.filter(|c| c.is_alphanumeric() || *c == '-' || *c == '_' || *c == '.')
.collect()
}
Input Validation Middleware
Add Actix middleware to validate all incoming parameters:
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, HttpMessage};
use actix_web::middleware::Middleware;
pub struct InputValidation;
impl Middleware for InputValidation
where
S: actix_web::dev::Service>,
S::Future: 'static,
{
type Response = ServiceResponse;
type Error = actix_web::Error;
type Future = actix_web::dev::BoxFuture<'static, Result Environment Variable Sanitization
Actix applications should sanitize environment variables before use:
async fn run_with_env(req: HttpRequest) -> impl Responder {
let config = req.json::Config>().await?;
// Sanitize all environment variables
let mut sanitized_env = std::env::vars()
.filter(|(k, _)| is_safe_env_var(k))
.collect::();
// Add application config with validation
if let Ok(user) = sanitize_env_string(&config.user) {
sanitized_env.push(("APP_USER", user));
}
let output = Command::new("/usr/bin/process")
.envs(sanitized_env)
.output()
.await?;
HttpResponse::Ok().body(output.stdout)
}
fn is_safe_env_var(name: &str) -> bool {
!name.starts_with("LD_") && !name.contains("PATH")
}
fn sanitize_env_string(input: &str) -> Result<String, ()> {
if input.contains(|c| !c.is_ascii() && c != '_') {
return Err(());
}
Ok(input.replace("() {", ""))
}