Command Injection in Rocket with Mutual Tls
Command Injection in Rocket with Mutual Tls — how this specific combination creates or exposes the vulnerability
Command Injection is a class of injection flaws where untrusted data is passed to a system shell or to a command interpreter. In Rocket, this often arises when dynamic values derived from HTTP requests (query parameters, headers, or body fields) are forwarded to OS-level commands without proper validation or escaping. When Mutual Tls (mTLS) is used, the presence of client certificates may give developers a false sense of strong authentication, leading them to treat incoming requests as inherently trusted. This trust relationship can result in relaxed input validation or insufficient sanitization because the operator assumes only authorized clients can reach the endpoint.
Consider a scenario where a Rocket route reads a certificate field (e.g., a subject alternative name or a custom header populated by the mTLS layer) and uses it to invoke a system utility. If the input is not strictly constrained, an attacker able to present a valid client certificate (obtained through compromise or social engineering) can supply crafted payloads such as ; id or | cat /etc/passwd that the shell will interpret. The mTLS layer does not sanitize or restrict these values; it only confirms identity. Therefore, the combination of mTLS and unchecked command construction introduces a path for authenticated command injection. Attack patterns like this map to the OWASP API Top 10 category ‘Broken Object Level Authorization’ when the parameter is tied to object access, and to ‘Injection’ when command execution occurs.
In practice, middleBrick scans detect such issues by observing that endpoints accept inputs which are later used in command-like contexts without sanitization, even when the endpoint is protected by mTLS. The scanner does not rely on internal architecture but on runtime behavior, identifying places where data crosses from the request into system-level execution. Real-world CVEs in similar frameworks demonstrate that seemingly trusted channels can be abused when input validation is inconsistent with the assumed trust boundary.
Additionally, logging or debugging endpoints that echo certificate details into shell commands compound the risk. For example, a route that constructs a command string by concatenating certificate-derived strings with static text can be exploited to leak environment variables or invoke arbitrary binaries. The scanner flags these as high-severity findings because they bypass the protective intent of mTLS by reintroducing unsafe data flows.
Mutual Tls-Specific Remediation in Rocket — concrete code fixes
Remediation focuses on strict input validation, avoiding shell command construction, and ensuring that mTLS metadata is treated as untrusted unless explicitly verified. Below are concrete Rocket code examples that demonstrate secure handling when mTLS is in use.
// Safe approach: use typed parameters and avoid shell commands
#[get("/user/<id>")]
fn get_user(id: u32, user_certificate: Option<<<MyMtls as Guard>>::GuardResult> ) -> String {
// id is already validated as numeric by Rocket; certificate is inspected only for authorization
if let Some(cert) = user_certificate {
// Perform authorization checks on certificate fields, not command construction
if !is_authorized(cert) {
return String::from("Forbidden");
}
}
format!("User {}", id)
}
// Avoid: constructing shell commands with external input
#[post("/run", data = <input>)]
fn unsafe_run(input: Form<RunInput>, req: &Request) -> String {
// UNSAFE: directly using input.value in a shell command
// let output = Command::new("sh").arg("-c").arg(format!("echo {}", input.value)).output();
// Instead, use a hardcoded command with validated arguments
let output = Command::new("echo")
.arg(&input.safe_value) // safe_value is pre-validated
.output()
.unwrap_or_default();
String::from_utf8_lossy(&output.stdout).to_string()
}
// Use parameterized APIs or libraries that do not involve a shell
#[get("/file/<path>")]
fn get_file(path: PathBuf) -> Result<String, Status> {
// Validate path strictly: only allow known directories and sanitize segments
let base = Path::new("/safe/base");
let full_path = base.join(path).canonicalize().map_err(|_| Status::BadRequest)?;
if !full_path.starts_with(base) {
return Err(Status::BadRequest);
}
// Process file using Rust libraries, not shell commands
fs::read_to_string(full_path).map_err(|_| Status::InternalServerError)
}
Key practices:
- Treat mTLS-derived data as identifiers for authorization, not as inputs for command construction.
- Use strongly typed parameters (e.g.,
u32,PathBuf) so Rocket performs early validation before reaching business logic. - Avoid spawning shells; prefer direct command APIs with a fixed executable and validated arguments.
- Implement allowlists for any filesystem paths or external identifiers derived from certificate fields.
- Log certificate fingerprints for auditing, but do not embed them into commands or responses that could be concatenated into future shell invocations.
These patterns reduce the attack surface even when mTLS is present, ensuring that trust in client identity does not translate into unsafe runtime behavior.
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 |