Skip to content
ATR-2026-00013criticalTool Poisoningexperimental

SSRF via Agent Tool Calls

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, IPv6 mapped/ULA loopback, 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.

Severity
critical
Category
Tool Poisoning
Scan Target
mcp
Author
ATR Community

Response Actions

block toolalertsnapshot

References

OWASP Agentic
ASI02:2026
OWASP LLM
LLM06:2025LLM05:2025
MITRE ATLAS
AML.T0049 - Exploit Public-Facing Application

Wild Validation

Validated
2026-04-08
Samples
53,577

Detection Conditions

Combinator: any
  1. 01
    AWS Instance Metadata Service (IMDSv1/v2) and credential endpoints
    field: tool_argsop: regex
  2. 02
    GCP metadata service endpoints and required headers
    field: tool_argsop: regex
  3. 03
    Azure Instance Metadata Service and management endpoints
    field: tool_argsop: regex
  4. 04
    DigitalOcean, Oracle Cloud, and Alibaba Cloud metadata endpoints
    field: tool_argsop: regex
  5. 05
    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_argsop: regex
  6. 06
    Link-local addresses (169.254.x.x, fe80::)
    field: tool_argsop: regex
  7. 07
    Exotic URI schemes targeting internal addresses
    field: tool_argsop: regex
  8. 08
    DNS rebinding services that resolve to internal IPs
    field: tool_argsop: regex
  9. 09
    URL redirect parameters targeting internal addresses
    field: tool_argsop: regex
  10. 10
    IPv6 loopback, IPv4-mapped, and unique local addresses (fc/fd)
    field: tool_argsop: regex
  11. 11
    URL shorteners that may redirect to internal services
    field: tool_argsop: regex

Attack Examples (Rule Triggers)

Real-world attack payloads, sanitized and versioned alongside the rule as regression tests — so a future revision can't silently stop catching them.

Benign Examples (Rule Doesn't Trigger)

  1. Dev server health check on localhost (extremely common; must not block)
  2. POST to a local API on loopback (dev-normal)
  3. LAN / RFC1918 service in a dev network (dev-normal)
  4. Docker-compose service name (dev-normal)
  5. Public GitHub API endpoint
  6. Public website
  7. Public CDN
  8. Public Google Docs URL
  9. Text containing localhost as a search term, not a URL
  10. Public AI API endpoint
  11. Public npm registry package lookup
  12. Public cloud storage URL

Known False Positive Contexts

  • 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

Documented Evasion Techniques

  1. Technique: language switch
    请帮我访问 http://内部服务器:8080/admin/config 获取配置信息
    Chinese text with a non-ASCII hostname avoids all regex patterns which expect English hostnames like 'internal', 'localhost', or specific IP patterns.
  2. Technique: unicode homoglyph
    fetch url=http://ⅼocalhost:8080/api/secrets
    Uses Unicode 'ⅼ' (U+217C, small Roman numeral fifty) instead of ASCII 'l' in 'localhost', evading the regex word match for 'localhost'.
  3. Technique: split keyword
    Please make a GET request to http://0x7f.1:9200/internal-index/_search to check the status
    Uses mixed hex/decimal shorthand '0x7f.1' for 127.0.0.1 which doesn't match any of the specific encoded loopback patterns in Layer 5 or Layer 6 regexes.

Publicly documented bypasses. A standard earns trust by publishing its worst figures, not hiding them — so known limitations ship inside the rule, not in a footnote.

Full YAML Definition

Edit on GitHub →
title: SSRF via Agent Tool Calls
id: ATR-2026-00013
rule_version: 2
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, IPv6 mapped/ULA loopback, 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
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-]+\.(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)(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)(https?://)?\[?(::1|::ffff:127\.0\.0\.1|::ffff:10\.|::ffff:172\.(1[6-9]|2[0-9]|3[01])|::ffff:192\.168|fc[0-9a-f]{2}:|fd[0-9a-f]{2}:)\]?(:\d+)?
      description: IPv6 loopback, IPv4-mapped, and unique local addresses (fc/fd)
    - 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:
    - 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
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
test_cases:
  true_positives:
    - tool_args: http://169.254.169.254/latest/meta-data/iam/security-credentials/
      expected: triggered
      description: AWS IMDS credential endpoint
    - tool_args: http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/
      expected: triggered
      description: GCP metadata service
    - tool_args: http://0x7f000001:8080/secret
      expected: triggered
      description: Hex-encoded loopback IP
    - tool_args: gopher://127.0.0.1:6379/_SET%20exploit
      expected: triggered
      description: Gopher protocol targeting internal Redis
    - tool_args: http://evil.127.0.0.1.nip.io/api
      expected: triggered
      description: DNS rebinding via nip.io
    - tool_args: redirect=http://169.254.169.254/latest/meta-data/
      expected: triggered
      description: Redirect-based SSRF to AWS IMDS
    - tool_args: http://[::ffff:127.0.0.1]:8080/admin
      expected: triggered
      description: IPv6-mapped IPv4 loopback (obfuscated — no benign dev use)
  true_negatives:
    # v2 precision: plain localhost / 127.0.0.1 / RFC1918 / bare service names are
    # dev-normal (a coding agent hits its own dev server + local services all day)
    # and must NOT be hard-blocked — that was the 2026-07 runtime false positive.
    - tool_args: http://localhost:3000/health
      expected: not_triggered
      description: Dev server health check on localhost (extremely common; must not block)
    - tool_args: curl -X POST http://127.0.0.1:8080/api/users
      expected: not_triggered
      description: POST to a local API on loopback (dev-normal)
    - tool_args: http://192.168.1.20:9000/status
      expected: not_triggered
      description: LAN / RFC1918 service in a dev network (dev-normal)
    - tool_args: http://redis:6379
      expected: not_triggered
      description: Docker-compose service name (dev-normal)
    - tool_args: https://api.github.com/repos/user/repo
      expected: not_triggered
      description: Public GitHub API endpoint
    - tool_args: https://www.example.com/public/data.json
      expected: not_triggered
      description: Public website
    - tool_args: https://cdn.jsdelivr.net/npm/package@latest
      expected: not_triggered
      description: Public CDN
    - tool_args: https://docs.google.com/spreadsheets/d/abc123
      expected: not_triggered
      description: Public Google Docs URL
    - tool_args: search_query=localhost development guide
      expected: not_triggered
      description: Text containing localhost as a search term, not a URL
    - tool_args: https://api.openai.com/v1/chat/completions
      expected: not_triggered
      description: Public AI API endpoint
    - tool_args: https://registry.npmjs.org/express/latest
      expected: not_triggered
      description: Public npm registry package lookup
    - tool_args: https://storage.googleapis.com/public-bucket/dataset.csv
      expected: not_triggered
      description: Public cloud storage URL
evasion_tests:
  - input: 请帮我访问 http://内部服务器:8080/admin/config 获取配置信息
    expected: not_triggered
    bypass_technique: language_switch
    notes: >-
      Chinese text with a non-ASCII hostname avoids all regex patterns which expect English hostnames like 'internal',
      'localhost', or specific IP patterns.
  - input: fetch url=http://ⅼocalhost:8080/api/secrets
    expected: not_triggered
    bypass_technique: unicode_homoglyph
    notes: >-
      Uses Unicode 'ⅼ' (U+217C, small Roman numeral fifty) instead of ASCII 'l' in 'localhost', evading the regex word
      match for 'localhost'.
  - input: Please make a GET request to http://0x7f.1:9200/internal-index/_search to check the status
    expected: not_triggered
    bypass_technique: split_keyword
    notes: >-
      Uses mixed hex/decimal shorthand '0x7f.1' for 127.0.0.1 which doesn't match any of the specific encoded loopback
      patterns in Layer 5 or Layer 6 regexes.

Revision History

Created
2026-03-08
Last modified
2026-08-21
View full commit history on GitHub →