NOTE: This issue is different from the issue titled "Signature Theft Front Run Can Result In Collateral Takeover" seen in the HYDN audit which is already fixed by the addition of this check. Issues (like the one i describe here) with the permit implementation still persist.
the V3Vault.createWithPermit() function is meant to allow creation of a new collateralized position with a permit for token spending (transfer position with permit).
The issue with this implementation is that it does another action right after the permit call. Since nonfungiblePositionManager.permit() is callable by anyone, it is possibe to DOS the legit user calling createWithPermit() by calling nonfungiblePositionManager.permit() first, with the exact same params and user's signature. This will cause the user permit call to fail and essentially the whole tx to fail. So essentially the createWithPermit() is frontran with a call to nonfungiblePositionManager.permit(). The user's params can be read/derived from the mempool by the attacker.
Permit issues of these type has recently been well documented by Trust Security as seen in their blogpost titled Permission denied - The story of an EIP that sinned. The blogpost shows a wide range of confirmed reports of this or similar vulns.
This issue also occurs in executeWithPermit().v3Utils.sol. executeWithPermit() can be made to fail by front running the call with a permit() call.
Proof Of Concept
create file in src/tests and paste the code below. run with forge test --mt testFrontRunPermit
Makes user's signature to become invalid and cause the user's call to vault.createWithPermit() to revert.
Recommended Mitigation Steps
modify code in createWithPermit(), if permit() call fails, it means that there is already a permit approval. so that error can be caught and the token can then be transferred ie
function createWithPermit(
uint256 tokenId,
address owner,
address recipient,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external override {
if (msg.sender != owner) {
revert Unauthorized();
}
try
nonfungiblePositionManager.permit(
address(this),
tokenId,
deadline,
v,
r,
s
)
{} catch {
// IF PERMIT FAILS, MAYBE THERE IS AN APPROVAL BUT CHECK TO CONFIRM
if (
nonfungiblePositionManager.getApproved(tokenId) != address(this)
) {
revert("VAULT NOT APPROVED TO TAKE UNIV3 POSITION");
}
}
nonfungiblePositionManager.safeTransferFrom(
owner,
address(this),
tokenId,
abi.encode(recipient)
);
}
This same type of implementation can be done for executeWithPermit().v3Utils.sol too.
Lines of code
https://github.com/code-423n4/2024-03-revert-lend/blob/435b054f9ad2404173f36f0f74a5096c894b12b7/src/V3Vault.sol#L410-L425
Vulnerability details
Bug Description
NOTE: This issue is different from the issue titled "Signature Theft Front Run Can Result In Collateral Takeover" seen in the HYDN audit which is already fixed by the addition of this check. Issues (like the one i describe here) with the permit implementation still persist.
the
V3Vault.createWithPermit()
function is meant to allow creation of a new collateralized position with a permit for token spending (transfer position with permit).The issue with this implementation is that it does another action right after the permit call. Since nonfungiblePositionManager.permit() is callable by anyone, it is possibe to DOS the legit user calling
createWithPermit()
by callingnonfungiblePositionManager.permit()
first, with the exact same params and user's signature. This will cause the user permit call to fail and essentially the whole tx to fail. So essentially thecreateWithPermit()
is frontran with a call tononfungiblePositionManager.permit()
. The user's params can be read/derived from the mempool by the attacker.Permit issues of these type has recently been well documented by Trust Security as seen in their blogpost titled Permission denied - The story of an EIP that sinned. The blogpost shows a wide range of confirmed reports of this or similar vulns.
This issue also occurs in executeWithPermit().v3Utils.sol. executeWithPermit() can be made to fail by front running the call with a permit() call.
Proof Of Concept
src/tests
and paste the code below. run withforge test --mt testFrontRunPermit
Impact
Makes user's signature to become invalid and cause the user's call to vault.createWithPermit() to revert.
Recommended Mitigation Steps
modify code in
createWithPermit()
, ifpermit()
call fails, it means that there is already a permit approval. so that error can be caught and the token can then be transferred ieThis same type of implementation can be done for executeWithPermit().v3Utils.sol too.
Assessed type
Context