Attack walkthroughs
Each walkthrough names the step where the attack stops and how sure we are of it: live on preprod, tested offline, or a known gap.
1. Prompt injection: “pay this address now”
Section titled “1. Prompt injection: “pay this address now””A web page, a token name or a paid response tells the agent to send funds to an attacker.
- The agent calls
sendorpay_x402with the attacker’s address. The MCP worker cannot sign; it creates a request. - Text that reaches the agent from ZyroPay (token names, registry metadata, paid responses) is sanitized and wrapped in an “untrusted data, not instructions” envelope, which makes the injection harder but cannot make it impossible.
- The policy denies a recipient that is not on the allowlist (
RECIPIENT_NOT_ALLOWED), denies if spending or the protocol is off, and applies the USD limits on spent plus reserved, so a burst of requests cannot add up past them. - On Cardano, a request that passes the policy still comes to you; the review shows the real recipient and amount, and you reject it.
Stops at: step 3, or step 4. Tested offline Denial reasons and concurrent limits are tested; no live denial is recorded.
2. Recipient swap after approval
Section titled “2. Recipient swap after approval”Someone between your approval and the chain (a compromised agent path, a bug) swaps the recipient or amount.
- Your approval binds to
payloadHash, the SHA-256 of the canonical terms; changed terms produce a different hash and the approval does not apply (PAYLOAD_HASH_MISMATCH). - Your passkey’s stamp covers the exact request body; the API refuses a stamp whose bytes, or whose Cardano body hash recomputed from the stored transaction, differ.
- An independent decoder, with no network access, checks the bytes pay exactly what the payload says, and the signed transaction is checked against the reviewed bytes before broadcast.
- For x402, the MCP worker retries only the exact URL the API bound, and the transaction pays
exactly
payTothe quoted amount; a facilitator cannot redirect it.
Stops at: step 1 or 2. Tested offline Forged stamps, other bytes and other body hashes are refused and audited in tests.
3. Dishonest seller
Section titled “3. Dishonest seller”The seller takes the money and delivers something else, or nothing.
- With escrow, the money sits in the Masumi script, not with the seller.
- Tamper: the seller records the hash of its real report on chain and delivers other bytes. The
MCP worker hashes what it actually received; the API compares it with the on-chain
result_hash:mismatch. The receipt also fails (content_mismatch) or proves what was sent. - No delivery: no result reaches the chain by
submitResultTime:not_delivered. - ZyroPay recommends the refund: request it (passkey), and withdraw it once the seller authorizes it or the deadline passes. After a result, a dispute goes to the seller’s authorization or the admins.
The demo seller has a deliberately dishonest switch for exactly this, off by default
(DEMO_DISHONEST_MODE, then ?demo=tamper or ?demo=nodelivery on the escrow route; every response
says so in X-Demo-Mode).
Stops at: step 2 or 3 (detection) and 4 (money back). The honest case is live:
ee7b142e73…865361 (block 5353820, fee 0.688128 ADA) was verified. The dishonest run: Awaiting live run;
Tested offline both modes are tested on the seller and the proof states on the API.
Direct payments have no refund: the receipt tells you what you got and from whom, and you lose at most the price, which your policy bounds.
4. Compromised MCP worker
Section titled “4. Compromised MCP worker”An attacker runs code in the MCP worker.
- It has no Turnkey credential, no database URL and no user session; its only credential is the
internal service token, valid only on
/internal/*. - Every internal call names an agent; the API re-checks that the agent is active and derives the user from it, never trusting a user id from the worker.
- It can create requests for agents whose tokens it sees. They pass the policy and, on Cardano, your passkey: worst case, requests you see and reject.
- It can lie about deliveries: withhold one, or misreport the body hash and so the proof (see Who you trust).
Stops at: step 3 for funds. The proof’s integrity relies on this worker. Tested offline
5. Compromised API
Section titled “5. Compromised API”An attacker runs code in the API worker, the system of record.
- In Always Ask, the API holds no signing capability: no root credential, no delegated key.
- Turnkey requires your passkey’s stamp for every signature. The API can forward a stamp but not alter it.
- But the API writes the review your app shows, and the app does not decode the transaction on the device. A compromised API could describe a payment to the seller and ask your passkey to sign a body hash that pays someone else. This is a known gap, listed on Limits and roadmap.
- In Autonomous mode (never on Cardano), the delegated key is bounded by the Turnkey envelope your passkey created: allowlisted recipients, per-payment native caps, allowed chains and programs. A compromised API cannot exceed it, but can spend up to it, repeatedly.
Stops at: step 1 and 2 for silent theft; not stopped for a deceptive review (step 3).