Summary
The purrgeBadPawsition
function does not implement any access control mechanism, which means that anyone can call this function to purge the position of any user. This lack of restriction can be exploited by malicious actors to perform unauthorized operations on users' collateral positions.
Vulnerability Details
The purrgeBadPawsition
function allows external users to call it without any restriction, making it vulnerable to unauthorized access. The function should restrict access to ensure that only authorized entities can perform the purging operation.
function purrgeBadPawsition(address _user) external returns (uint256 _totalAmountReceived) {
require(!(_hasEnoughMeowllateral(_user)), KittyPool__UserIsPurrfect());
uint256 totalDebt = kittyCoinMeownted[_user];
kittyCoinMeownted[_user] = 0;
i_kittyCoin.burn(msg.sender, totalDebt);
uint256 userMeowllateralInEuros = getUserMeowllateralInEuros(_user);
uint256 redeemPercent;
if (totalDebt >= userMeowllateralInEuros) {
redeemPercent = PRECISION;
}
else {
redeemPercent = totalDebt.mulDiv(PRECISION, userMeowllateralInEuros);
}
uint256 vaults_length = vaults.length;
for (uint256 i; i < vaults_length; ) {
IKittyVault _vault = IKittyVault(vaults[i]);
uint256 vaultCollateral = _vault.getUserVaultMeowllateralInEuros(_user);
uint256 toDistribute = vaultCollateral.mulDiv(redeemPercent, PRECISION);
uint256 extraCollateral = vaultCollateral - toDistribute;
uint256 extraReward = toDistribute.mulDiv(REWARD_PERCENT, PRECISION);
extraReward = Math.min(extraReward, extraCollateral);
_totalAmountReceived += (toDistribute + extraReward);
_vault.executeWhiskdrawal(msg.sender, toDistribute + extraReward);
unchecked {
++i;
}
}
}
Impact
The lack of access control allows any external user to:
Purge the position of any user without their consent.
Potentially manipulate the collateral positions of other users, leading to financial loss and instability within the contract.
Tools Used
Manual
Recommendations
Implement access control to restrict who can call the purrgeBadPawsition
function. This can be done using an onlyAuthorized
modifier or similar mechanism to ensure that only authorized entities can perform this operation.
modifier onlyAuthorized() {
require(isAuthorized(msg.sender), "Caller is not authorized");
_;
}
function isAuthorized(address _caller) internal view returns (bool) {
return authorizedAddresses[_caller];
}
function purrgeBadPawsition(address _user) external onlyAuthorized returns (uint256 _totalAmountReceived) {
require(!(_hasEnoughMeowllateral(_user)), KittyPool__UserIsPurrfect());
require(vaults.length > 0, "No vaults available");
uint256 totalDebt = kittyCoinMeownted[_user];
uint256 userMeowllateralInEuros = getUserMeowllateralInEuros(_user);
uint256 redeemPercent;
if (totalDebt >= userMeowllateralInEuros) {
redeemPercent = PRECISION;
} else {
redeemPercent = totalDebt.mulDiv(PRECISION, userMeowllateralInEuros);
}
uint256 vaults_length = vaults.length;
for (uint256 i; i < vaults_length; ) {
IKittyVault _vault = IKittyVault(vaults[i]);
uint256 vaultCollateral = _vault.getUserVaultMeowllateralInEuros(_user);
uint256 toDistribute = vaultCollateral.mulDiv(redeemPercent, PRECISION);
uint256 extraCollateral = vaultCollateral - toDistribute;
uint256 extraReward = toDistribute.mulDiv(REWARD_PERCENT, PRECISION);
extraReward = Math.min(extraReward, extraCollateral);
_totalAmountReceived += (toDistribute + extraReward);
_vault.executeWhiskdrawal(msg.sender, toDistribute + extraReward);
unchecked {
++i;
}
}
kittyCoinMeownted[_user] = 0;
i_kittyCoin.burn(msg.sender, totalDebt);
}