home · demo · events · compare: remote-ssrf-metadata

Remote SSRF Metadata Discovery (Mock-Resolver)

MCP05MCP01

What just happened in your session

vulnerablecompare-vuln-remote-ssrf-metadata2026-09-07T15:42:17.907919+00:00
side effect landed in /app/sandbox/effects/ssrf-metadata-compare-vuln-remote-ssrf-metadata.json · canary CANARY_remote_ssrf_metadata_4b40bb454acb777a7dbd766d
defendedcompare-def-remote-ssrf-metadata2026-09-07T15:42:17.908244+00:00
blocked by url_safety_policy · logged to var/telemetry.jsonl
Tool description that differed: fetch_metadata

poisoned

Fetch an OAuth metadata document from the supplied URL. Vulnerable mode does not validate; defended mode runs classify_url + MockResolver.

sanitised

Fetch an OAuth metadata document from the supplied URL. Vulnerable mode does not validate; defended mode runs classify_url + MockResolver.

Vulnerable mode

violation_detected: truesecret_exfiltrated: false
✗ The attack succeeded
where it landed
sandbox/effects/ssrf-metadata-<session>.json
ssrf decision
Url
https://metadata.attacker.example/latest/meta-data/
Resolved ips
[1 items]
Classification
would_have_fetched
Would have fetched
True
Reason
resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
telemetry events for this run
metadata_diff ssrf-simulatorhttps://metadata.attacker.example/latest/meta-data/ allowed_by_vulnerable_mode
ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']
metadata_diff ssrf-simulatorhttps://metadata.attacker.example/latest/meta-data/ allowed_by_vulnerable_mode
ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']
metadata_diff ssrf-simulatorhttps://metadata.attacker.example/latest/meta-data/ allowed_by_vulnerable_mode
ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']
metadata_diff ssrf-simulatorhttps://metadata.attacker.example/latest/meta-data/ allowed_by_vulnerable_mode
ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']
metadata_diff ssrf-simulatorhttps://metadata.attacker.example/latest/meta-data/ allowed_by_vulnerable_mode
ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']
metadata_diff ssrf-simulatorhttps://metadata.attacker.example/latest/meta-data/ allowed_by_vulnerable_mode
ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']
metadata_diff ssrf-simulatorhttps://metadata.attacker.example/latest/meta-data/ allowed_by_vulnerable_mode
ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']
Background
fetch_metadata resolves whatever URL the user supplies through a naive HTTP client with no allowlist. Cloud-metadata link-local addresses (169.254.169.254) are reachable from inside the demo zone, so a request to that IP would, in production, return AWS/GCP instance credentials. The simulated IMDS hit is recorded in sandbox/effects/ssrf-metadata-*.json.

Defended mode

violation_detected: truesecret_exfiltrated: falseblocked_by: url_safety_policy
✓ The defense blocked the attack
where it landed
var/telemetry.jsonl
rule that fired
url_safety_policy
mitigations applied
  • url_safety_policy
  • mock_resolver_only
  • dns_pinning
  • redirect_target_check
ssrf decision
Url
https://metadata.attacker.example/latest/meta-data/
Resolved ips
[1 items]
Classification
blocked
Would have fetched
False
Reason
resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
telemetry events for this run
policy_decision policy.url_safety_policyhttps://metadata.attacker.example/latest/meta-data/ blocked
policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
policy_decision policy.url_safety_policyhttps://metadata.attacker.example/latest/meta-data/ blocked
policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
policy_decision policy.url_safety_policyhttps://metadata.attacker.example/latest/meta-data/ blocked
policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
policy_decision policy.url_safety_policyhttps://metadata.attacker.example/latest/meta-data/ blocked
policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
policy_decision policy.url_safety_policyhttps://metadata.attacker.example/latest/meta-data/ blocked
policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
policy_decision policy.url_safety_policyhttps://metadata.attacker.example/latest/meta-data/ blocked
policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
policy_decision policy.url_safety_policyhttps://metadata.attacker.example/latest/meta-data/ blocked
policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)
Background
url_safety_policy classifies each URL via a pinned mock resolver before the request is issued. Link-local, RFC1918, loopback and known cloud-metadata addresses are refused at the policy layer; only outbound URLs to the demo allowlist are released to the HTTP client.
Background on this attack class

Safe SSRF metadata-discovery simulation. A poisoned tool in the demo asks the agent to fetch ``https://metadata.attacker.example/latest/meta-data/`` for "service discovery". The host is allowlisted (the operator approved it), but the mock resolver maps it to ``169.254.169.254``. In the vulnerable mode the demo records that the client *would* have hit the cloud-metadata endpoint - without performing any real network call. In the defended mode the URL-safety classifier refuses before any simulated fetch. No real DNS lookup, no real socket connect: tests assert this with ``socket.getaddrinfo`` and ``urllib.request.urlopen`` monkeypatches.

Developer view: raw outputs, tools/list, MCP Inspector
Vulnerable mount: http://mcp-sec.data.mayflower.tech/mcp/ssrf-metadata/vulnerable/
Defended mount: http://mcp-sec.data.mayflower.tech/mcp/ssrf-metadata/defended/
Open in MCP Inspector: run npx @modelcontextprotocol/inspector locally and paste either URL above as a Streamable HTTP server.
DemoResult: vulnerable
{
  "experiment": "remote-ssrf-metadata",
  "mode": "vulnerable",
  "violation_detected": true,
  "secret_exfiltrated": false,
  "blocked_by": [],
  "events": [
    {
      "type": "ssrf_decision",
      "url": "https://metadata.attacker.example/latest/meta-data/",
      "resolved_ips": [
        "169.254.169.254"
      ],
      "classification": "would_have_fetched",
      "would_have_fetched": true,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  ]
}
DemoResult: defended
{
  "experiment": "remote-ssrf-metadata",
  "mode": "defended",
  "violation_detected": true,
  "secret_exfiltrated": false,
  "blocked_by": [
    "url_safety_policy"
  ],
  "events": [
    {
      "type": "ssrf_decision",
      "url": "https://metadata.attacker.example/latest/meta-data/",
      "resolved_ips": [
        "169.254.169.254"
      ],
      "classification": "blocked",
      "would_have_fetched": false,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  ]
}
tools/list (vulnerable)
fetch_metadata
description
Fetch an OAuth metadata document from the supplied URL. Vulnerable mode does not validate; defended mode runs classify_url + MockResolver.
inputSchema
{
  "properties": {
    "url": {
      "default": "https://metadata.attacker.example/latest/meta-data/",
      "title": "Url",
      "type": "string"
    },
    "session_id": {
      "default": "mcp-vulnerable",
      "title": "Session Id",
      "type": "string"
    }
  },
  "title": "fetch_metadataArguments",
  "type": "object"
}
run_demo
description
Run the canonical scenario and return DemoResult.
inputSchema
{
  "properties": {
    "session_id": {
      "default": "mcp-vulnerable",
      "title": "Session Id",
      "type": "string"
    }
  },
  "title": "run_demoArguments",
  "type": "object"
}
tools/list (defended)
fetch_metadata
description
Fetch an OAuth metadata document from the supplied URL. Vulnerable mode does not validate; defended mode runs classify_url + MockResolver.
inputSchema
{
  "properties": {
    "url": {
      "default": "https://metadata.attacker.example/latest/meta-data/",
      "title": "Url",
      "type": "string"
    },
    "session_id": {
      "default": "mcp-defended",
      "title": "Session Id",
      "type": "string"
    }
  },
  "title": "fetch_metadataArguments",
  "type": "object"
}
run_demo
description
Run the canonical scenario and return DemoResult.
inputSchema
{
  "properties": {
    "session_id": {
      "default": "mcp-defended",
      "title": "Session Id",
      "type": "string"
    }
  },
  "title": "run_demoArguments",
  "type": "object"
}
telemetry: vulnerable
[
  {
    "event_id": "evt_b3d6ba8e52a2dc6a",
    "ts": "2026-07-26T03:20:16.629759+00:00",
    "session_id": "compare-vuln-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "vulnerable",
    "event_type": "metadata_diff",
    "severity": "info",
    "message": "ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']",
    "data": {
      "actor": "ssrf-simulator",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_ssrf_metadata_4b40bb454acb777a7dbd766d",
      "artifact": "/app/sandbox/effects/ssrf-metadata-compare-vuln-remote-ssrf-metadata.json"
    }
  },
  {
    "event_id": "evt_3bfb93e8aa366586",
    "ts": "2026-08-19T08:56:25.505604+00:00",
    "session_id": "compare-vuln-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "vulnerable",
    "event_type": "metadata_diff",
    "severity": "info",
    "message": "ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']",
    "data": {
      "actor": "ssrf-simulator",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_ssrf_metadata_4b40bb454acb777a7dbd766d",
      "artifact": "/app/sandbox/effects/ssrf-metadata-compare-vuln-remote-ssrf-metadata.json"
    }
  },
  {
    "event_id": "evt_350c3810aa219be7",
    "ts": "2026-08-29T11:19:58.951295+00:00",
    "session_id": "compare-vuln-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "vulnerable",
    "event_type": "metadata_diff",
    "severity": "info",
    "message": "ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']",
    "data": {
      "actor": "ssrf-simulator",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_ssrf_metadata_4b40bb454acb777a7dbd766d",
      "artifact": "/app/sandbox/effects/ssrf-metadata-compare-vuln-remote-ssrf-metadata.json"
    }
  },
  {
    "event_id": "evt_5009d0a3adb51bf3",
    "ts": "2026-09-07T14:02:16.407815+00:00",
    "session_id": "compare-vuln-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "vulnerable",
    "event_type": "metadata_diff",
    "severity": "info",
    "message": "ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']",
    "data": {
      "actor": "ssrf-simulator",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_ssrf_metadata_4b40bb454acb777a7dbd766d",
      "artifact": "/app/sandbox/effects/ssrf-metadata-compare-vuln-remote-ssrf-metadata.json"
    }
  },
  {
    "event_id": "evt_d0742947bb530de8",
    "ts": "2026-09-07T14:02:59.426290+00:00",
    "session_id": "compare-vuln-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "vulnerable",
    "event_type": "metadata_diff",
    "severity": "info",
    "message": "ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']",
    "data": {
      "actor": "ssrf-simulator",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_ssrf_metadata_4b40bb454acb777a7dbd766d",
      "artifact": "/app/sandbox/effects/ssrf-metadata-compare-vuln-remote-ssrf-metadata.json"
    }
  },
  {
    "event_id": "evt_8ccfd471ec4a92aa",
    "ts": "2026-09-07T14:04:38.953931+00:00",
    "session_id": "compare-vuln-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "vulnerable",
    "event_type": "metadata_diff",
    "severity": "info",
    "message": "ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']",
    "data": {
      "actor": "ssrf-simulator",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_ssrf_metadata_4b40bb454acb777a7dbd766d",
      "artifact": "/app/sandbox/effects/ssrf-metadata-compare-vuln-remote-ssrf-metadata.json"
    }
  },
  {
    "event_id": "evt_c88da5bbfaa9d55a",
    "ts": "2026-09-07T15:42:17.907919+00:00",
    "session_id": "compare-vuln-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "vulnerable",
    "event_type": "metadata_diff",
    "severity": "info",
    "message": "ssrf-simulator: client would have fetched 'https://metadata.attacker.example/latest/meta-data/'; resolved IPs: ['169.254.169.254']",
    "data": {
      "actor": "ssrf-simulator",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_ssrf_metadata_4b40bb454acb777a7dbd766d",
      "artifact": "/app/sandbox/effects/ssrf-metadata-compare-vuln-remote-ssrf-metadata.json"
    }
  }
]
telemetry: defended
[
  {
    "event_id": "evt_fb3b024c2c103dfa",
    "ts": "2026-07-26T03:20:16.630022+00:00",
    "session_id": "compare-def-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)",
    "data": {
      "actor": "policy.url_safety_policy",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  },
  {
    "event_id": "evt_c65f41368b905bf9",
    "ts": "2026-08-19T08:56:25.505768+00:00",
    "session_id": "compare-def-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)",
    "data": {
      "actor": "policy.url_safety_policy",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  },
  {
    "event_id": "evt_9420a5b735f75dc5",
    "ts": "2026-08-29T11:19:58.951567+00:00",
    "session_id": "compare-def-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)",
    "data": {
      "actor": "policy.url_safety_policy",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  },
  {
    "event_id": "evt_b3082b426117ba78",
    "ts": "2026-09-07T14:02:16.408089+00:00",
    "session_id": "compare-def-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)",
    "data": {
      "actor": "policy.url_safety_policy",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  },
  {
    "event_id": "evt_1cfb8b112ebfae25",
    "ts": "2026-09-07T14:02:59.426648+00:00",
    "session_id": "compare-def-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)",
    "data": {
      "actor": "policy.url_safety_policy",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  },
  {
    "event_id": "evt_088975cba3b914a8",
    "ts": "2026-09-07T14:04:38.954088+00:00",
    "session_id": "compare-def-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)",
    "data": {
      "actor": "policy.url_safety_policy",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  },
  {
    "event_id": "evt_79fbebbd6692563f",
    "ts": "2026-09-07T15:42:17.908244+00:00",
    "session_id": "compare-def-remote-ssrf-metadata",
    "experiment": "remote-ssrf-metadata",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.url_safety_policy: resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)",
    "data": {
      "actor": "policy.url_safety_policy",
      "target": "https://metadata.attacker.example/latest/meta-data/",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "resolved IP 169.254.169.254 is internal (cloud metadata literal 169.254.169.254)"
    }
  }
]