Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Valid

Incorrect owner in vault withdrawal

Summary

The _withdrawFromVault function in LendingPool incorrectly uses msg.sender as the owner parameter when withdrawing from Curve vault. During borrowing, the vault attempts to burn shares from the borrower instead of from the LendingPool, which actually owns them.

Vulnerability Details

When a user attempts to borrow and RToken does not have enough liquidity (as seen in balanceOf(reserve.reserveRTokenAddress)), the protocol tries to withdraw from Curve vault:

function _ensureLiquidity(uint256 amount) internal {
// if curve vault is not set, do nothing
if (address(curveVault) == address(0)) {
return;
}
uint256 availableLiquidity = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
if (availableLiquidity < amount) {
uint256 requiredAmount = amount - availableLiquidity;
// Withdraw required amount from the Curve vault
> _withdrawFromVault(requiredAmount);
}
}

In _withdrawFromVault, msg.sender is incorrectly passed as the owner who owns the shares, whereas LendingPool actually owns them. It can be seen in _depositIntoVault that address(this) (LendingPool) receives the shares. When borrowing, msg.sender only seeks to borrow reserve assets using their NFT collateral:

function _withdrawFromVault(uint256 amount) internal {
> curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
totalVaultDeposits -= amount;
}

The Curve vault's withdraw function requires the owner to have sufficient shares to burn:

def withdraw(
assets: uint256, # amount to withdraw
receiver: address, # who gets the assets
> owner: address, # who owns the shares to burn
max_loss: uint256 = 0,
strategies: DynArray[address, MAX_QUEUE] = []
) -> uint256:

The vault will try to burn shares from msg.sender (the borrower) who does not own any vault shares as these are held by the LendingPool.

Impact

High: All borrows requiring vault withdrawals will fail as the vault attempts to burn shares from the borrower who owns no shares.

Recommendations

Fix the owner parameter to be the LendingPool's address (address(this)). Additionally, update the receiver to reserve.reserveRTokenAddress (see the submission "Incorrect receiver in vault withdrawal" for this vulnerability):

function _withdrawFromVault(uint256 amount) internal {
- curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
+ curveVault.withdraw(amount, reserve.reserveRTokenAddress, address(this), 0, new address[](0));
totalVaultDeposits -= amount;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::_withdrawFromVault incorrectly uses msg.sender instead of address(this) as the owner parameter, causing vault withdrawals to fail

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::_withdrawFromVault incorrectly uses msg.sender instead of address(this) as the owner parameter, causing vault withdrawals to fail

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.