Shellshock in Fastapi
How Shellshock Manifests in Fastapi
Shellshock (CVE-2014-6271) is a vulnerability in the Bash interpreter that allows an attacker to execute arbitrary code when environment variables contain specially crafted strings. In a FastAPI application, the flaw appears when developer‑controlled input is passed to a shell command without proper sanitization, and the environment of that subprocess includes user‑supplied data (e.g., HTTP headers, query parameters, or request body).
Typical vulnerable patterns in FastAPI include:
- Using
subprocess.runoros.systemwithshell=Trueand concatenating request data into the command string. - Passing the entire
os.environ(or a copy) to a subprocess, which propagates headers such asUser-Agent,Referer, or customX‑*headers directly into the Bash environment. - Relying on third‑party utilities that internally invoke Bash (e.g.,
curl,ssh,git) and forwarding raw header values as arguments or environment variables.
Concrete example: a FastAPI endpoint that logs the user agent by spawning a shell logger:
from fastapi import FastAPI, Request
import subprocess
import os
app = FastAPI()
@app.get("/ping")
async def ping(request: Request):
# Vulnerable: user‑controlled header becomes part of the environment
env = os.environ.copy()
env["HTTP_USER_AGENT"] = request.headers.get("user-agent", "")
# Shellshock payload can be injected via the header value
subprocess.run(['logger', '-t', 'fastapi-ping'], shell=True, env=env)
return {"status": "ok"}
If an attacker sends a request with the header:
User-Agent: () { :; }; echo "VULNERABLE" > /tmp/exploit
the Bash interpreter invoked by subprocess.run(..., shell=True) will execute the echo command, demonstrating remote code execution.
This issue maps to OWASP API Security Top 10 2023 – A03:2023 Injection, and is closely related to CVE‑2014-6271 (Shellshock) and CVE‑2014-7169 (the follow‑up fix bypass).
Fastapi-Specific Detection
Detecting Shellshock in a FastAPI service requires probing the unauthenticated attack surface for environment‑variable injection points. Because the vulnerability is triggered only when a subprocess is spawned with shell=True and the attacker‑controlled data ends up in the environment, black‑box scanners must craft requests that place the malicious Bash function syntax into headers, query strings, or body fields that are likely to be propagated.
middleBrick performs this check automatically as part of its "Input Validation" and "Unauthenticated LLM endpoint" (though the latter is unrelated) test suite. When scanning a FastAPI endpoint, middleBrick:
- Enumerates all reachable HTTP methods and paths without authentication.
- For each parameter (header, query string, JSON body, form field), it injects a payload that matches the Shellshock pattern:
() { :; }; echo {{RANDOM}}where {{RANDOM}} is a unique string to confirm execution. - Monitors outgoing traffic or side‑effects (e.g., DNS lookup, file write, network connection) that would only occur if the payload were executed by a Bash subprocess.
- Reports the finding with severity, the exact parameter that triggered the issue, and remediation guidance.
Example of a manual test that mirrors what middleBrick does:
curl -v -H "User-Agent: () { :; }; echo exploited > /tmp/shock" https://api.example.com/ping
If the server is vulnerable, the file /tmp/shock will be created (or a DNS query to a unique subdomain will be observed). middleBrick automates this observation by using internal canary mechanisms that do not require any agents on the target.
Because the scan is limited to the unauthenticated surface and completes in 5–15 seconds, teams can run it on every pull request via the middleBrick GitHub Action:
name: API Security Scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick scan
uses: middlebrick/github-action@v1
with:
api-url: https://staging.example.com
fail-below: B # fail the PR if score drops below B
This yields actionable feedback before code reaches production.
Fastapi-Specific Remediation
The most reliable fix is to eliminate the use of a shell altogether. When subprocess execution is unavoidable, avoid shell=True and pass arguments as a list, ensuring that no user input is interpreted by the shell. Additionally, never copy the full environment into the subprocess; instead, construct a minimal environment containing only trusted variables.
Refactoring the vulnerable logger example:
from fastapi import FastAPI, Request
import subprocess
import shlex
app = FastAPI()
@app.get("/ping")
async def ping(request: Request):
# Safe: no shell, arguments as list
# User‑agent is treated as data, not code
user_agent = request.headers.get("user-agent", "")
# Optionally sanitize for logging (e.g., remove newlines)
safe_agent = user_agent.replace('\n', ' ').replace('\r', '')
subprocess.run(['logger', '-t', 'fastapi-ping', safe_agent])
return {"status": "ok"}
If you must use a shell (e.g., invoking a complex legacy script), apply strict whitelisting of allowed values and use shlex.quote to escape any data that will be placed inside the command string:
import shlex subprocess.run(['bash', '-c', f'echo {shlex.quote(user_agent)}'], shell=False)Better yet, avoid Bash entirely and use Python’s built‑in libraries for tasks such as logging, file operations, or HTTP calls.
Additional defensive measures:
- Use FastAPI’s dependency injection and Pydantic models to validate and coerce incoming data before it reaches any business logic.
- Employ middleware that strips or sanitizes known dangerous headers (e.g.,
User-Agent,Referer,X-Forwarded-For) if they are not required for your application. - Leverage the middleBrick dashboard to monitor the "Input Validation" score over time; a drop indicates a regression that may have reintroduced a shell‑injection vector.
- Keep the host’s Bash patched (CVE‑2014-6271, CVE‑2014-7169) as a defense‑in‑depth measure, but do not rely on patching alone.
By combining code‑level avoidance of shell=True, strict input validation, and continuous scanning with middleBrick, FastAPI teams can effectively mitigate Shellshock‑style command injection vulnerabilities.