CVE-2026-55787
Flyto2 Core: SSRF guard bypass via IPv6 transition addresses (IPv4-mapped / 6to4 / NAT64) in validate_url_ssrf
Flyto2 Core is an execution kernel for automation and AI-agent workflows. Prior to 2.26.3, validate_url_ssrf and is_private_ip in src/core/utils.py compare resolved addresses only against native private IPv4 and IPv6 ranges and do not unwrap IPv4-mapped, IPv4-compatible, 6to4, or NAT64 transition addresses. An authenticated workflow author can provide a transition-form literal URL through the Execution API POST /v1/execute route, http.get, or sibling guarded outbound-request modules, causing the guard to accept an address whose embedded IPv4 points to loopback, RFC 1918, or cloud metadata services. On a host that routes the selected transition form, the outbound request reaches the internal destination and returns the response body to the workflow caller, enabling read SSRF and disclosure of internal-service data or cloud credentials. This issue is fixed in version 2.26.3.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- Low
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.1
Flyto2 Core: SSRF guard bypass via IPv6 transition addresses (IPv4-mapped / 6to4 / NAT64) in validate_url_ssrf
Flyto2 Core is an execution kernel for automation and AI-agent workflows. Prior to 2.26.3, validate_url_ssrf and is_private_ip in src/core/utils.py compare resolved addresses only against native private IPv4 and IPv6 ranges and do not unwrap IPv4-mapped, IPv4-compatible, 6to4, or NAT64 transition addresses. An authenticated workflow author can provide a transition-form literal URL through the Execution API POST /v1/execute route, http.get, or sibling guarded outbound-request modules, causing the guard to accept an address whose embedded IPv4 points to loopback, RFC 1918, or cloud metadata services. On a host that routes the selected transition form, the outbound request reaches the internal destination and returns the response body to the workflow caller, enabling read SSRF and disclosure of internal-service data or cloud credentials. This issue is fixed in version 2.26.3.
- Record
CVE-2026-55787- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
EUVDEUVD-2026-101588
7.1
Flyto2 Core is an execution kernel for automation and AI-agent workflows. Prior to 2.26.3, validate_url_ssrf and is_private_ip in src/core/utils.py compare resolved addresses only against native private IPv4 and IPv6 ranges and do not unwrap IPv4-mapped, IPv4-compatible, 6to4, or NAT64 transition addresses. An authenticated workflow author can provide a transition-form literal URL through the Execution API POST /v1/execute route, http.get, or sibling guarded outbound-request modules, causing the guard to accept an address whose embedded IPv4 points to loopback, RFC 1918, or cloud metadata services. On a host that routes the selected transition form, the outbound request reaches the internal destination and returns the response body to the workflow caller, enabling read SSRF and disclosure of internal-service data or cloud credentials. This issue is fixed in version 2.26.3.
- Record
EUVD-2026-101588- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-794r-5rp2-fpg8
7.1
flyto-core has SSRF guard bypass via IPv6 transition addresses (IPv4-mapped / 6to4 / NAT64) in validate_url_ssrf
Summary
flyto-core's SSRF protection (validate_url_ssrf / is_private_ip in src/core/utils.py) blocks private and metadata destinations by resolving the host and testing the resulting IP for membership in a hardcoded PRIVATE_IP_RANGES list. That list contains only the native RFC 1918 / loopback / link-local / unique-local ranges. It does not account for IPv6 transition address forms that embed an IPv4 (or loopback) target:
- IPv4-mapped
::ffff:a.b.c.d - IPv4-compatible
::a.b.c.d - 6to4
2002::/16 - NAT64 well-known prefix
64:ff9b::/96and local-use64:ff9b:1::/48
A workflow author can submit a URL with a literal transition-form host (for example http://[::ffff:127.0.0.1]:8080/... or http://[64:ff9b::a9fe:a9fe]/latest/meta-data/). is_private_ip() returns False for these (the address is not literally inside any listed range), so validate_url_ssrf lets the request through, and the http.get atomic module (and ~10 sibling modules that share the same guard) performs the outbound aiohttp fetch and returns the response body. On a host that uses NAT64/6to4 these addresses route to the embedded IPv4 endpoint (e.g. the cloud instance-metadata service 169.254.169.254); on any dual-stack host the IPv4-mapped form is routed by the kernel directly to the embedded IPv4, including loopback and RFC 1918 internal services.
This is CWE-918 (Server-Side Request Forgery): the guard that exists specifically to keep workflow-authored URLs away from internal/metadata endpoints is bypassable, and the response body is returned to the caller (a read SSRF).
Affected code
src/core/utils.py:
PRIVATE_IP_RANGES(around L297) — lists native ranges only; no64:ff9b::/96, no2002::/16, no::ffff:0:0/96, no::/96.is_private_ip(ip_str)(around L337) —ipaddress.ip_address(ip_str)then membership test againstPRIVATE_IP_RANGES. Because the test is plain network membership (notis_global/is_privatepredicates), it does not unwrap transition forms, so even::ffff:127.0.0.1— whichipaddressitself classifiesis_private == True— is not caught here.validate_url_ssrf(around L358) — resolves viasocket.getaddrinfo(hostname, None, AF_UNSPEC)and rejects only whenis_private_ip(ip)isTrue.validate_url_with_env_config(url)(around L496) — the wrapper actually invoked by the modules.
Trust boundary in src/core/modules/atomic/http/get.py:
- L93
url = params.get('url')— workflow parameter, attacker-controlled by the workflow author. - L104
validate_url_with_env_config(url)— the guard above. - L116
async with session.get(url, headers=headers, ssl=ssl_param) as response—aiohttpfetch; the body is returned to the caller.
How input reaches the sink (reachability)
params['url'] (L93) is fully attacker-controlled by the workflow author. It reaches the sink with no intervening sanitization other than the SSRF guard itself: L93 read → L104 validate_url_with_env_config(url) (the bypassed guard) → L116 aiohttp session.get. The route is POST /v1/execute with body {"module_id":"http.get","params":{"url":...}} (bearer-token authenticated; the token is the per-instance workflow-author credential), or equivalently an http.get node in a workflow YAML. The response body is returned in the data.body field, making this a read SSRF.
The same guarded-then-fetch pattern is shared by the http.{request,batch,paginate,session}, browser.goto, image.download, communication.webhook_trigger, notification.send, vector.connector and llm.chat atomic modules.
Impact
A user who can author/execute a workflow (the product's normal untrusted-input surface — reachable over the Execution API POST /v1/execute with a module-execute body, or via a workflow YAML node) can drive an authenticated outbound GET to internal-only destinations that the SSRF guard is explicitly meant to block:
- Cloud instance-metadata service…
- Record
GHSA-794r-5rp2-fpg8- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
NVD
7.1
Flyto2 Core is an execution kernel for automation and AI-agent workflows. Prior to 2.26.3, validate_url_ssrf and is_private_ip in src/core/utils.py compare resolved addresses only against native private IPv4 and IPv6 ranges and do not unwrap IPv4-mapped, IPv4-compatible, 6to4, or NAT64 transition addresses. An authenticated workflow author can provide a transition-form literal URL through the Execution API POST /v1/execute route, http.get, or sibling guarded outbound-request modules, causing the guard to accept an address whose embedded IPv4 points to loopback, RFC 1918, or cloud metadata services. On a host that routes the selected transition form, the outbound request reaches the internal destination and returns the response body to the workflow caller, enabling read SSRF and disclosure of internal-service data or cloud credentials. This issue is fixed in version 2.26.3.
- Record
CVE-2026-55787- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSV
7.1
Flyto2 Core: SSRF guard bypass via IPv6 transition addresses (IPv4-mapped / 6to4 / NAT64) in validate_url_ssrf
Flyto2 Core is an execution kernel for automation and AI-agent workflows. Prior to 2.26.3, validate_url_ssrf and is_private_ip in src/core/utils.py compare resolved addresses only against native private IPv4 and IPv6 ranges and do not unwrap IPv4-mapped, IPv4-compatible, 6to4, or NAT64 transition addresses. An authenticated workflow author can provide a transition-form literal URL through the Execution API POST /v1/execute route, http.get, or sibling guarded outbound-request modules, causing the guard to accept an address whose embedded IPv4 points to loopback, RFC 1918, or cloud metadata services. On a host that routes the selected transition form, the outbound request reaches the internal destination and returns the response body to the workflow caller, enabling read SSRF and disclosure of internal-service data or cloud credentials. This issue is fixed in version 2.26.3.
- Record
CVE-2026-55787- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSVGHSA-794r-5rp2-fpg8
7.1
flyto-core has SSRF guard bypass via IPv6 transition addresses (IPv4-mapped / 6to4 / NAT64) in validate_url_ssrf
Summary
flyto-core's SSRF protection (validate_url_ssrf / is_private_ip in src/core/utils.py) blocks private and metadata destinations by resolving the host and testing the resulting IP for membership in a hardcoded PRIVATE_IP_RANGES list. That list contains only the native RFC 1918 / loopback / link-local / unique-local ranges. It does not account for IPv6 transition address forms that embed an IPv4 (or loopback) target:
- IPv4-mapped
::ffff:a.b.c.d - IPv4-compatible
::a.b.c.d - 6to4
2002::/16 - NAT64 well-known prefix
64:ff9b::/96and local-use64:ff9b:1::/48
A workflow author can submit a URL with a literal transition-form host (for example http://[::ffff:127.0.0.1]:8080/... or http://[64:ff9b::a9fe:a9fe]/latest/meta-data/). is_private_ip() returns False for these (the address is not literally inside any listed range), so validate_url_ssrf lets the request through, and the http.get atomic module (and ~10 sibling modules that share the same guard) performs the outbound aiohttp fetch and returns the response body. On a host that uses NAT64/6to4 these addresses route to the embedded IPv4 endpoint (e.g. the cloud instance-metadata service 169.254.169.254); on any dual-stack host the IPv4-mapped form is routed by the kernel directly to the embedded IPv4, including loopback and RFC 1918 internal services.
This is CWE-918 (Server-Side Request Forgery): the guard that exists specifically to keep workflow-authored URLs away from internal/metadata endpoints is bypassable, and the response body is returned to the caller (a read SSRF).
Affected code
src/core/utils.py:
PRIVATE_IP_RANGES(around L297) — lists native ranges only; no64:ff9b::/96, no2002::/16, no::ffff:0:0/96, no::/96.is_private_ip(ip_str)(around L337) —ipaddress.ip_address(ip_str)then membership test againstPRIVATE_IP_RANGES. Because the test is plain network membership (notis_global/is_privatepredicates), it does not unwrap transition forms, so even::ffff:127.0.0.1— whichipaddressitself classifiesis_private == True— is not caught here.validate_url_ssrf(around L358) — resolves viasocket.getaddrinfo(hostname, None, AF_UNSPEC)and rejects only whenis_private_ip(ip)isTrue.validate_url_with_env_config(url)(around L496) — the wrapper actually invoked by the modules.
Trust boundary in src/core/modules/atomic/http/get.py:
- L93
url = params.get('url')— workflow parameter, attacker-controlled by the workflow author. - L104
validate_url_with_env_config(url)— the guard above. - L116
async with session.get(url, headers=headers, ssl=ssl_param) as response—aiohttpfetch; the body is returned to the caller.
How input reaches the sink (reachability)
params['url'] (L93) is fully attacker-controlled by the workflow author. It reaches the sink with no intervening sanitization other than the SSRF guard itself: L93 read → L104 validate_url_with_env_config(url) (the bypassed guard) → L116 aiohttp session.get. The route is POST /v1/execute with body {"module_id":"http.get","params":{"url":...}} (bearer-token authenticated; the token is the per-instance workflow-author credential), or equivalently an http.get node in a workflow YAML. The response body is returned in the data.body field, making this a read SSRF.
The same guarded-then-fetch pattern is shared by the http.{request,batch,paginate,session}, browser.goto, image.download, communication.webhook_trigger, notification.send, vector.connector and llm.chat atomic modules.
Impact
A user who can author/execute a workflow (the product's normal untrusted-input surface — reachable over the Execution API POST /v1/execute with a module-execute body, or via a workflow YAML node) can drive an authenticated outbound GET to internal-only destinations that the SSRF guard is explicitly meant to block:
- Cloud instance-metadata service…
- Record
GHSA-794r-5rp2-fpg8- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSVPYSEC-2026-2481
7.1
flyto-core has SSRF guard bypass via IPv6 transition addresses (IPv4-mapped / 6to4 / NAT64) in validate_url_ssrf
Summary
flyto-core's SSRF protection (validate_url_ssrf / is_private_ip in src/core/utils.py) blocks private and metadata destinations by resolving the host and testing the resulting IP for membership in a hardcoded PRIVATE_IP_RANGES list. That list contains only the native RFC 1918 / loopback / link-local / unique-local ranges. It does not account for IPv6 transition address forms that embed an IPv4 (or loopback) target:
- IPv4-mapped
::ffff:a.b.c.d - IPv4-compatible
::a.b.c.d - 6to4
2002::/16 - NAT64 well-known prefix
64:ff9b::/96and local-use64:ff9b:1::/48
A workflow author can submit a URL with a literal transition-form host (for example http://[::ffff:127.0.0.1]:8080/... or http://[64:ff9b::a9fe:a9fe]/latest/meta-data/). is_private_ip() returns False for these (the address is not literally inside any listed range), so validate_url_ssrf lets the request through, and the http.get atomic module (and ~10 sibling modules that share the same guard) performs the outbound aiohttp fetch and returns the response body. On a host that uses NAT64/6to4 these addresses route to the embedded IPv4 endpoint (e.g. the cloud instance-metadata service 169.254.169.254); on any dual-stack host the IPv4-mapped form is routed by the kernel directly to the embedded IPv4, including loopback and RFC 1918 internal services.
This is CWE-918 (Server-Side Request Forgery): the guard that exists specifically to keep workflow-authored URLs away from internal/metadata endpoints is bypassable, and the response body is returned to the caller (a read SSRF).
Affected code
src/core/utils.py:
PRIVATE_IP_RANGES(around L297) — lists native ranges only; no64:ff9b::/96, no2002::/16, no::ffff:0:0/96, no::/96.is_private_ip(ip_str)(around L337) —ipaddress.ip_address(ip_str)then membership test againstPRIVATE_IP_RANGES. Because the test is plain network membership (notis_global/is_privatepredicates), it does not unwrap transition forms, so even::ffff:127.0.0.1— whichipaddressitself classifiesis_private == True— is not caught here.validate_url_ssrf(around L358) — resolves viasocket.getaddrinfo(hostname, None, AF_UNSPEC)and rejects only whenis_private_ip(ip)isTrue.validate_url_with_env_config(url)(around L496) — the wrapper actually invoked by the modules.
Trust boundary in src/core/modules/atomic/http/get.py:
- L93
url = params.get('url')— workflow parameter, attacker-controlled by the workflow author. - L104
validate_url_with_env_config(url)— the guard above. - L116
async with session.get(url, headers=headers, ssl=ssl_param) as response—aiohttpfetch; the body is returned to the caller.
How input reaches the sink (reachability)
params['url'] (L93) is fully attacker-controlled by the workflow author. It reaches the sink with no intervening sanitization other than the SSRF guard itself: L93 read → L104 validate_url_with_env_config(url) (the bypassed guard) → L116 aiohttp session.get. The route is POST /v1/execute with body {"module_id":"http.get","params":{"url":...}} (bearer-token authenticated; the token is the per-instance workflow-author credential), or equivalently an http.get node in a workflow YAML. The response body is returned in the data.body field, making this a read SSRF.
The same guarded-then-fetch pattern is shared by the http.{request,batch,paginate,session}, browser.goto, image.download, communication.webhook_trigger, notification.send, vector.connector and llm.chat atomic modules.
Impact
A user who can author/execute a workflow (the product's normal untrusted-input surface — reachable over the Execution API POST /v1/execute with a module-execute body, or via a workflow YAML node) can drive an authenticated outbound GET to internal-only destinations that the SSRF guard is explicitly meant to block:
- Cloud instance-metadata service…
- Record
PYSEC-2026-2481- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
flyto-corePyPI |
through 2.26.2 | 2.26.3 |
flyto-corePyPI |
before 2.26.3, plus listed versions 1.0.01.0.11.0.21.0.31.0.4 Show 45 moreShow less1.0.51.0.61.0.71.0.81.0.91.1.01.1.11.11.01.12.01.13.01.14.01.14.11.14.21.15.01.16.01.16.11.16.101.16.21.16.31.16.41.16.51.16.61.16.71.16.81.16.91.2.01.3.01.4.01.5.01.5.11.5.21.5.41.6.01.6.11.6.21.6.31.6.41.6.51.7.01.7.11.7.21.7.31.7.41.7.51.7.6 |
2.26.3 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-55787 is a high vulnerability (CVSS 7.1) in Flytohub Flyto-core, published July 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.29%, the 21st percentile. Fixed in flyto-core 2.26.3.
References
github.com/flytohub/flyto-core/security/advisories/GHSA-794r-5rp2-fpg8
github.com/flytohub/flyto-core/pull/20
github.com/flytohub/flyto-core/commit/519e040384a74984848c4e84a6600e5f1020a6d2
github.com/flytohub/flyto-core/releases/tag/v2.26.3
github.com/advisories/GHSA-794r-5rp2-fpg8
github.com/CVEProject/cvelistV5/tree/main/cves/2026/55xxx/CVE-2026-55787.json
nvd.nist.gov/vuln/detail/CVE-2026-55787
github.com/flytohub/flyto-core
pypi.org/project/flyto-core
Left out here: 163 versions and the rest of 3 source descriptions. The JSON record has them.