Critical x402 SDK Flaw Could Let Malicious APIs Drain AI Agents’ Token Balances

critical-x402-sdk-flaw-could-let-malicious-apis-drain-ai-agents’-token-balances
Critical x402 SDK Flaw Could Let Malicious APIs Drain AI Agents’ Token Balances

AI agents paying for API calls with the x402 SDKs could sign a payment for their full balance of a supported token if a malicious API asked for it, security firm V12 said on October 22.

The cause was the SDKs’ default client, which set no spending limit until releases on August 18 added one. The risk was limited to agents who automatically signed payments and whose apps set no limits.

The missing limit is one of 25 flaws V12 found in the x402 code, and one of three it rated critical and described in detail. The x402 Foundation, which maintains the SDKs, had already fixed all three critical flaws in the August 18 releases.

One of the other two let a facilitator, the service that checks x402 payments, accept a Solana transaction that moved no tokens as a valid payment. Among the x402 SDKs, only the TypeScript one had this flaw. The other let a malicious server get an agent to sign a login message for a different website.

V12 found the flaws by reviewing the code and running local tests. Neither V12 nor the x402 Foundation has publicly reported any attacks using them.

The foundation has published no security advisory for the three flaws, and its changelogs list the fixes without calling them security fixes. As of October 23, npm audit returned no advisories for the last TypeScript releases before the fixes, and PyPI listed no vulnerabilities for the last Python release before them, The Hacker News found. A developer who relies on those checks gets no warning to upgrade.

Coinbase created x402 and handed it to the x402 Foundation this year, a Linux Foundation project whose members include Visa and Mastercard. Coinbase holds the most senior seat on the foundation’s technical steering committee.

No Spending Limit in the Default Client

x402 is an open standard that lets software, including AI agents, pay for an API call within the web request itself, without requiring a person to approve each payment.

A server that wants payment answers with the HTTP status code 402 Payment Required and lists its payment options, each with an amount, a token, and a recipient. The client picks one, signs a payment, and sends the request again.

The default client took the first option on the list, and before the fix, it set no limit on the amount.

A hostile API could use that to turn a routine data request into a request for the wallet’s full balance of a supported token, V12 said. An earlier small payment could reveal the wallet address, so the server could look up the balance first.

With an automatic signer and no payment policy set by the app, the client could sign a payment for that amount. For a token that supports signed transfers, the attacker could then submit the signed payment and collect the funds.

The August 18 releases added spend controls to the TypeScript, Python, and Go clients. By default, they now cap each payment at $1 and allow only US dollar stablecoins the SDK recognizes, such as USDC.

Token Approvals Accepted as Solana Payments

In x402, a merchant’s server relies on a facilitator to check each payment and submit it to the blockchain. On Solana, the facilitator also pays the transaction fee.

The TypeScript facilitator code for Solana checked that a payment called an allowed token program, then read it as a transfer without checking the instruction type. A token approval, which lets another account spend tokens later but moves none itself, differs from a transfer only in its first byte.

In V12’s local test, the facilitator accepted such an approval as a valid payment. The test did not send a live transaction.

A merchant relying on the facilitator could therefore deliver a paid resource while receiving no tokens, V12 said, calling it “a complete payment bypass.” The attacker needed a token account, but no balance, and the facilitator paid the transaction fee each time.

The Python and Go SDKs already checked the instruction type. The August 18 releases added the same check to the TypeScript code.

Every published @x402/svm release from 2.0.0 to 2.22.0 lacks the check, and every release from 2.23.0 on has it, The Hacker News found by searching the published packages.

Logins Signed for Other Websites

x402 also has a sign-in extension called Sign-In-With-X (SIWx). It lets an agent log in by signing a short message with its wallet, for example to prove an earlier payment instead of paying again. On Ethereum, the message names the website, the wallet address, and a one-time code.

The automated client signed these messages without verifying that the website named in them was the one it had contacted.

A malicious server could get a fresh login message from another site, pass it to the agent, and use the signature to log in to that site as the agent’s wallet, V12 said. That would work wherever the other site accepted the relayed message, and the attacker would get whatever access the login allowed.

The August 18 releases make the TypeScript, Python and Go clients refuse to sign unless the site in the message matches the site that sent the 402 response. The project also changed the SIWx specification, which now says a client must refuse to sign when the two do not match.

TypeScript code that calls createSIWxPayload directly must now pass the URL of the 402 response, after any redirects. The @x402/fetch and @x402/axios wrappers pass it on their own.

What to Update

Upgrading to the latest releases also brings later fixes, including one for a separate payment bypass in @x402/core 2.27.0 in September. As of October 23, the latest versions are 2.28.0 for @x402/core, @x402/svm, @x402/extensions, @x402/fetch and @x402/axios, v2.28.0 for the Go module and 2.25.0 for the Python package.

In TypeScript, clients get the spending fix by updating @x402/fetch or @x402/axios to 2.23.0 or later, which pulls in the matching @x402/core. Apps that use SIWx also need @x402/extensions 2.23.0 or later, which holds the login check. Facilitators that check Solana payments need @x402/svm 2.23.0 or later.

After the upgrade, existing clients are capped at $1 per payment and limited to recognized stablecoins unless the app sets its own spend controls. Apps that pass spendControls: false turn all of these controls off.

Teams that cannot upgrade yet can register a payment policy that drops any option above a set amount. The older clients support this with registerPolicy in TypeScript and Go and register_policy in Python, which also has a ready-made max_amount policy.

V12 advises clients to check that a SIWx login message identifies the site they contacted before signing, to set budgets for each payment and in total, and to limit which tokens, networks, and recipients they will pay.

Found this article interesting? Follow us on Google News, Twitter and LinkedIn to read more exclusive content we post.

Leave a Reply

Your email address will not be published. Required fields are marked *