For the complete documentation index, see llms.txt. This page is also available as Markdown.

57697 sc low missing recipient from checks in zeroxswapverifier enable direct asset theft

Submitted on Oct 28th 2025 at 09:02:22 UTC by @yesofcourse for Audit Comp | Alchemix V3

  • Report ID: #57697

  • Report Type: Smart Contract

  • Report severity: Low

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/utils/ZeroXSwapVerifier.sol

  • Impacts:

    • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

Brief/Intro

ZeroXSwapVerifier - the library intended to pre-verify 0x swap calldata—omits critical checks on the recipient and the from address and uses spoofable parsing for VIP routes.

As a result, attacker-crafted calldata can pass verification while (a) pulling tokens from the owner/strategy and (b) sending swap proceeds to the attacker.

If integrated in production (as intended for future strategies), this becomes a straight path to direct theft of user/strategy funds.

Vulnerability Details

The verifier is meant to block unsafe 0x calldata before execution. However:

  • Recipient and buyToken not enforced in the top-level verifier:

    There is no assertion that saa.recipient == owner (or whitelisted) and no binding that saa.buyToken equals the expected asset.

  • TRANSFER_FROM ignores the true seller (from):

    Any calldata that pulls funds from any address passes, provided the token matches targetToken. This defeats the point of verifying ownership of funds being spent.

  • VIP route token parsing is spoofable:

    For Uniswap V3 (and similar “VIP” paths), the verifier accepts the first 32 bytes of fills as the token-easy to forge so the verifier “sees” the expected token while the real route pays out elsewhere.

A typical integration would do:

  1. require(ZeroXSwapVerifier.verifySwapCalldata(...));

  2. Call the 0x executor/settler with the same calldata (and the strategy has allowances set).

An attacker builds calldata that:

  • Sets recipient = attacker (unchecked).

  • Includes a TRANSFER_FROM that pulls the strategy’s tokens (unchecked from).

  • Uses VIP fills that decode to the expected token in the verifier but route value to the attacker in execution.

The PoC in the next section tests and proves:

  • test_Verifier_Ignores_From_In_TransferFrom – Verifier passes a payload that pulls from a non-owner.

  • test_Verifier_Allows_Attacker_Recipient_On_VIP – Verifier passes a VIP payload with recipient = attacker and spoofed buyToken.

This demonstrates the verification bypass decisively.

Even though there are currently no strategies utilizing ZeroXSwapVerifier, the team explicitly stated that it is in scope:

The intent is the ZeroXSwapVerifier is in scope as it is intended to be used in future strategies. We will validate reports that do not involve an impossible/OOS scenario and find logical errors in the contract. A PoC should be submitted using the contract entry points, demonstrating errors in the contracts flows.

  • This report demonstrates logical errors in the library via its public entry points (verification functions), with a PoC that shows malicious calldata is accepted.

  • Given the stated intent to integrate this verifier in future strategies, the flaw translates directly into critical, one-call drains once adopted, and should be treated as Critical severity now to avoid future incidents.

Impact Details

  • Direct asset loss. Given standard allowances, an attacker can get “verified” calldata executed that:

    • Pulls tokenIn from the owner/strategy (unchecked from).

    • Pays tokenOut to the attacker (unchecked recipient/buyToken).

  • Any vault/strategy/module that trusts this verifier before forwarding calldata to a 0x executor is exposed. Loss scales with approved balances/limits.

References

  • Affected library: src/utils/ZeroXSwapVerifier.sol

    • _verifyExecuteCalldata – no recipient / buyToken enforcement. https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/utils/ZeroXSwapVerifier.sol#L125-L130

    • _verifyTransferFrom – ignores from address. https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/utils/ZeroXSwapVerifier.sol#L238-L246

    • VIP helpers (e.g., _extractTokenFromUniswapFills) – spoofable decoding. https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/utils/ZeroXSwapVerifier.sol#L281-L287

Proof of Concept

Proof of Concept

Paste the following file in src/test/ZeroXSwapVerifier_PoC.t.sol and run with forge test --match-contract ZeroXSwapVerifier_PoC -vv:

Result:

Was this helpful?