58079 sc low missing from validation in zeroxswapverifier verifyswapcalldata enables direct theft of approved funds

Submitted on Oct 30th 2025 at 13:33:25 UTC by @Novathemachine for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58079

  • 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 approves malicious 0x routes because _verifyTransferFrom() never checks that the from address equals the owner. Once verified, a route can call transferFrom(owner, attacker, amount) and drain the owner’s approved tokens.

Vulnerability Details

  • Where: src/utils/ZeroXSwapVerifier.sol , _verifyTransferFrom(bytes action, address owner, address targetToken, uint256 targetAmount).

  • What happens: The function decodes (token, from, to, amount) but only enforces token == targetToken. It ignores from and to.

    (address token, , , uint256 amount) = abi.decode(
        _slice(action, 4),
        (address, address, address, uint256)
    );
    require(token == targetToken, "IT");
  • Why it’s bad: Any transferFrom embedded in a 0x route that pulls from the victim will pass verification as long as the token matches. With an existing allowance to the 0x spender, funds move directly to an attacker-controlled address.

Impact Details

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

  • Any user who approved the 0x spender can lose their entire approved balance in a single verified route. No market or timing constraints limit the loss.

References

  • Vulnerable code: src/utils/ZeroXSwapVerifier.sol (_verifyTransferFrom()).

Proof of Concept

Proof of Concept

  1. Add this test to src/test/ZeroXSwapVerifier.t.sol:

  2. Run the test:

  3. Expected result:

    • verifySwapCalldata() returns true, proving the malicious route is accepted.

    • transferFrom(owner, attacker, amountToSteal) succeeds, and balances reflect the theft.

Was this helpful?