Lines of code
<https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L256-L289>
<https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L193>
<https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L286>
<https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L193>
The claimConcentratedRewards Function & claimAmbientRewards Function
The claim functions that are vulnerable to reentrancy are: For claimConcentratedRewards & for claimAmbientRewards
function claimConcentratedRewards(
// ...
) internal {
// Send ETH reward
(bool sent, ) = owner.call{value: rewardsToSend}("");
require(sent, "Sending rewards failed");
}
function claimAmbientRewards(
// ...
) internal {
// Send ETH reward
(bool sent, ) = owner.call{value: rewardsToSend}("");
require(sent, "Sending rewards failed");
}
Specifically, this is the problematic line using .call() to send ETH:
(bool sent, ) = owner.call{value: rewardsToSend}("");
The issue is that by using .call(), the contract is vulnerable to reentrancy if the recipient contract calls back into the contract before this line finishes executing.
This could allow draining additional rewards by re-entering before the sent check.
Specifically, a malicious recipient contract could call claimConcentratedRewards() again, before the sent check here runs.
This would allow claiming rewards multiple times before the balance is updated.
A simple example exploit:
contract Attacker {
function attack(LiquidityMining lm) external {
lm.claimReward();
// Re-enter before first claim finishes
lm.claimReward();
}
function claimReward() external {
// Callback to exploit reentrancy
}
}
Vs
This could be mitigated by using a reentrancy guard.
To prevent this, the contract should use a reentrancy guard:
bool internal locked;
modifier nonReentrant() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
function claimConcentratedRewards() internal nonReentrant {
// Rest of function
}
Adding a reentrancy guard prevents reentering until the function completes execution.
Reentrancy
The text was updated successfully, but these errors were encountered:
All reactions