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 V3arrow-up-right

  • 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?