title: SSRF via Agent Tool Calls
id: ATR-2026-00013
rule_version: 4
status: experimental
description: |
  Detects high-confidence Server-Side Request Forgery (SSRF) that targets cloud CREDENTIAL / metadata services or uses deliberate obfuscation — the cases where an agent has no legitimate reason to make the request. Detection covers: AWS/GCP/Azure/DigitalOcean/Oracle/Alibaba metadata and IMDS credential endpoints and token headers, OBFUSCATED loopback encodings (hex/octal), exotic URI schemes (file, gopher, dict, tftp, ldap) targeting internal addresses, DNS-rebinding service hostnames, IPv4-mapped IPv6 forms of loopback and private IPv4, redirect-parameter SSRF to internal targets, and internal-redirect URL shorteners. NOTE (v2, precision): plain localhost / 127.0.0.1 / ::1, RFC1918 private IPs, and bare internal service names are intentionally NOT flagged by this critical hard-block rule — a coding agent reaches its own dev server and local services (Docker/k8s) constantly, so blocking those is a false positive. Those ambiguous internal-access signals belong in an advisory-severity rule, not this credential-theft hard-block.
author: ATR Community
date: 2026/03/08
schema_version: '0.1'
detection_tier: pattern
maturity: test
severity: critical
references:
  owasp_llm:
    - LLM06:2025
    - LLM05:2025
  owasp_agentic:
    - ASI02:2026
  mitre_atlas:
    - AML.T0049 - Exploit Public-Facing Application
  mitre_attack:
    - T1090 - Proxy
    - T1071 - Application Layer Protocol
  cve:
    - CVE-2019-5418
    - CVE-2021-21311
    - CVE-2026-55096
  external:
    - https://nvd.nist.gov/vuln/detail/CVE-2026-55096
    - https://github.com/leshchenko1979/fast-mcp-telegram/security/advisories/GHSA-xr72-j7vj-vp7g
compliance:
  nist_ai_rmf:
    - subcategory: MS.2.7
      context: SSRF via agent tool calls is a security/resilience failure where attackers pivot agent tool invocations to internal endpoints, cloud metadata services, and private ranges; MS.2.7 requires that these security risks are evaluated and documented through continuous detection of SSRF patterns including IP encoding evasion.
      strength: primary
    - subcategory: MP.5.1
      context: Tool-call SSRF attempts—metadata endpoint access, exotic URI schemes, DNS rebinding, and IP encoding evasion—are adversarial inputs whose likelihood and impact (credential theft, internal network access) must be characterized; MP.5.1 requires identification and tracking of these risk vectors.
      strength: secondary
    - subcategory: MG.2.3
      context: Detection of SSRF indicators in tool parameters triggers risk treatment plans to block or disengage the agent's outbound request before internal services or cloud credentials are exposed; MG.2.3 mandates these response mechanisms are pre-defined.
      strength: secondary
    - subcategory: MG.3.2
      context: NIST AI RMF MANAGE 3.2 (pre-trained models monitored as part of maintenance) is supported where this rule detects the tool-poisoning technique (SSRF via Agent Tool Calls).
      strength: secondary
  iso_42001:
    - clause: '8.1'
      context: ISO/IEC 42001 Clause 8.1 (operational planning and control, including control of externally provided processes) is operationalised by this rule's detection of the tool-poisoning technique (SSRF via Agent Tool Calls).
      strength: primary
    - clause: '8.3'
      context: ISO/IEC 42001 Clause 8.3 (AI risk treatment) is supported by this rule, which implements runtime detection of the tool-poisoning technique (SSRF via Agent Tool Calls) as a treatment control.
      strength: secondary
  eu_ai_act:
    - article: '15'
      context: Article 15 (accuracy, robustness and cybersecurity) requires high-risk AI systems to resist unauthorised attempts to alter their use, outputs or performance; this rule provides runtime detection evidence by flagging the tool-poisoning technique (SSRF via Agent Tool Calls).
      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 that detects the tool-poisoning technique (SSRF via Agent Tool Calls).
      strength: secondary
tags:
  category: tool-poisoning
  subcategory: ssrf
  scan_target: mcp
  confidence: high
agent_source:
  type: tool_call
  framework:
    - any
  provider:
    - any
detection:
  conditions:
    - field: tool_args
      operator: regex
      value: (?i)(https?://)?169\.254\.169\.254|/latest/(meta-data|user-data|api/token|dynamic|instance-identity)|X-aws-ec2-metadata-token|amazonaws\.com.{0,50}(credentials|security-credentials|role)|iam[\/\\]security-credentials
      description: AWS Instance Metadata Service (IMDSv1/v2) and credential endpoints
    - field: tool_args
      operator: regex
      value: (?i)(https?://)?metadata\.google\.internal|/computeMetadata/v1|Metadata-Flavor:\s*Google
      description: GCP metadata service endpoints and required headers
    - field: tool_args
      operator: regex
      value: (?i)(https?://)?169\.254\.169\.254/metadata|Metadata:\s*true|api-version=\d{4}-\d{2}-\d{2}.*metadata|management\.azure\.com.{0,50}(subscriptions|resourceGroups)
      description: Azure Instance Metadata Service and management endpoints
    - field: tool_args
      operator: regex
      value: (?i)(https?://)?169\.254\.169\.254/metadata/v1|/opc/v[12]/|100\.100\.100\.200
      description: DigitalOcean, Oracle Cloud, and Alibaba Cloud metadata endpoints
    - field: tool_args
      operator: regex
      value: (?i)(https?://)?(0x7f000001|0x7f\.0x0\.0x0\.0x1|017700000001|0177\.0000\.0000\.0001|0x0\.0x0\.0x0\.0x0|0000\.0000\.0000\.0000)
      description: 'OBFUSCATED loopback addresses (hex/octal encodings). Plain localhost / 127.0.0.1 / ::1 and RFC1918 private IPs are intentionally NOT matched here: a coding agent legitimately curls its own dev server / local services constantly, so blocking them is a false positive. Only the deliberate encoding-evasion forms (which have no benign dev use) trigger.'
    - field: tool_args
      operator: regex
      value: '(?i)(https?://)?169\.254\.\d{1,3}\.\d{1,3}(:\d+)?|fe80::'
      description: Link-local addresses (169.254.x.x, fe80::)
    - field: tool_args
      operator: regex
      value: (?i)\b(file|gopher|dict|ftp|tftp|ldap|ldaps|sftp|ssh|telnet|jar|netdoc|mailto|view-source|ws|wss)\s*://\s*(localhost|127\.|10\.|172\.(1[6-9]|2[0-9]|3[01])|192\.168\.|0\.0\.0\.0|\[?::1\]?|0x|0177)
      description: Exotic URI schemes targeting internal addresses
    - field: tool_args
      operator: regex
      value: (?i)(https?://)?[a-zA-Z0-9-]{1,63}\.(xip\.io|nip\.io|sslip\.io|localtest\.me|vcap\.me|lvh\.me|lacolhost\.com|127\.0\.0\.1\.[a-z]+\.\w+)(:\d+)?
      description: DNS rebinding services that resolve to internal IPs
    - field: tool_args
      operator: regex
      value: (?i)\b(?:https?|wss?|ftp):(?:\\?/){2}(?:[^\s/\\@"'<>]{1,64}@)?(?:localtest\.me|lvh\.me|vcap\.me|lacolhost\.com)\.?(?:[:/?#"'\s\\\],;)>|]|$)
      description: 'APEX loopback-wildcard domains used as a URL host. localtest.me, lvh.me, vcap.me and lacolhost.com resolve to 127.0.0.1 at the apex as well as on every subdomain, so a hostname-string SSRF denylist that does not resolve DNS lets https://localtest.me/admin reach loopback services (CVE-2026-55096, fast-mcp-telegram send_message files[]). The pattern above only covers the subdomain form; this one requires a URL scheme directly before the apex host (plain "//" or JSON-escaped "\/\/", optional userinfo, optional trailing dot) and then a host terminator character (: / ? # quote whitespace backslash ] , ; ) > |) or end of input, so prose mentions and look-alike hosts such as localtest.mesh.example.com, lvh.media, vcap.me.uk or lvh.me-cdn.net do not match. The terminator is consumed rather than asserted with a lookahead so the pattern stays RE2-portable. Known limitations (not covered): protocol-relative //localtest.me/admin with no scheme; other apex loopback domains such as localho.st or fuf.me; attacker-owned domains whose A record points at an internal IP (needs DNS resolution, not possible at the pattern layer). Known false positives: a developer reaching their own dev server through the lvh.me alias (curl http://lvh.me:3000/...), and a free-text search query that quotes the URL https://localtest.me - the existing subdomain condition above already flags app.lvh.me:3000, so this is consistent with it.'
    - field: tool_args
      operator: regex
      value: (?i)(redirect|redir|url|next|return|returnUrl|returnTo|continue|dest|destination|go|goto|target|link|out|view|ref|callback|forward)\s*=\s*(https?%3A%2F%2F|https?://)(localhost|127\.0\.0\.1|10\.|172\.(1[6-9]|2[0-9])|192\.168|169\.254|0\.0\.0|\[?::1\]?)
      description: URL redirect parameters targeting internal addresses
    - field: tool_args
      operator: regex
      value: (?i)(?:^|[^0-9a-f:]|%5b)\[?(?:(?:0{1,4}:){1,4}:|::|(?:0{1,4}:){5})ffff:(?:127\.\d{1,3}\.\d{1,3}\.\d{1,3}|10\.|172\.(?:1[6-9]|2[0-9]|3[01])\.|192\.168\.)
      description: 'IPv4-mapped IPv6 forms (::ffff:a.b.c.d, also written 0::ffff:, 0:0::ffff: or fully expanded 0:0:0:0:0:ffff:) of loopback (127/8) and RFC 1918 IPv4, an obfuscation that slips past filters written for dotted IPv4. The address must start the token (start of input, a character that is not a hex digit or colon, or a percent-encoded bracket %5B), so the suffix of a longer IPv6 address does not count. Known limits: a mapped address directly after a colon (unbracketed host:port:addr syntax such as curl --resolve x:80:::ffff:127.0.0.1) and a JSON-escaped bracket (\u005b) are not matched; the hex form ::ffff:7f00:1, IPv4-compatible ::127.0.0.1 and NAT64 64:ff9b:: forms are left to ATR-2026-02868; ::ffff:0.0.0.0 and ::ffff:100.64.0.0/10 are not covered. Plain ::1 and unique local addresses (fc00::/7) are not matched, for the same reason plain localhost, 127.0.0.1 and RFC 1918 are not (see the v2 note in the description): a coding agent reaches its own dev server on [::1] and private services on ULA all the time. v3 matched both with no token boundary, so the tail of a public 6to4 address (2002:cb00:7101::1), ::10, and Oracle APEX URLs (:::190) matched as loopback.'
    - field: tool_args
      operator: regex
      value: (?i)(https?://)?(bit\.ly|tinyurl\.com|t\.co|goo\.gl|is\.gd|v\.gd|rb\.gy|cutt\.ly|short\.io|tiny\.cc)/[a-zA-Z0-9]+
      description: URL shorteners that may redirect to internal services
  condition: any
  false_positives:
    - Logs and diagnostics pasted into a tool argument that report IPv4 peers in IPv4-mapped form, as dual-stack sockets do on Linux (netstat / ss / lsof output, Node "connect ECONNREFUSED ::ffff:127.0.0.1:6379", Express req.ip ::ffff:192.168.1.10, nginx or MySQL messages with ::ffff:172.17.0.1). Loopback addresses of system resolvers such as ::ffff:127.0.0.53 match too, since 127/8 is covered.
    - Agents with legitimate access to internal services explicitly allowlisted
    - Development environments where localhost access is expected
    - Private network tools designed for internal infrastructure management
    - Cloud management tools that legitimately query metadata endpoints
    - Service mesh tools that use .internal or .consul DNS names by design
    - Developers who use the lvh.me / localtest.me / vcap.me / lacolhost.com loopback aliases for their own dev server (common in the Rails community, e.g. curl http://lvh.me:3000/users/sign_in) - both the apex and the subdomain forms are flagged, unlike plain localhost which v2 deliberately allows. Maintainers should decide whether these aliases belong in this hard-block rule or an advisory-severity rule.
    - Free-text tool arguments (search queries, notes) that quote a full https://localtest.me style URL while discussing it rather than requesting it.
response:
  actions:
    - block_tool
    - alert
    - snapshot
  actions_rationale: 'response-action eligibility (docs/RESPONSE-ACTION-ELIGIBILITY.md): benign FP 39/5352 = 0.73% exceeds the 0.5% production ceiling; ceiling = interrupt. Removed kill_agent(terminate). Detection unchanged — recall cost is zero.'
  auto_response_threshold: critical
  message_template: |
    [ATR-2026-013] CRITICAL: SSRF attempt detected via agent tool call. Rule: ATR-2026-013. Tool: {tool_name}. Pattern: {matched_pattern}. The tool call has been blocked; the agent was NOT killed.
confidence: 90
wild_validated: 2026/04/08
wild_samples: 53577
