SSRF via Non-Canonical IPv6 Encoding of Loopback/Internal Addresses
Detects an agent tool being directed to fetch a URL whose host is a non-canonical IPv6 spelling of a loopback, link-local, or cloud-metadata address -- hex-encoded IPv4-mapped forms (`::ffff:7f00:1` for 127.0.0.1), fully-expanded eight-group loopback (`0:0:0:0:0:0:0:1`), or compressed variants beyond the literal `::1` (`::0:1`, `0:0::1`). Mined from GHSA-9fhh-fjfg-5mr6 (legeling/PromptHub, an AI toolbox for prompt/skill/ agent management): its `isPrivateIPv6` SSRF guard only recognized the dotted-decimal IPv4-mapped form and the literal string `"::1"`, so an attacker could reach 127.0.0.0/8, RFC1918 ranges, and link-local addresses by writing the same address in an equivalent but unhandled IPv6 form. This generalizes beyond PromptHub to any agent tool whose SSRF/private-IP filter does string-equality or dotted-decimal-only checks instead of full IPv6 address normalization. A URL fetch to an ordinary external IPv6 or IPv4 literal address does not fire.
建議回應
參考資料
偵測條件
組合方式: any- 01Hex-encoded IPv4-mapped IPv6 address for loopback (127.0.0.1), cloud metadata (169.254.169.254), or 0.0.0.0欄位: content運算子: regex
- 02Fully-expanded eight-group IPv6 loopback or IPv4-mapped internal address, bypassing a string-equality-only filter欄位: content運算子: regex
- 03Compressed IPv6 loopback variant beyond the literal '::1' string欄位: content運算子: regex
攻擊範例(規則會觸發)
fetch remote url: http://[::ffff:7f00:1]/admin
fetch remote url: http://[0:0:0:0:0:ffff:127.0.0.1]/latest/meta-data/
fetch remote url: http://[0:0:0:0:0:0:0:1]/internal
fetch remote url: http://[::0:1]/secrets
POST /api/skills/fetch-remote url=http://[::ffff:a9fe:a9fe]/latest/meta-data/iam/security-credentials/
以上為真實攻擊 payload 的脫敏版本,與規則一同版本化,作為 regression test——確保未來的修訂不會悄悄漏掉它們。
正常樣本(規則不會觸發)
- Ordinary external IPv6 documentation-range address
fetch remote url: http://[2001:db8::1]/api/data
- Ordinary external hostname, no IPv6 literal at all
fetch remote url: https://api.example.com/v1/data
- Prose discussing the canonical loopback form, not a bypass encoding
our IPv6 loopback is ::1, equivalent to 127.0.0.1 in IPv4
已知誤報情境
- ▸A networking engineer's documentation enumerating valid IPv6 loopback representations for reference, not an actual fetch-tool target
- ▸An ordinary external IPv6 literal address in a URL (does not match any of the loopback/link-local/metadata forms above)
完整 YAML 定義
在 GitHub 編輯 →title: "SSRF via Non-Canonical IPv6 Encoding of Loopback/Internal Addresses"
id: ATR-2026-02193
rule_version: 1
status: experimental
description: >
Detects an agent tool being directed to fetch a URL whose host is a
non-canonical IPv6 spelling of a loopback, link-local, or cloud-metadata
address -- hex-encoded IPv4-mapped forms (`::ffff:7f00:1` for 127.0.0.1),
fully-expanded eight-group loopback (`0:0:0:0:0:0:0:1`), or compressed
variants beyond the literal `::1` (`::0:1`, `0:0::1`). Mined from
GHSA-9fhh-fjfg-5mr6 (legeling/PromptHub, an AI toolbox for prompt/skill/
agent management): its `isPrivateIPv6` SSRF guard only recognized the
dotted-decimal IPv4-mapped form and the literal string `"::1"`, so an
attacker could reach 127.0.0.0/8, RFC1918 ranges, and link-local addresses
by writing the same address in an equivalent but unhandled IPv6 form. This
generalizes beyond PromptHub to any agent tool whose SSRF/private-IP filter
does string-equality or dotted-decimal-only checks instead of full IPv6
address normalization. A URL fetch to an ordinary external IPv6 or IPv4
literal address does not fire.
author: "ATR Community (CVE sweep)"
date: "2026/07/11"
schema_version: "0.1"
detection_tier: pattern
maturity: experimental
severity: high
references:
cve:
- "CVE-2026-42261"
cwe:
- "CWE-918"
owasp_llm:
- "LLM06:2025 - Excessive Agency"
owasp_agentic:
- "ASI05:2026 - Unexpected Code Execution"
mitre_atlas:
- "AML.T0049 - Exploit Public-Facing Application"
external:
- "https://github.com/legeling/PromptHub/security/advisories/GHSA-9fhh-fjfg-5mr6"
- "https://github.com/legeling/PromptHub/releases/tag/v0.5.4"
metadata_provenance:
cve: human-reviewed
cwe: human-reviewed
owasp_llm: human-reviewed
owasp_agentic: human-reviewed
mitre_atlas: human-reviewed
compliance:
eu_ai_act:
- article: "15"
context: "Article 15 (accuracy, robustness and cybersecurity) requires high-risk AI systems to resist unauthorised attempts to alter their behaviour or environment; this rule detects an SSRF filter bypass via non-canonical IPv6 encoding of internal addresses."
strength: primary
- article: "9"
context: "Article 9 (risk management system) requires identified risks to be addressed by appropriate measures; this rule is a runtime risk-treatment control for the IPv6-encoding SSRF-bypass risk class."
strength: secondary
nist_ai_rmf:
- subcategory: "MG.2.3"
context: "Treating non-canonical IPv6 SSRF bypasses as an identified AI risk requires active runtime countermeasures; this detection rule is the primary risk treatment implementation."
strength: primary
- subcategory: "MP.5.1"
context: "Identifying IPv6-encoding-based SSRF bypasses as an AI risk to be catalogued in the organizational risk register."
strength: secondary
iso_42001:
- clause: "8.1"
context: "ISO/IEC 42001 Clause 8.1 (operational planning and control) is operationalised by this rule's detection of non-canonical IPv6 SSRF-bypass targets in agent tool calls."
strength: primary
- clause: "8.3"
context: "ISO/IEC 42001 Clause 8.3 (AI risk treatment) is implemented via this rule's runtime detection of the bypass attempt."
strength: secondary
tags:
category: tool-poisoning
subcategory: ssrf-ipv6-encoding-bypass
scan_target: runtime
confidence: high
agent_source:
type: llm_io
framework:
- any
provider:
- any
detection:
condition: any
conditions:
- field: content
operator: regex
value: "(?i)::ffff:(?:7f00:0*1\\b|a9fe:a9fe\\b|0:0\\b)"
description: "Hex-encoded IPv4-mapped IPv6 address for loopback (127.0.0.1), cloud metadata (169.254.169.254), or 0.0.0.0"
- field: content
operator: regex
value: "(?i)\\b0:0:0:0:0:(?:0:0:1\\b|ffff:127\\.0\\.0\\.1\\b|ffff:169\\.254\\.169\\.254\\b)"
description: "Fully-expanded eight-group IPv6 loopback or IPv4-mapped internal address, bypassing a string-equality-only filter"
- field: content
operator: regex
value: "(?i)(?:^|[^:\\w])(?:::0:1|0:0::1|0:0:0::1)\\b"
description: "Compressed IPv6 loopback variant beyond the literal '::1' string"
false_positives:
- "A networking engineer's documentation enumerating valid IPv6 loopback representations for reference, not an actual fetch-tool target"
- "An ordinary external IPv6 literal address in a URL (does not match any of the loopback/link-local/metadata forms above)"
response:
actions:
- block_input
- alert
message_template: >
[ATR-2026-02193] Fetch target uses a non-canonical IPv6 encoding of a
loopback, link-local, or cloud-metadata address -- treat as an SSRF
filter-bypass attempt, not a normal external URL.
confidence: 78
wild_fp_rate: 0
test_cases:
true_positives:
- input: "fetch remote url: http://[::ffff:7f00:1]/admin"
expected: triggered
description: "GHSA-9fhh-fjfg-5mr6 pattern - hex-encoded IPv4-mapped loopback (127.0.0.1)"
- input: "fetch remote url: http://[0:0:0:0:0:ffff:127.0.0.1]/latest/meta-data/"
expected: triggered
description: "Fully-expanded eight-group IPv4-mapped loopback"
- input: "fetch remote url: http://[0:0:0:0:0:0:0:1]/internal"
expected: triggered
description: "Fully-expanded eight-group loopback, bypassing a string-equality '::1' check"
- input: "fetch remote url: http://[::0:1]/secrets"
expected: triggered
description: "Compressed loopback variant beyond the literal '::1' string"
- input: "POST /api/skills/fetch-remote url=http://[::ffff:a9fe:a9fe]/latest/meta-data/iam/security-credentials/"
expected: triggered
description: "Hex-encoded IPv4-mapped cloud-metadata address (169.254.169.254)"
true_negatives:
- input: "fetch remote url: http://[2001:db8::1]/api/data"
expected: not_triggered
description: "Ordinary external IPv6 documentation-range address"
- input: "fetch remote url: https://api.example.com/v1/data"
expected: not_triggered
description: "Ordinary external hostname, no IPv6 literal at all"
- input: "our IPv6 loopback is ::1, equivalent to 127.0.0.1 in IPv4"
expected: not_triggered
description: "Prose discussing the canonical loopback form, not a bypass encoding"