Skip to content

[BUG] Hector A2A shared app token lets callers rebind metadata.user_id and read app-scoped tasks #2

Description

@N0zoM1z0

Summary

Hector documents an app JWT model. On the A2A path, tasks are stored by app plus task id, while message.metadata.user_id can override the authenticated subject before session selection. In the replay, Bob holds the same app JWT as Alice, reads Alice's task by taskId, and then sends metadata.user_id=alice with Alice's contextId to enter Alice's downstream session.

Affected Target Verified

  • Repository: https://github.com/verikod/hector
  • Component: A2A JSON-RPC handler under /agents/{agent}
  • Affected commit verified: abe472b6deb12b3dbba290dbec44cd3be27e163d
  • Vulnerability type: Authorization bypass within a shared app-token boundary

Preconditions

  • One app JWT is shared by multiple end users or local callers.
  • The app expects metadata.user_id to represent end-user identity.
  • A task id or context id leaks through normal application flow or logs.

Steps To Reproduce

From the attachment directory:

bash reproduce.sh

The script clones the target repository, checks out the verified commit, prepares a local test environment, and runs the bundled PoC. It does not contact attacker-controlled services; the setup only downloads the public repository and normal build dependencies.

To run against an existing checkout:

HECTOR_TARGET_DIR=/path/to/checkout bash reproduce.sh

The wrapper accepts HECTOR_TARGET_DIR for an existing checkout. It also accepts the target commit override documented in attachments/reproduce.sh.

Technical Details

The JWT subject identifies the app. The A2A event path derives userID from claims, then lets caller-controlled message.metadata.user_id override it. Sessions are keyed as appName:userID:sessionID; tasks are persisted as (app_name, id) without a user dimension.

Expected Behavior

End-user identity inside an authenticated app should come from a trusted claim
or app-verified mapping, not from caller-controlled A2A message metadata. Task
reads and session lookup should include that trusted user dimension when an app
token is shared by multiple users.

Actual Behavior

  • Bob can tasks/get Alice's task and receive Alice's secret-bearing artifact/history.
  • Bob using metadata.user_id=bob on Alice context does not see Alice memory.
  • Bob using metadata.user_id=alice on the same context receives Alice memory.

Impact

Any holder of the shared app token can select another end-user namespace by
supplying message.metadata.user_id. In the reproduced flow, Bob's own
metadata.user_id=bob request does not see Alice's memory, but
metadata.user_id=alice with Alice's context enters Alice's downstream
session.

For deployments where one app token represents an application and multiple end
users are distinguished inside that app, this lets one local caller or peer
read another user's task artifacts/history by task id and then join that user's
session context. The exposed data can include prompts, agent responses,
workflow artifacts, and user-specific memory stored under the app/user/session
tuple.

The issue also weakens audit and attribution. The authenticated token still
identifies the app, but the end-user authority used for session selection comes
from caller-controlled A2A metadata, so later actions may be recorded or
processed under the wrong end-user namespace.

Suggested Fix

Do not trust A2A message metadata for user authority. Derive end-user identity from a trusted authenticated claim or an app-verified mapping, and include that identity in task reads and session lookup.

Attachments

attachments.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions