qwed-mcp has Unsafe SymPy `parse_expr()` Remote Code Execution via Unsanitized Math Expression Input
Published Aug 25, 2026 · Updated Oct 23, 2026 · Also known as GHSA-mw6r-2hvm-4rp2
Original summary
Normalized public record · Updated Oct 23, 2026
qwed-mcp has Unsafe SymPy `parse_expr()` Remote Code Execution via Unsanitized Math Expression Input
Publisher and source text is retained separately from QFinch interpretation.
QFinch assessment
verifymathexpression() in qwed-mcp v0.2.0 passes attacker-controlled strings directly to SymPy's parseexpr() without restricting globaldict or validating the expression's AST. Because parseexpr() internally calls eval() and Python automatically injects the current module's builtins when no explicit restriction is set, an attacker can embed arbitrary Python expressions — including import('os').system(...) — to execute OS commands in the context of the running process. Confirmed exploitation in a Docker container yields root-level arbitrary command execution with no authentication or special configuration required.
Potential impact
Any caller that passes attacker-controlled input to verifymathexpression() or any future MCP tool registration that exposes this function over a network interface is fully compromised. An attacker can: - Execute arbitrary OS commands as the process user (demonstrated as root in Phase 2). - Read, write, or delete files accessible to the process. - Exfiltrate secrets (API keys, environment variables, credentials) from the process environment. - Pivot to internal services reachable from the host. The function is part of the public PyPI package qwed-mcp. Any downstream library consumer or service that wraps verifymathexpression() with user-supplied input is affected without additional configuration. While v0.2.0's default MCP tool registry does not expose this function as a registered tool, the library API is directly importable and exploitable by any code that calls it.
Technical context
The vulnerability resides in src/qwedmcp/engines/mathengine.py. The public function verifymathexpression(expression, claimedresult, operation) accepts both the expression and claimedresult arguments as raw strings and passes them — after a trivial ^ → substitution — to sympy.parsing.sympyparser.parseexpr(): localdict only adds math symbols to the evaluation namespace; it does not remove builtins. SymPy's parseexpr() eventually calls Python's built-in eval(), which — absent an explicit {"builtins": {}} in globaldict — receives the full built-in namespace. This makes import, open, exec, and every other Python built-in available to the evaluated expression. There is no allowlist, AST pre-validation, or sandboxing applied at any point before the parseexpr() calls (lines 50 and 64). Data flow: 1. Source — mathengine.py:13-16: external caller supplies expression and claimedresult. 2. Propagation — mathengine.py:50-54: expression substituted and forwarded to parseexpr(). 3. Propagation — mathengine.py:64-68: claimedresult substituted and forwarded to parseexpr(). 4. Sink — sympy.parsing.sympyparser.parseexpr(): calls eval() with unrestricted builtins.
Evidence: public source recordsAffected products
| Affected package, product or range | Fix status |
|---|---|
| Consult the referenced vendor advisory for affected versions. | Consult source advisory |
Matching a version is only one part of assessing exposure. QFinch has not assessed your environment on this public page.
Remediation
Apply the vendor-supported fixed release or mitigation, then verify the affected component is no longer reachable.
- Confirm the affected product and reachable code path exist in your environment.
- Apply the vendor-supported patch or mitigation from the primary advisory.
- Rescan the exact deployed revision and retain verification evidence.
Evidence coverage
- SSVC priority
- MEDIUM
- Confidence
- 20%
- Aliases
- GHSA-mw6r-2hvm-4rp2
Scores appear only when corresponding evidence exists. Missing data is never interpreted as zero risk.
Sources and record history
Record history
Published Aug 25, 2026 · Last refreshed Oct 23, 2026