function validateVariablePoolHasEnoughLiquidity(State storage state, uint256 amount) public view {
uint256 liquidity = state.data.underlyingBorrowToken.balanceOf(address(state.data.variablePool));
if (liquidity < amount) {
revert Errors.NOT_ENOUGH_BORROW_ATOKEN_LIQUIDITY(liquidity, amount);
}
}
The validateVariablePoolHasEnoughLiquidity() function in the CapsLibrary contract is used by buyCreditMarket(), sellCreditMarket(), and liquidateWithReplacement() to verify that there is sufficient liquidity in AAVE for cash withdrawal.
However, the function incorrectly reads the USDC balance from the variablePool as liquidity. The actual liquidity is stored in the aUSDC token. This can be confirmed by checking the USDC balance of the following addresses on Ethereum at block height 20204585:
Consequently, after the mainnet deployment of the Size codebase, validateVariablePoolHasEnoughLiquidity() will use 0 as liquidity and thus revert. This would cause buyCreditMarket() and sellCreditMarket(), the two main functions, to enter a denial-of-service (DoS) state.
Simply put, the Size protocol will be completely non-functional due to this issue.
This PoC use a fork test to show that the incorrect liquidity reading in validateVariablePoolHasEnoughLiquidity() will cause a DoS on the buyCreditMarket() function.
To run the this PoC, first update the forge-std library to at least version 1.8.0 (The USDC deal issue is fixed in this version). Then set the RPC for Ethereum mainnet in foundry.toml. Finally, run forge test --mc PoC -vvvv. The result is as follows:
Lines of code
https://github.com/code-423n4/2024-06-size/blob/8850e25fb088898e9cf86f9be1c401ad155bea86/src/libraries/CapsLibrary.sol#L67-L72
Vulnerability details
Impact
The
validateVariablePoolHasEnoughLiquidity()
function in theCapsLibrary
contract is used bybuyCreditMarket()
,sellCreditMarket()
, andliquidateWithReplacement()
to verify that there is sufficient liquidity in AAVE for cash withdrawal.However, the function incorrectly reads the USDC balance from the
variablePool
asliquidity
. The actual liquidity is stored in theaUSDC
token. This can be confirmed by checking the USDC balance of the following addresses on Ethereum at block height 20204585:Consequently, after the mainnet deployment of the Size codebase,
validateVariablePoolHasEnoughLiquidity()
will use 0 asliquidity
and thus revert. This would causebuyCreditMarket()
andsellCreditMarket()
, the two main functions, to enter a denial-of-service (DoS) state.Simply put, the Size protocol will be completely non-functional due to this issue.
Proof of Concept
This PoC use a fork test to show that the incorrect liquidity reading in
validateVariablePoolHasEnoughLiquidity()
will cause a DoS on thebuyCreditMarket()
function.To run the this PoC, first update the forge-std library to at least version 1.8.0 (The USDC deal issue is fixed in this version). Then set the RPC for Ethereum mainnet in
foundry.toml
. Finally, runforge test --mc PoC -vvvv
. The result is as follows:Tools Used
Manual Review, Foundry
Recommended Mitigation Steps
Read the liquidity from aToken instead of variablePool in
validateVariablePoolHasEnoughLiquidity()
:Assessed type
DoS