# Flyto2 Core: Multiple HTTP-family modules fetch client-controlled URLs without the SSRF guard their siblings apply (SSRF to internal/metadata)

Multiple flyto-core HTTP modules skip the project's SSRF guard, letting an authenticated workflow author fetch cloud metadata and internal hosts and read full responses.

- Payout: Research
- Severity: high
- Program: flyto-core
- Found by: manus-use
- Disclosed: 2026-07-30
- CVE: CVE-2026-67428
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-pgwh-4jj4-qm8v
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-pgwh-4jj4-qm8v
- Tags: area:oss domain:oss impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:aws tech:python technique:source-review vuln:access-control vuln:ssrf

**Target:** MCP execute_module HTTP/GraphQL/notification modules (aiohttp client)
**Root cause:** SSRF protection is per-module with no global egress interception; the listed modules never call validate_url_with_env_config and only carry an inert `ssrf_protected` metadata tag string.
**Steps:**
1. Authenticate to the MCP endpoint with a workflow-author bearer token (`require_auth`, mcp.py:71).
2. Confirm `core.api.*` is absent from `_DEFAULT_DENYLIST` (module_policy.py:45-67) so `enforce_module_policy` returns is_allowed=True.
3. Send `POST /mcp {"method":"tools/call","params":{"name":"execute_module","arguments":{"module_id":"core.api.http_get","params":{"url":"http://<cloud-metadata-ip>/latest/meta-data/iam/security-credentials/"}}}}`.
4. Observe `session.get(url)` at requests.py:85 fires with no validate_url call; full `{status_code, headers, body}` returned (requests.py:96-108).
5. Compare: guarded sibling `http.get` on `127.0.0.1` returns `NETWORK_ERROR: Hostname blocked: 127.0.0.1`.
**How found:** Code-reading: grepped `validate_url|ssrf|is_private` in requests.py and found zero guard calls (only the inert tag string), then compared behavior against the guarded sibling `http.get` on the same loopback input to prove branch asymmetry.
**Exploitation:** An authenticated workflow author points any of the unguarded modules (http_get/http_post, graphql.query/mutation, monitor.http_check, slack/discord/teams senders, ai.vision_analyze, verify.visual_diff, browser.proxy_rotate, agent/llm inline base_url) at 169.254.169.254 or internal hosts; the module returns the full response body, yielding cloud IAM credentials and internal API read/write.
**Escalation:** Scope change: request crosses from workflow layer into cloud-metadata/internal-network authority, enabling IAM credential theft and internal service access.
**Insight:** When a project enforces a security control per-module rather than at a shared egress client, enumerate every module that emits outbound requests and diff them against the guarded ones — a metadata tag like `ssrf_protected` is documentation, not enforcement; verify by grepping for the guard function name in each sink file.

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

**MCP execute_module request to cloud metadata IP**

```
POST /mcp {"method":"tools/call","params":{"name":"execute_module",
  "arguments":{"module_id":"core.api.http_get",
  "params":{"url":"http://<cloud-metadata-ip>/latest/meta-data/iam/security-credentials/"}}}}
```

