The leverageUp and leverageDown functions in LeverageTransformer contract are expected to be called by V3Vault. However, there is no access control to check that the caller is an authorized V3Vault address.
Due to this, it is possible for any user owned contract to steal position tokens on tokenIds that are already pre-approved for isAuthorizedForToken calls in nonfungiblePositionManager contract. Additionally, the user-owned contract can steal tokens held in the LeverageTransformer using the routed swap calls in leverageUp and leverageDown functions
Assume LeverageTransformer is already authorized to call nonfungiblePositionManager.decreaseLiquidity(), it receives the position tokens from the liquidity decrement and swaps
Lines of code
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/transformers/LeverageTransformer.sol#L43 https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/transformers/LeverageTransformer.sol#L124
Vulnerability details
Impact
The
leverageUp
andleverageDown
functions in LeverageTransformer contract are expected to be called by V3Vault. However, there is no access control to check that the caller is an authorized V3Vault address.Due to this, it is possible for any user owned contract to steal position tokens on tokenIds that are already pre-approved for
isAuthorizedForToken
calls in nonfungiblePositionManager contract. Additionally, the user-owned contract can steal tokens held in the LeverageTransformer using the routed swap calls inleverageUp
andleverageDown
functionsProof of Concept
In https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/transformers/LeverageTransformer.sol#L123
Assume LeverageTransformer is already authorized to call
nonfungiblePositionManager.decreaseLiquidity()
, it receives the position tokens from the liquidity decrement and swapsSafeERC20.safeApprove(IERC20(token), msg.sender, amount); IVault(msg.sender).repay(params.tokenId, amount, false);