home · demo · events · compare: remote-auth-confused-deputy

Remote Auth Confused Deputy (Fake-OAuth)

MCP01MCP07

What just happened in your session

vulnerablecompare-vuln-remote-auth-confused-deputy2026-09-07T15:44:38.098862+00:00
side effect landed in var/telemetry.jsonl · canary CANARY_remote_auth_confused_deputy_c13666533560c487bd878c74
defendedcompare-def-remote-auth-confused-deputy2026-09-07T15:44:38.099567+00:00
blocked by audience_mismatch · logged to var/telemetry.jsonl
Tool description that differed: update_profile

poisoned

Update the fake CRM record for a user. Authorisation via the bearer token argument.

sanitised

Update the fake CRM record for a user. Authorisation via the bearer token argument.

Vulnerable mode

violation_detected: truesecret_exfiltrated: false
✗ The attack succeeded
where it landed
var/telemetry.jsonl
auth decision
Check
skipped
Outcome
applied_change_without_verification
telemetry events for this run
permission_change_applied client:demo-clientfake-crm.alice allowed_by_vulnerable_mode
client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check
permission_change_applied client:demo-clientfake-crm.alice allowed_by_vulnerable_mode
client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check
permission_change_applied client:demo-clientfake-crm.alice allowed_by_vulnerable_mode
client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check
permission_change_applied client:demo-clientfake-crm.alice allowed_by_vulnerable_mode
client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check
permission_change_applied client:demo-clientfake-crm.alice allowed_by_vulnerable_mode
client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check
permission_change_applied client:demo-clientfake-crm.alice allowed_by_vulnerable_mode
client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check
permission_change_applied client:demo-clientfake-crm.alice allowed_by_vulnerable_mode
client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check
Background
The vulnerable update_profile accepts an OAuth bearer (FAKEJWT) and treats it as authorisation as long as the JWT decodes. It does not check the audience claim, so a token issued for a different service is still applied: the helper acts as a confused deputy on behalf of the wrong principal and the fake CRM mutates with the wrong-aud token.

Defended mode

violation_detected: truesecret_exfiltrated: falseblocked_by: audience_mismatch
✓ The defense blocked the attack
where it landed
var/telemetry.jsonl
mitigations applied
  • audience_validation
  • expiry_validation
  • scope_subset_check
  • per_client_redirect_consent
auth decision
Check
audience_mismatch
Expected
mcp-demo-server
Actual
other-mcp-service
Reason
audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'
telemetry events for this run
policy_decision policy.fake_oauth.audience_mismatchfake-crm.alice blocked
policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'
policy_decision policy.fake_oauth.audience_mismatchfake-crm.alice blocked
policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'
policy_decision policy.fake_oauth.audience_mismatchfake-crm.alice blocked
policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'
policy_decision policy.fake_oauth.audience_mismatchfake-crm.alice blocked
policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'
policy_decision policy.fake_oauth.audience_mismatchfake-crm.alice blocked
policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'
policy_decision policy.fake_oauth.audience_mismatchfake-crm.alice blocked
policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'
policy_decision policy.fake_oauth.audience_mismatchfake-crm.alice blocked
policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'
Background
The wrapper verifies aud (must equal mcp-demo-server), exp (must be in the future), scope (must be a subset of the granted set) and the per-client/per-redirect-uri consent record. Wrong-aud, expired or over-scoped tokens are rejected with the specific failed check named in telemetry.
Background on this attack class

Safe fake-OAuth confused-deputy demo. A fake CRM stores ``alice``'s display name. A fake "helper" client tries to update it via an MCP tool whose proxy validates a bearer token. In the vulnerable mode the proxy accepts the token without checking the audience, the client identity or the consent record. In the defended mode the proxy verifies audience (`mcp-demo-server`), expiry, scopes and the per-client/per-redirect-uri consent record before applying the change. Nothing real is touched: tokens are FAKEJWT strings, the issuer URL is ``https://issuer.demo.invalid``, redirect URIs must end in ``.demo.invalid`` or ``.example``, and the CRM is a process-local dict.

Developer view: raw outputs, tools/list, MCP Inspector
Vulnerable mount: http://mcp-sec.data.mayflower.tech/mcp/auth-confused-deputy/vulnerable/
Defended mount: http://mcp-sec.data.mayflower.tech/mcp/auth-confused-deputy/defended/
Open in MCP Inspector: run npx @modelcontextprotocol/inspector locally and paste either URL above as a Streamable HTTP server.
DemoResult: vulnerable
{
  "experiment": "remote-auth-confused-deputy",
  "mode": "vulnerable",
  "violation_detected": true,
  "secret_exfiltrated": false,
  "blocked_by": [],
  "events": [
    {
      "type": "auth_decision",
      "check": "skipped",
      "outcome": "applied_change_without_verification"
    }
  ]
}
DemoResult: defended
{
  "experiment": "remote-auth-confused-deputy",
  "mode": "defended",
  "violation_detected": true,
  "secret_exfiltrated": false,
  "blocked_by": [
    "audience_mismatch"
  ],
  "events": [
    {
      "type": "auth_decision",
      "check": "audience_mismatch",
      "expected": "mcp-demo-server",
      "actual": "other-mcp-service",
      "reason": "audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'"
    }
  ]
}
tools/list (vulnerable)
update_profile
description
Update the fake CRM record for a user. Authorisation via the bearer token argument.
inputSchema
{
  "properties": {
    "user_id": {
      "title": "User Id",
      "type": "string"
    },
    "new_display_name": {
      "title": "New Display Name",
      "type": "string"
    },
    "bearer": {
      "title": "Bearer",
      "type": "string"
    }
  },
  "required": [
    "user_id",
    "new_display_name",
    "bearer"
  ],
  "title": "update_profileArguments",
  "type": "object"
}
run_demo
description
Drive the canonical auth-confused-deputy scenario for this mode and return the DemoResult JSON.
inputSchema
{
  "properties": {
    "session_id": {
      "default": "mcp-vulnerable",
      "title": "Session Id",
      "type": "string"
    }
  },
  "title": "run_demoArguments",
  "type": "object"
}
tools/list (defended)
update_profile
description
Update the fake CRM record for a user. Authorisation via the bearer token argument.
inputSchema
{
  "properties": {
    "user_id": {
      "title": "User Id",
      "type": "string"
    },
    "new_display_name": {
      "title": "New Display Name",
      "type": "string"
    },
    "bearer": {
      "title": "Bearer",
      "type": "string"
    }
  },
  "required": [
    "user_id",
    "new_display_name",
    "bearer"
  ],
  "title": "update_profileArguments",
  "type": "object"
}
run_demo
description
Drive the canonical auth-confused-deputy scenario for this mode and return the DemoResult JSON.
inputSchema
{
  "properties": {
    "session_id": {
      "default": "mcp-defended",
      "title": "Session Id",
      "type": "string"
    }
  },
  "title": "run_demoArguments",
  "type": "object"
}
telemetry: vulnerable
[
  {
    "event_id": "evt_2abe0c27ad3baa4b",
    "ts": "2026-07-26T03:20:16.415480+00:00",
    "session_id": "compare-vuln-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "vulnerable",
    "event_type": "permission_change_applied",
    "severity": "info",
    "message": "client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check",
    "data": {
      "actor": "client:demo-client",
      "target": "fake-crm.alice",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_auth_confused_deputy_c13666533560c487bd878c74",
      "artifact": null
    }
  },
  {
    "event_id": "evt_4144db3171984e80",
    "ts": "2026-08-19T08:56:24.833959+00:00",
    "session_id": "compare-vuln-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "vulnerable",
    "event_type": "permission_change_applied",
    "severity": "info",
    "message": "client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check",
    "data": {
      "actor": "client:demo-client",
      "target": "fake-crm.alice",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_auth_confused_deputy_c13666533560c487bd878c74",
      "artifact": null
    }
  },
  {
    "event_id": "evt_56a16490c2377f12",
    "ts": "2026-08-29T11:19:48.424067+00:00",
    "session_id": "compare-vuln-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "vulnerable",
    "event_type": "permission_change_applied",
    "severity": "info",
    "message": "client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check",
    "data": {
      "actor": "client:demo-client",
      "target": "fake-crm.alice",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_auth_confused_deputy_c13666533560c487bd878c74",
      "artifact": null
    }
  },
  {
    "event_id": "evt_180b243788b6efc3",
    "ts": "2026-09-07T14:02:15.969519+00:00",
    "session_id": "compare-vuln-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "vulnerable",
    "event_type": "permission_change_applied",
    "severity": "info",
    "message": "client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check",
    "data": {
      "actor": "client:demo-client",
      "target": "fake-crm.alice",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_auth_confused_deputy_c13666533560c487bd878c74",
      "artifact": null
    }
  },
  {
    "event_id": "evt_b7fdaebaa487d497",
    "ts": "2026-09-07T14:02:58.970406+00:00",
    "session_id": "compare-vuln-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "vulnerable",
    "event_type": "permission_change_applied",
    "severity": "info",
    "message": "client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check",
    "data": {
      "actor": "client:demo-client",
      "target": "fake-crm.alice",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_auth_confused_deputy_c13666533560c487bd878c74",
      "artifact": null
    }
  },
  {
    "event_id": "evt_bf661ea9eaf66b35",
    "ts": "2026-09-07T14:04:38.448793+00:00",
    "session_id": "compare-vuln-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "vulnerable",
    "event_type": "permission_change_applied",
    "severity": "info",
    "message": "client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check",
    "data": {
      "actor": "client:demo-client",
      "target": "fake-crm.alice",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_auth_confused_deputy_c13666533560c487bd878c74",
      "artifact": null
    }
  },
  {
    "event_id": "evt_481dc8ec85a17ca8",
    "ts": "2026-09-07T15:44:38.098862+00:00",
    "session_id": "compare-vuln-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "vulnerable",
    "event_type": "permission_change_applied",
    "severity": "info",
    "message": "client:demo-client: fake-CRM display_name was rewritten via a token whose audience/consent the vulnerable proxy did not check",
    "data": {
      "actor": "client:demo-client",
      "target": "fake-crm.alice",
      "policy_decision": "allowed_by_vulnerable_mode",
      "canary_id": "CANARY_remote_auth_confused_deputy_c13666533560c487bd878c74",
      "artifact": null
    }
  }
]
telemetry: defended
[
  {
    "event_id": "evt_c74ebc3b541d923c",
    "ts": "2026-07-26T03:20:16.416262+00:00",
    "session_id": "compare-def-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'",
    "data": {
      "actor": "policy.fake_oauth.audience_mismatch",
      "target": "fake-crm.alice",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'"
    }
  },
  {
    "event_id": "evt_b7b97545272c1da0",
    "ts": "2026-08-19T08:56:24.834122+00:00",
    "session_id": "compare-def-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'",
    "data": {
      "actor": "policy.fake_oauth.audience_mismatch",
      "target": "fake-crm.alice",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'"
    }
  },
  {
    "event_id": "evt_72e79578e2cb8808",
    "ts": "2026-08-29T11:19:48.424896+00:00",
    "session_id": "compare-def-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'",
    "data": {
      "actor": "policy.fake_oauth.audience_mismatch",
      "target": "fake-crm.alice",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'"
    }
  },
  {
    "event_id": "evt_c09e3e26575d65f0",
    "ts": "2026-09-07T14:02:15.969961+00:00",
    "session_id": "compare-def-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'",
    "data": {
      "actor": "policy.fake_oauth.audience_mismatch",
      "target": "fake-crm.alice",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'"
    }
  },
  {
    "event_id": "evt_b9a3b04b53f44bc0",
    "ts": "2026-09-07T14:02:58.970689+00:00",
    "session_id": "compare-def-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'",
    "data": {
      "actor": "policy.fake_oauth.audience_mismatch",
      "target": "fake-crm.alice",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'"
    }
  },
  {
    "event_id": "evt_4f4eaaa2dad84cdf",
    "ts": "2026-09-07T14:04:38.449215+00:00",
    "session_id": "compare-def-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'",
    "data": {
      "actor": "policy.fake_oauth.audience_mismatch",
      "target": "fake-crm.alice",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'"
    }
  },
  {
    "event_id": "evt_5a21496ef0b550e7",
    "ts": "2026-09-07T15:44:38.099567+00:00",
    "session_id": "compare-def-remote-auth-confused-deputy",
    "experiment": "remote-auth-confused-deputy",
    "mode": "defended",
    "event_type": "policy_decision",
    "severity": "warning",
    "message": "policy.fake_oauth.audience_mismatch: audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'",
    "data": {
      "actor": "policy.fake_oauth.audience_mismatch",
      "target": "fake-crm.alice",
      "policy_decision": "blocked",
      "canary_id": null,
      "artifact": null,
      "reason": "audience mismatch: expected 'mcp-demo-server', got 'other-mcp-service'"
    }
  }
]