LiquidityManagement.katanaV3MintCallback() is subject to address collision attack, allowing an attacker to steal funds from users who give approvals to the NonfungiblePositionManager. #32
Labels
bug
Something isn't working
downgraded by judge
Judge downgraded the risk level of this issue
grade-c
primary issue
Highest quality submission among a set of duplicates
QA (Quality Assurance)
Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
🤖_primary
AI based primary recommendation
🤖_37_group
AI based duplicate group recommendation
sponsor disputed
Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue
sufficient quality report
This report is of sufficient quality
unsatisfactory
does not satisfy C4 submission criteria; not eligible for awards
Lines of code
katana-v3-contracts/src/periphery/NonfungiblePositionManager.sol#L25-L31
Vulnerability details
Proof of Concept
The
LiquidityManagement.katanaV3MintCallback()
function is expected to be called only by a valid pool. However, if an attacker can craft an address (let's call it B) using the create2 opcode, which passes as a pool address, the attacker can exploit this to steal user approvals and funds. Many users, for convenience, grant maximum approvals to theNonfungiblePositionManager
, which leaves them vulnerable. The attacker can use their controlled contract at address B to steal funds by callingLiquidityManagement.katanaV3MintCallback()
as follows:amount0Owed
,amount1Owed
and make suredata.payer
is the address of the user we like to steal (we can check the transaction history to identify such victims who gave approvals to theNonfungiblePositionManager
).In the following we show how such B can be found. First, let's see how the pool verification works.
The verification does not check whether the caller (
msg.sender
) is a pool deployed by the factory or not. It only checks that caller address matches an address that can be computed bycomputeAddress()
wherekey.fee
can be searched in a brute-force fashion as we show below. If the attacker can find akey.fee
such that the computed address happens to be controlled by the attacker, bingo, that address can be used to steal funds.Note that as the computation of the Create2 address is done by truncating a 256 bit keccak256 hash to 160 bits, meaning 2^96 possible hashes will be mapped to the same address!
We need to find a collision between two lists:
computeAddress
will produce a list of addresses.salt
with create2 will produce another list of addresses.Once we find a collision B between these two lists, the attacker can deploy a contract to address B, which passes the pool verification, and then proceed to exploit approvals and steal funds as described above.
Several similar collision attacks have been reported and rewarded:
Arabadzhiev - The pool verification in
NapierRouter
is prone to collision attacks sherlock-audit/2024-01-napier-judging#111[https://github.com/PUSH0 -
CREATE2
address collision against an Account will allow complete draining of lending pools sherlock-audit/2023-12-arcadia-judging#59]EIP-3607, which rationale is this exact attack. The EIP is in final state.
The feasibility and cost of the attack has been discussed in above reports.
Recommended Mitigation Steps
The factory should keep track of all the pools that it has deployed, the callback function can call factory to check whether ```msg.sender`` is one of those pools.
Assessed type
Invalid Validation
The text was updated successfully, but these errors were encountered: