Lines of code
<https://github.com/code-423n4/2023-08-verwa/blob/498a3004d577c8c5d0c71bff99ea3a7907b5ec23/src/LendingLedger.sol#L179>
the function claim in the LendingLedger.sol will send native token $CANTO to the msg.sender by .call which it can be EOA or Contracts, because there is no any RA checks the caller can make double call in the same time to get himself more tokens reward than deserved and even drain the contract tokens.
attacker can use a contract that will reentrant the LendingLedger.sol when it receive native token by set a specific logic in side a receive function which it call the claim function before the first call ended and before the all state changes happen which in this case the attacker can take more tokens rewards than deserved. contract like this will allow this case happen:
<https://solidity-by-example.org/hacks/re-entrancy/>
the claim function:
function claim(
address _market,
uint256 _claimFromTimestamp,
uint256 _claimUpToTimestamp
) external is_valid_epoch(_claimFromTimestamp) is_valid_epoch(_claimUpToTimestamp) {
address lender = msg.sender;
uint256 userLastClaimed = userClaimedEpoch[_market][lender];
require(userLastClaimed > 0, "No deposits for this user");
_checkpoint_lender(_market, lender, _claimUpToTimestamp);
_checkpoint_market(_market, _claimUpToTimestamp);
uint256 currEpoch = (block.timestamp / WEEK) * WEEK;
uint256 claimStart = Math.max(userLastClaimed, _claimFromTimestamp);
uint256 claimEnd = Math.min(currEpoch - WEEK, _claimUpToTimestamp);
uint256 cantoToSend;
if (claimEnd >= claimStart) {
// This ensures that we only set userClaimedEpoch when a claim actually happened
for (uint256 i = claimStart; i <= claimEnd; i += WEEK) {
uint256 userBalance = lendingMarketBalances[_market][lender][i];
uint256 marketBalance = lendingMarketTotalBalance[_market][i];
RewardInformation memory ri = rewardInformation[i];
require(ri.set, "Reward not set yet"); // Can only claim for epochs where rewards are set, even if it is set to 0
uint256 marketWeight = gaugeController.gauge_relative_weight_write(_market, i); // Normalized to 1e18
cantoToSend += (marketWeight * userBalance * ri.amount) / (1e18 * marketBalance); // (marketWeight / 1e18) * (userBalance / marketBalance) * ri.amount;
}
userClaimedEpoch[_market][lender] = claimEnd + WEEK;
}
//@audit RA may happen
if (cantoToSend > 0) {
(bool success, ) = msg.sender.call{value: cantoToSend}("");
require(success, "Failed to send CANTO");
}
}
manual review
add reentrancy checks or use the OZ reentrancy modifier:
<https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol>
Reentrancy
The text was updated successfully, but these errors were encountered:
All reactions