58666 sc low recipient owner not enforced in action verifiers enables theft of swap proceeds
Submitted on Nov 3rd 2025 at 23:02:56 UTC by @Johnyfwesh for Audit Comp | Alchemix V3
Report ID: #58666
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
Finding Description and Impact
ZeroXSwapVerifier.decodeAndVerifyActions() never validates the SlippageAndActions.recipient or the propagated owner field, so downstream action verifiers accept calldata that forwards swap proceeds to arbitrary recipients. In both verifier entry points, _verifyExecuteCalldata and _verifyExecuteMetaTxnCalldata, the struct is decoded and the code immediately relays saa.actions to _verifyActions without checking saa.recipient (src/utils/ZeroXSwapVerifier.sol:125-143). Inside the action dispatch, neither _verifyBasicSellToPool, _verifyUniswapV3VIP, nor _verifyTransferFrom inspect the decoded recipient; _verifyTransferFrom even ignores the from/to parameters entirely (src/utils/ZeroXSwapVerifier.sol:163-246).
Integrations such as the allocator pipeline inherit the verifier’s result as a hard precondition before calling the 0x settler (src/AlchemistAllocator.sol). Because the library returns true for any calldata that satisfies token and slippage checks, an attacker can craft a bundle where SlippageAndActions.recipient (or a per-action recipient) is the attacker’s address, while keeping the sell token and BPS within bounds. The malicious calldata passes verifySwapCalldata, allowing the privileged caller to forward the payload to the 0x settler, which then executes transfers that siphon proceeds to the attacker instead of the intended owner.
Affected code
Impact
Swap proceeds can be rerouted
An attacker supplies quotes where
SlippageAndActions.recipient(or the Uniswap VIP recipient) is the attacker.verifySwapCalldatareturnstrue, so orchestrators trust the calldata and forward it to the 0x settler.During settlement, proceeds are transferred to the attacker’s address, resulting in direct fund theft with no on-chain validation preventing it.
Owner/recipient spoofing breaks accounting
Integrators that assume the verifier enforces the owner or recipient can account rewards or balances to the wrong party.
Because
_verifyTransferFromignores bothfromandto, an attacker can pull assets from any approved address whileverifySwapCalldatastill succeeds, bypassing intended recipient controls.
Recommended mitigation steps
Explicitly validate
saa.recipientagainst the expectedowner(or a caller-provided allowlist) before delegating to_verifyActions.Within each action verifier, enforce that decoded recipient/output addresses match
saa.recipient(or the caller’s supplied target) and thatowneris respected for source addresses.Reject any action that omits or mismatches the enforced addresses, and extend tests to cover each action type to prevent regressions.
Proof of Concept
PoC Test testRecipientCanStealSwapProceeds That will Run in (src/test/Poc.t.sol)
testRecipientCanStealSwapProceeds That will Run in (src/test/Poc.t.sol)Exploit Sequence
Deploy a victim executor that calls
ZeroXSwapVerifier.verifySwapCalldatabefore delegating swaps to a 0x settler.Mint vault tokens, transfer them to the executor, and craft swap calldata whose
SlippageAndActions.recipientis the attacker.The verifier returns
truebecause slippage and token filters pass, so the executor approves the settler and forwards the call.The mock settler executes
transferFromusing the malicious recipient, draining the vault while the legitimate owner receives nothing.
The test demonstrates the theft and logs the balance changes: Add this test file in the src/test folder
Result (src/test/Poc.t.sol:101):
Was this helpful?