The ERC20Minter, NativeMinterRedeem, and BaseMinterWithdrawal contracts contain functions (deposit, redeem, and requestWithdrawal) that interact with arbitrary ERC20 tokens. These functions assume that the exact amount specified in the function call will be transferred. However, some tokens (like cUSDCv3) have non-standard transfer implementations that may not transfer the exact amount specified, leading to potential discrepancies between the amount the contract thinks it received and the amount it actually received.
This vulnerability affects multiple functions across different contracts in the system, potentially impacting the integrity of deposits, redemptions, and withdrawal requests.
Attack Scenario
An attacker could exploit this vulnerability using a token with a non-standard transfer implementation, such as cUSDCv3. A potential scenario could be:
The attacker has a small balance (e.g., 100 units) of a non-standard token (like cUSDCv3).
The attacker calls the deposit() function with an amount of type(uint256).max.
The non-standard token's transfer function interprets type(uint256).max as a signal to transfer the entire balance.
Only 100 units are actually transferred to the contract.
However, the contract proceeds to mint staking tokens based on the original type(uint256).max amount.
This results in the attacker receiving far more staking tokens than they should, based on their actual deposit.
Similar scenarios could play out with the redeem() and requestWithdrawal() functions, potentially allowing an attacker to redeem or withdraw more tokens than they should be able to.
// ERC20Minter contract
function deposit(uint256 amount, address receiver) public virtual nonReentrant {
require(amount > 0, "ZeroDeposit");
+ require(amount < type(uint256).max, "AmountTooLarge");
uint256 mintAmount = previewDeposit(amount);
require(mintAmount > 0, "ZeroMintAmount");
+ uint256 balanceBefore = baseToken.balanceOf(address(this));
baseToken.safeTransferFrom(address(msg.sender), address(this), amount);
+ uint256 actualTransferred = baseToken.balanceOf(address(this)) - balanceBefore;
+ require(actualTransferred == amount, "TransferAmountMismatch");
- stakingToken.mint(receiver, mintAmount);
+ stakingToken.mint(receiver, previewDeposit(actualTransferred));
- emit Deposit(address(msg.sender), receiver, amount);
+ emit Deposit(address(msg.sender), receiver, actualTransferred);
}
// Similar changes should be applied to the redeem() function in NativeMinterRedeem
// and the requestWithdrawal() function in BaseMinterWithdrawal
These revisions add checks to ensure the actual transferred amount matches the specified amount, and use the actual transferred amount for subsequent operations. This protects against discrepancies that could arise from non-standard token implementations.
The fix addresses the vulnerability by:
Adding an upper bound check to prevent potential overflow issues with extremely large inputs.
Verifying the actual transferred amount against the expected amount.
Using the actual transferred amount for minting, burning, or recording withdrawal requests, and in event emissions.
Github username: -- Twitter username: -- Submission hash (on-chain): 0x1c59c2346e8d70837bd9c3a0ae00359496f372f23e0c779095011d6aabff5766 Severity: medium
Description: Description
The ERC20Minter, NativeMinterRedeem, and BaseMinterWithdrawal contracts contain functions (deposit, redeem, and requestWithdrawal) that interact with arbitrary ERC20 tokens. These functions assume that the exact amount specified in the function call will be transferred. However, some tokens (like cUSDCv3) have non-standard transfer implementations that may not transfer the exact amount specified, leading to potential discrepancies between the amount the contract thinks it received and the amount it actually received.
This vulnerability affects multiple functions across different contracts in the system, potentially impacting the integrity of deposits, redemptions, and withdrawal requests.
Attack Scenario
An attacker could exploit this vulnerability using a token with a non-standard transfer implementation, such as cUSDCv3. A potential scenario could be:
Similar scenarios could play out with the redeem() and requestWithdrawal() functions, potentially allowing an attacker to redeem or withdraw more tokens than they should be able to.
Attachments
Revised Code Suggestion
These revisions add checks to ensure the actual transferred amount matches the specified amount, and use the actual transferred amount for subsequent operations. This protects against discrepancies that could arise from non-standard token implementations.
The fix addresses the vulnerability by: