Tiny Licorice Loris
Medium
Malicious actors can always DOS WrappedTokenGatewayV3.withdrawETHWithPermit()
by frontrunning and calling permit()
aWETH.permit()
call within WrappedTokenGatewayV3.withdrawETHWithPermit()
will DOS the tx if it is frontrun with direct permit().
https://github.com/sherlock-audit/2025-01-aave-v3-3/blob/main/aave-v3-origin/src/contracts/helpers/WrappedTokenGatewayV3.sol#L140
function withdrawETHWithPermit(
address,
uint256 amount,
address to,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) external override {
IAToken aWETH = IAToken(POOL.getReserveAToken(address(WETH)));
uint256 userBalance = aWETH.balanceOf(msg.sender);
uint256 amountToWithdraw = amount;
// if amount is equal to type(uint256).max, the user wants to redeem everything
if (amount == type(uint256).max) {
amountToWithdraw = userBalance;
}
// permit `amount` rather than `amountToWithdraw` to make it easier for front-ends and integrators
aWETH.permit(msg.sender, address(this), amount, deadline, permitV, permitR, permitS);//@audit-issue DOS by frontrunning with direct permit() https://www.trust-security.xyz/post/permission-denied
aWETH.transferFrom(msg.sender, address(this), amountToWithdraw);
POOL.withdraw(address(WETH), amountToWithdraw, address(this));
WETH.withdraw(amountToWithdraw);
_safeTransferETH(to, amountToWithdraw);
}
aWETH.permit()
call is supposed to be used via try-catch so that even if WrappedTokenGatewayV3.withdrawETHWithPermit()
is frontrun by an attacker by directly calling permit() with the ERC712 permit sig there won't be a revert but it is not.
The root cause of this DOS is that the aWETH.permit()
isn't used in a try-catch. So attackers can always cause reverts by calling permit() directly. using up the nonce
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external override {
require(owner != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
//solium-disable-next-line
require(block.timestamp <= deadline, Errors.INVALID_EXPIRATION);
uint256 currentValidNonce = _nonces[owner];
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
)
);
require(owner == ecrecover(digest, v, r, s), Errors.INVALID_SIGNATURE);
_nonces[owner] = currentValidNonce + 1;
_approve(owner, spender, value);
}
Extra context: https://www.trust-security.xyz/post/permission-denied
No response
Tx needs to happen in a chain where frontrunning is possible. e.g ETH, Avalanche e.t.c
- User calls
WrappedTokenGatewayV3.withdrawETHWithPermit()
- Attacker front-runs the tx, calling permit() directly with the ERC712 permit sig of the user. This will use up user's nonce.
nonces[owner] = currentValidNonce + 1;
- User's nonce is used up, causing reverts in his transaction.
User's can always be prevented from withdrawing their ETH via WrappedTokenGatewayV3.withdrawETHWithPermit()
by simply frontrunning their tx and calling permit() directly with their ERC712 permit sig.
This can be done all the time indefinitely at no cost...All it cost is gas fees
No response
Wrap the aWETH.permit()
call within `WrappedTokenGatewayV3.withdrawETHWithPermit() in a try-catch