Uniswap v4 Hook Attack Vector Analysis and Detection Methods

Of the 84,000 Uniswap v4 Hooks analyzed by 0x Protocol, only 19.4% were secure, while 54.2% were deemed malicious. This article dissects attack vectors and detection methods for hidden charges, reentrancy, and oracle manipulation.

The recent Robinhood Chain hype has attracted a large number of users to conduct Uniswap transactions on its chain. When a user found a Uniswap v4 pool displaying a 0% fee and an offer price several basis points lower than the best market price, they selected it as the optimal route for the transaction. However, after the transaction, the user actually received nearly 1% fewer tokens than the quoted price. Checking the on-chain data revealed that this loss was actually due to a hidden "tax" within the Hook contract. The Hook, through the afterSwapReturnsDelta permission, withheld a portion of the tokens before transaction settlement.

In September, liquidity aggregation infrastructure 0x Protocol conducted static analysis, dynamic analysis, and actual transaction observation on 84,163 Uniswap v4 hooks across 6 chains. The analysis revealed that only 19.4% of the hooks were deemed safe, 54.2% were classified as malicious, and another 26.4% exhibited suspected malicious behavior. This article from the Beosin security team will analyze the attack vectors of Uniswap v4 hooks to help users and developers understand the potential malicious behaviors.

I. Uniswap Hook Mechanism

In Uniswap v4, liquidity pools are managed centrally by the PoolManager. Hook contracts can insert custom logic at key nodes such as pool initialization, adding liquidity, removing liquidity, Swap, and Donate, including modifying transaction fees, pre- and post-trade balances, and settlement logic. Its security risks are mainly concentrated in the following areas:

  • Hook returns abnormal balance changes
  • Hook calls external contracts in callbacks
  • Hook reads the instantaneous price and determines the rate accordingly.
  • Hooks save state across pools and users.
  • Hooks can create denial-of-service attacks by exploiting callback order and transaction rollback mechanisms.

Attackers might first create a seemingly legitimate liquidity pool, then insert hidden fees and blacklisting logic into the hook. They can then attract users through frontends, airdrops, aggregators, or social media. After users trade or provide liquidity, attackers can maliciously acquire other users' assets through custom logic and external calls. According to Beosin's analysis, common malicious hook forms include:

  • A small additional fee is charged for each transaction.
  • Different rates are charged for specific addresses.
  • The behavior suddenly changes when the price approaches a certain threshold.
  • Prevent liquidity providers from removing liquidity
  • The callback consumes a large amount of gas, causing the exchange transaction to fail.
  • Inducing users to call fake unlock, settle, or token transfer functions.

II. Analysis of Malicious Hook Examples

1. Hidden transaction fees

The hook below demonstrates the logic of "charging additional fees for a specified address": the developer waives fees for themselves, market makers, or specified bots through privilegedUsers, while other users are required to pay additional transaction fees.

contract SuspiciousFeeHook { address public immutable manager; address public immutable feeRecipient; mapping(address => bool) public privilegedUsers; constructor(address _manager, address _feeRecipient) {
manager = _manager; feeRecipient = _feeRecipient;
}
function beforeSwap(
address sender,
uint256 amountIn ) external returns (uint256 adjustedAmountIn) { require(msg.sender == manager, "only manager"); if (privilegedUsers[sender]) {
return amountIn;
} uint256 hiddenFee = amountIn * 30 / 10_000; adjustedAmountIn = amountIn - hiddenFee; // demo, in reality, Hook should not bypass the settlement rules of the PoolManager IERC20(token()).transferFrom(
sender
feeRecipient,
hiddenFee
);
} function token() internal view returns (address) {
return address(0);
}
}

In a real v4 architecture, a Hook typically cannot arbitrarily bypass the PoolManager's settlement rules, but it can influence the final settlement through custom delta, external token calls, or pool configuration. Therefore, the audit focus should be on examining all balance change paths of the Hook.

2. Dynamic pricing or rates

contract AddressBasedHook { mapping(address => bool) public blocked; mapping(address => uint24) public customFee; function beforeSwap(
address sender, uint256 amountSpecified ) external returns (uint24 fee) { if (blocked[sender]) { revert("blocked trader");
} uint24 userFee = customFee[sender]; if (userFee != 0) {
return userFee;
} return 10_000; // default 100%
}
}

The code above includes a blacklist mechanism and sets different rates based on the user's address. If the rates are not fully displayed on the front end, the pool the user sees may not match the actual transaction conditions. During auditing or development, it is essential to ensure that all rate return values comply with the protocol's allowed range, rather than simply checking regular paths.

3. Reentrancy and external callbacks

contract ReentrantHook { address public callbackTarget;
bool private entered; function beforeSwap(address sender) external { require(!entered, "reentered");
entered = true) // External call, may call back the current Hook or other protocols ICallback(callbackTarget).onSwap(sender); entered = false;
}
}

External calls within a Hook may trigger:

  • ERC-777 or a custom ERC-20 callback;
  • ERC-721/ERC-1155 receive callback;
  • Loan agreement callback;
  • Aggregator callback;
  • The current Hook is called again;
  • Swap from other pools.

If state updates and external calls are executed in the wrong order, issues such as duplicate calculations, bypassing single-transaction limits, duplicate fee collection, cross-pool state contamination, and price or quota checks may occur. It's important to note that while the v4 core contract has its own unlocking and settlement constraints, this does not mean that Hooks can safely make arbitrary external calls. Security boundaries must be established individually for each Hook.

4. Manipulating oracles using instantaneous prices

contract FragileOracleHook { uint256 public lastObservedPrice; function beforeSwap( uint160 currentSqrtPriceX96
external { uint256 spotPrice = priceFromSqrtPrice(currentSqrtPriceX96); // Incorrect approach: Directly using the current pool's instantaneous price if (spotPrice > 1_100e18) { triggerLiquidation();
} lastObservedPrice = spotPrice;
} function triggerLiquidation() internal {
// Teaching slots: Settle or distribute rewards based on price.
}

An attacker could acquire a large amount of assets through a flash loan in the same transaction; then perform a large swap on the target pool to push the instantaneous price to extremes, thereby triggering the Hook's liquidation, reward, or collateral valuation logic. If the Hook treats the current pool price as a trusted oracle, the attacker could potentially complete the manipulation within a single block.

5. Prevent liquidity withdrawal

contract ExitBlockingHook { mapping(address => bool) public blockedLP; function beforeRemoveLiquidity(address provider) external { if (blockedLP[provider]) { revert("withdraw disabled");
}
} function setExitLock(address user, bool locked) external onlyOwner {
blockedLP[user] = locked;
}
}

These types of hooks allow users to add liquidity but not remove it. Common triggering conditions include:

  • User address has been added to blacklist
  • The pool reaches a certain TVL
  • After a certain timestamp
  • Hook the administrator to call the hidden switch
  • The user had previously traded through an aggregator.
  • The user did not pay any additional fees.

6. Gas Griefing

This type of attack is actually very common in previous contract attacks, causing transactions to fail by consuming abnormal gas.

contract GasGriefHook { function beforeSwap(uint256 n) external { for (uint256 i = 0; i < n; i++) { keccak256(abi.encodePacked(i, msg.sender));
}
}
}

More covert forms include:

  • Iterate through the ever-growing array of addresses;
  • Clear historical user status;
  • Check the balance of each of the numerous pools one by one;
  • Perform a large amount of computation before failure;
  • Invoking multiple malicious external contracts.

III. Methods for Detecting Malicious Hooks

1. Check Hook address permission bits: Confirm which callback permissions are declared in the Hook address and compare them with the functions in the actual bytecode. If the address declares beforeSwap, afterSwap, beforeAddLiquidity, or beforeRemoveLiquidity, then the corresponding functions need to be analyzed one by one.

2. Obtain the code: For example, you can use a block explorer or RPC to call eth_getCode(hookAddress) to check the following key points: whether it is a proxy contract, whether there is an upgrade entry, whether there is self-destruct logic, whether it contains unknown external calls, and whether it is consistent with the verified source code.

3. Check the liquidity pool initialization transaction: Pay special attention to PoolKey, currency0/currency1, fee, tick spacing, Hook address, initialization address, administrator, and fund receiving address.

4. Simulate boundary scenarios: It is recommended to simulate at least the following scenarios: normal swap, very small amount swap, large amount swap, zero amount swap, extreme price changes, duplicate swap, liquidity added and immediately removed, Hook administrator change, token transfer failure, external call callback, etc. Compare the actual amount of tokens paid by the user with the actual amount of tokens received by the user, Hook balance changes, PoolManager delta, event logs and gas consumption.

Conclusion

Uniswap v4 hooks are designed to make Uniswap more scalable, but they also introduce new attack surfaces and the possibility of malicious exploitation. For ordinary users, the most important criteria for judging hook security are "whether the hook is verified, whether the source code is publicly available, and whether permissions are restricted." For the entire DeFi ecosystem, DeFi protocols and security companies need to take more measures, such as detection tools and audits of liquidity pools, to improve the security and reliability of on-chain transactions.

Source:Global Cybersecurity Alliance (GCSA)
Website:www.gcsa.org