DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: low
Invalid

Unprotected execution of `checkUpkeep` function in `LiquidationKeeper` contract

Github
https://github.com/Cyfrin/2024-07-zaros/blob/d687fe96bb7ace8652778797052a38763fbcbb1b/src/external/chainlink/keepers/liquidation/LiquidationKeeper.sol#L44-L88
https://github.com/Cyfrin/2024-07-zaros/blob/d687fe96bb7ace8652778797052a38763fbcbb1b/src/external/chainlink/interfaces/IAutomationCompatible.sol#L5-L22

Summary

The checkUpkeep function in the LiquidationKeeper contract, which implements the IAutomationCompatible interface, lacks the cannotExecute modifier. This modifier is recommended by the interface documentation to ensure the function is only simulated and not executable directly. The absence of this modifier introduces a potential vulnerability, allowing the function to be called directly and possibly impacting the contract's intended functionality and security.

Vulnerability Details

The IAutomationCompatible interface specifies that the checkUpkeep function is designed for simulation purposes to determine if any upkeep work is required. The interface documentation suggests adding the cannotExecute modifier to prevent this function from being called directly. However, the LiquidationKeeper contract does not implement this modifier, making the function executable.

function checkUpkeep(bytes calldata checkData)
external
view
returns (bool upkeepNeeded, bytes memory performData)
{
(uint256 checkLowerBound, uint256 checkUpperBound, uint256 performLowerBound, uint256 performUpperBound) =
abi.decode(checkData, (uint256, uint256, uint256, uint256));
if (checkLowerBound >= checkUpperBound || performLowerBound >= performUpperBound) {
revert Errors.InvalidBounds();
}
IPerpsEngine perpsEngine = _getLiquidationKeeperStorage().perpsEngine;
uint128[] memory liquidatableAccountsIds =
perpsEngine.checkLiquidatableAccounts(checkLowerBound, checkUpperBound);
uint128[] memory accountsToBeLiquidated;
if (liquidatableAccountsIds.length == 0 || liquidatableAccountsIds.length <= performLowerBound) {
performData = abi.encode(accountsToBeLiquidated);
return (upkeepNeeded, performData);
}
uint256 boundsDelta = performUpperBound - performLowerBound;
uint256 performLength =
boundsDelta > liquidatableAccountsIds.length ? liquidatableAccountsIds.length : boundsDelta;
accountsToBeLiquidated = new uint128[](performLength);
for (uint256 i; i < performLength; i++) {
uint256 accountIdIndexAtLiquidatableAccounts = performLowerBound + i;
if (accountIdIndexAtLiquidatableAccounts >= liquidatableAccountsIds.length) {
break;
}
accountsToBeLiquidated[i] = liquidatableAccountsIds[accountIdIndexAtLiquidatableAccounts];
if (!upkeepNeeded && liquidatableAccountsIds[accountIdIndexAtLiquidatableAccounts] != 0) {
upkeepNeeded = true;
}
}
bytes memory extraData = abi.encode(accountsToBeLiquidated, address(this));
return (upkeepNeeded, extraData);
}

Impact

The absence of the cannotExecute modifier on the checkUpkeep function allows it to be called directly, rather than being restricted to simulation. Unauthorized invocation of the checkUpkeep function, which could bypass intended access controls or conditions.

Recommendations

Add the cannotExecute modifier to the checkUpkeep function. This ensures that the function is only used for simulation and cannot be called directly.

Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

`cannotExecute` modifier missing

Appeal created

0xtheblackpanther Submitter
12 months ago
inallhonesty Lead Judge
12 months ago
inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

`cannotExecute` modifier missing

Support

FAQs

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