58604 sc low verification bypass in verifyexecutemetatxncalldata enables arbitrary 0x actions to pass checks and execute in the zeroxswapverifier sol contract

Submitted on Nov 3rd 2025 at 14:00:30 UTC by @Kissiahmyo for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58604

  • Report Type: Smart Contract

  • Report severity: Low

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

  • Impacts:

    • Protocol insolvency

Description

Brief/Intro

The ZeroXSwapVerifier.sol library’s _verifyExecuteMetaTxnCalldata decodes both a SlippageAndActions struct and a separate bytes[] actions argument, but it verifies only the struct’s embedded actions. If the actual 0x Settler execution reads the separate actions argument (a common ABI style), a malicious caller can craft calldata where benign actions are placed in the struct while harmful actions are placed in the second parameter bytes[] actions. The verifier returns success, but settlement executes the harmful actions, bypassing whitelists, slippage limits, and recipient restrictions. This creates a practical path to reroute assets or violate risk controls in production.

Vulnerability Details

Context and goal: verifySwapCalldata() -> decodeAndVerifyActions() -> _verifyExecuteMetaTxnCalldata()

  • ZeroXSwapVerifier is designed to statically validate 0x Settler calldata for two entry points, execute(SlippageAndActions, bytes[]) and executeMetaTxn(SlippageAndActions, bytes[], address, bytes). It enforces token whitelists, slippage bounds, allowed action types, and target token constraints before execution.

  • The top-level entry verifySwapCalldata checks the function selector, then delegates to decodeAndVerifyActions which routes to _verifyExecuteCalldata or _verifyExecuteMetaTxnCalldata based on selector.

Vulnerable function and code: In src/utils/ZeroXSwapVerifier.sol, _verifyExecuteMetaTxnCalldata decodes (SlippageAndActions, bytes[], address, bytes) but disregards the bytes[] parameter and uses only saa.actions for verification:

ZeroXSwapVerifier.sol::verifyExecuteMetaTxnCalldata#L140arrow-up-right

This creates a semantic mismatch: the verifier inspects the struct’s actions, while the actual Settler contract may execute the separate bytes[] actions argument. If these two are not guaranteed identical at runtime, verification can be trivially bypassed.

Impact Details

Gate bypass of risk controls

  • Many 0x integration ABIs use a struct for metadata (recipient, buyToken, minAmountOut) and pass actions as a separate array argument that is executed. The current verifier trusts saa.actions and ignores the second actions parameter.

  • An attacker can: Construct calldata with “safe-looking” actions embedded in SlippageAndActions.actions to satisfy verification rules. Place malicious or policy-breaking actions in the second bytes[] actions parameter that the Settler will actually execute. Pass verifySwapCalldata and proceed to execution in the same transaction, achieving “verified but harmful” behavior.

Asset misdirection and loss:

  • Integrations commonly use ZeroXSwapVerifier.verifySwapCalldata(...) as a pre-execution gate before calling 0x Settler. With this bypass, “verify then execute” can lead to asset redirection, draining vault/user funds or performing out-of-policy trades.

  • Severity is high where this library gates treasury, vault strategies, or user swaps; a single transaction can pass verification and still execute malicious actions, enabling immediate theft or policy violations.

Additional notes that exacerbate risk:

  • The function includes a TODO shall we also verify saa.buyToken ?, indicating important fields like buyToken might be unverified. This weakens policy enforcement further (e.g., wrong output token).

  • The library does not assert consistency between the struct actions and the separate actions parameter, nor does it canonicalize or cross-check intended recipients against actual execution paths.

Recommendation

Validate the exact actions array that will be executed and eliminate ambiguity between duplicated parameters. Bind verification to the execution payload by enforcing equality between the struct’s saa.actions and the separate bytes[] actions argument, or remove one source entirely.

References

File: src/utils/ZeroXSwapVerifier.sol

  • _verifyExecuteMetaTxnCalldata: decodes (SlippageAndActions, bytes[], address, bytes) but verifies only saa.actions.

Entry points: verifySwapCalldata() -> decodeAndVerifyActions() -> _verifyExecuteMetaTxnCalldata()

  • verifySwapCalldata(bytes calldata, address owner, address targetToken, uint256 maxSlippageBps)

  • decodeAndVerifyActions(bytes calldata, address owner, address targetToken, uint256 maxSlippageBps)

ZeroXSwapVerifier.sol::verifyExecuteMetaTxnCalldata#L140arrow-up-right

Proof of Concept

Proof of Concept

The PoC test_poc_executeMetaTxn_verification_bypass can be added at the end of the ZeroXSwapVerifier.t.sol test file to successfully demonstrate the verification bypass vulnerability.

The PoC could be successfully added to the end of ZeroXSwapVerifier.t.sol . The test test_poc_executeMetaTxn_verification_bypass() demonstrates the verification bypass vulnerability by:

  • Creating benign actions in the SlippageAndActions struct that pass verification (correct token, acceptable slippage) Creating malicious actions in the separate bytes[] parameter that would normally fail verification (wrong token, excessive slippage, suspicious recipient)

  • Showing that verification passes because the verifier only checks saa.actions (the benign ones) while ignoring the bytes[] actions parameter that would actually be executed by the 0x Settler

  • Proving the bypass works by demonstrating that if the malicious actions were placed directly in the struct, verification would correctly fail

The test passes, confirming that the vulnerability exists and can be exploited to bypass token validation, slippage limits, and recipient restrictions. This PoC provides concrete evidence of the security flaw described in the bug report.

Was this helpful?