fast-agent: `fast-agent serve` binds HTTP to 0.0.0.0 with no authentication by default; with --shell this is unauthenticated remote command executionGHSA-9vhv-g5hf-m7m7
MEDIUMFix: evalstate/fast-agent#828GHSA-9vhv-g5hf-m7m7 is a medium-severity (CVSS 6.4) OS Command Injection vulnerability in fast-agent-mcp. A fix is available for fast-agent-mcp — see the affected versions and patch details below.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
Where this sits among everything scored
Real-World Exposure
fast-agent-mcpReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
fast-agent serve exposes the agent over MCP. By default it uses HTTP transport bound to 0.0.0.0 (all interfaces) on port 8000 with no authentication (auth=None) and prints no warning. Any host that can reach the port can call the exposed agent tool. When the server is started with --shell (which is -x, the flag used in the README quickstart uvx fast-agent mcp@latest -x), the agent has a local execute shell tool, so an unauthenticated remote client can drive the agent into running arbitrary OS commands — unauthenticated RCE.
Affected code
cli/commands/serve.py— transport defaults to HTTP;--hostdefaults to0.0.0.0; port 8000.mcp/server/agent_server.py—auth_provider = NoneunlessFAST_AGENT_SERVE_OAUTH=huggingface;run_async(transport="http", host=host, ...)withhostdefaulting to0.0.0.0.tools/local_shell_executor.py— the shell tool runs commands viacreate_subprocess_shell.
Verified (live, faithful — published 0.7.20 in Docker python:3.13-slim, stock unauthenticated client)
- Unauthenticated exposure (core defect): a client with no Authorization header completed
initializeandtools/list(->['agent']) againsthttp://HOST:8000/mcp. Model-independent. - Unauthenticated RCE with
--shell, demonstrated two ways:- Real production model (
gpt-4o-mini): a single plain-language message asking the agent to run a command via its shell tool -> the model autonomously calledexecuteand the command output (process identity showing it ran as root, the hostname, and the first lines of a sensitive system file) was returned to the unauthenticated caller. - Key-free deterministic (
passthroughmodel): the tool-call directive invoked the shell tool directly, same result. The server-side permission prompt did not block this in non-interactive serve mode.
- Real production model (
Proof of concept
Environment: Docker python:3.13-slim, pip install fast-agent-mcp -> 0.7.20.
# server (real model + the documented shell flag; 0.0.0.0 is also the built-in default)
OPENAI_API_KEY=*** fast-agent serve --model gpt-4o-mini --shell --host 0.0.0.0 --port 8000
# -> "Uvicorn running on http://0.0.0.0:8000" (no auth, no warning)
# attacker: unauthenticated MCP HTTP client (NO Authorization header) to http://HOST:8000/mcp
# initialize() -> 200 OK (server fast-agent-MCP-Server 0.7.20)
# tools/list -> ['agent']
# tools/call name="agent" arguments={"message":
# "Using your execute/shell tool, run exactly this command and return its raw stdout: "
# "id; echo PWNED=$(hostname); cat /etc/passwd | head -2"}
#
# response returned to the UNAUTHENTICATED caller:
# uid=0(root) gid=0(root) groups=0(root)
# PWNED=<host>
# <first lines of /etc/passwd>
Also reproduced key-free with --model passthrough and the message ***CALL_TOOL execute {"command":"id"} (passthrough invokes the tool deterministically). Full transcript + client scripts were sent by email.
Impact
An unauthenticated client that can reach the serve port controls the agent: with --shell this is remote command execution as the server user; without --shell it is unauthenticated use of the operator's LLM and any configured MCP servers' tools. The 0.0.0.0 default makes the server reachable beyond localhost as soon as it runs on a non-isolated host. Preconditions (serve + shell) are noted above and are why I defer the exact rating to you.
Remediation
- Default the bind host to
127.0.0.1; require an explicit--host 0.0.0.0opt-in for non-loopback. - Print a loud warning when binding a non-loopback interface without authentication.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | fast-agent-mcp | all versions | 0.7.21pip install --upgrade 'fast-agent-mcp==0.7.21' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for fast-agent-mcp, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update fast-agent-mcp to 0.7.21 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-9vhv-g5hf-m7m7 is resolved across your whole dependency graph.
Workarounds
Stop passing untrusted input into the interpreter or shell: call the affected binary with an argument array rather than a composed command string, reject anything outside a strict allowlist of expected values, and run the component under an account that cannot reach beyond the work it legitimately does.
Frequently Asked Questions
Is GHSA-9vhv-g5hf-m7m7 in your dependencies?
Find it across PyPI, including transitive dependencies.