Command Injection in Axum with Basic Auth
Command Injection in Axum with Basic Auth — how this specific combination creates or exposes the vulnerability
Command injection occurs when untrusted input is concatenated into a system command, allowing an attacker to execute arbitrary shell commands. In Axum, this risk arises when request data (e.g., headers, query parameters, or body fields) is passed to a command executor such as std::process::Command. The use of HTTP Basic Auth can inadvertently amplify the exposure: applications often log authentication headers or embed credentials in diagnostic contexts, and if any of that data is forwarded into shell commands, it expands the attack surface.
Consider an endpoint that accepts a username and password via Basic Auth and also takes a query parameter filename to generate a report. A vulnerable implementation might construct a shell command using string interpolation:
// WARNING: This is an insecure example for educational purposes only
use axum::{routing::get, Router};
use std::process::Command;
async fn report_handler(
// filename from query; user/pass from Basic Auth headers
filename: String,
) -> String {
// UNSAFE: direct concatenation of user input into a shell command
let output = Command::new("sh")
.arg("-c")
.arg(format!("echo generating report for {} && cat /data/{}", filename, filename))
.output()
.expect("failed to execute command");
String::from_utf8_lossy(&output.stdout).to_string()
}
let app = Router::new().route("/report", get(report_handler));
An attacker who knows the endpoint can provide filename as '; id #. Even if the application uses Basic Auth to identify the caller, the command runs with the process’s permissions. Because the command is built by concatenating the user-controlled filename, the attacker can break out of the intended argument and execute id (or any other shell command). The Basic Auth credentials themselves are not used in the command, but if logs or error messages include the Authorization header, they may aid the attacker in crafting subsequent injections or correlating requests.
In a real-world scenario, this pattern can lead to more severe outcomes: reading sensitive files, modifying data, or pivoting within the network. The risk is especially pronounced when the application also supports OpenAPI/Swagger spec analysis, because an exposed endpoint with unchecked input can be discovered automatically by scanners that correlate spec definitions with runtime behavior. middleBrick, for example, would flag such command injection findings under Input Validation and BFLA/Privilege Escalation checks, providing severity and remediation guidance without attempting to fix the code.
Basic Auth-Specific Remediation in Axum — concrete code fixes
Remediation focuses on two pillars: never pass untrusted input directly to a shell, and handle credentials safely without leaking them into logs or commands. For Axum services, prefer structured data processing over shell invocation; if a command must be executed, use a language-native library that avoids a shell entirely, and treat Basic Auth credentials as opaque tokens rather than embedding them in command arguments.
1. Avoid the shell; use direct command execution. Replace shell-based commands with explicit binaries and pass arguments as a vector, bypassing shell parsing and metacharacters:
use axum::{routing::get, Json};
use std::process::Command;
async fn safe_report_handler(
// filename from query; user credentials handled separately by Axum middleware
filename: String,
) -> Json {
// SAFE: no shell involved; arguments are passed directly
let output = Command::new("/usr/bin/cat")
.arg("/data")
.arg(&filename)
.output()
.expect("failed to execute cat");
Json(String::from_utf8_lossy(&output.stdout).to_string())
}
This eliminates injection via shell metacharacters such as ;, &, or |. Ensure the path and arguments are validated (e.g., restrict to known-safe filenames) and avoid using user input as part of the binary path itself.
2. Handle Basic Auth securely and separately. Use middleware to parse credentials, store them only as necessary in a secure runtime context, and avoid logging them. Do not include Authorization headers in command construction or diagnostic output:
use axum::{async_trait, extract::Request, middleware::Next, response::Response, http::header};
use std::convert::Infallible;
// Example middleware that extracts Basic Auth credentials without leaking them
pub struct BasicAuth;
#[async_trait]
impl tower_layer::Layer<S> for BasicAuth {
type Service = AuthMiddleware<S>;
fn layer(&self, inner: S) -> Self::Service {
AuthMiddleware { inner }
}
}
pub struct AuthMiddleware<S> {
inner: S,
}
#[async_trait]
impl<S> tower_service::Service<Request> for AuthMiddleware<S>
where
S: tower_service::Service<Request, Response = Response, Error = Infallible> + Send + 'static,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = std::future::Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
std::task::Poll::Ready(Ok(()))
}
fn call(&self, req: Request) -> Self::Future {
// Extract and optionally validate credentials, but do not forward to commands or logs
if let Some(auth_header) = req.headers().get(header::AUTHORIZATION) {
// Validate format "Basic base64(credentials)" without decoding or storing unnecessarily
let _ = auth_header.to_str().map(|s| s.starts_with("Basic "));
}
self.inner.call(req)
}
}
With these practices, the API remains functional while reducing the likelihood that user-controlled data reaches a shell. middleBrick’s scans (via CLI, GitHub Action, or MCP Server integrations) can verify that such mitigations are in place by checking for unsafe command construction patterns in the exposed surface.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |