Lucene search

K
code423n4Code4renaCODE423N4:2023-10-CANTO-FINDINGS-ISSUES-254
HistoryOct 06, 2023 - 12:00 a.m.

Reentrancy is possible in claim functions, which call out via .call().

2023-10-0600:00:00
Code4rena
github.com
1
reentrancy risk
.call function
vulnerable functions
exploit
mitigation steps

6.8 Medium

AI Score

Confidence

Low

Lines of code
<https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L256-L289&gt;
<https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L193&gt;
<https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L286&gt;
<https://github.com/code-423n4/2023-10-canto/blob/40edbe0c9558b478c84336aaad9b9626e5d99f34/canto_ambient/contracts/mixins/LiquidityMining.sol#L193&gt;

Vulnerability details

Impact

  • Reentrancy is possible in claim functions, which call out via .call().

Proof of Concept

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   
  }
  
}

Tools Used

Vs

Recommended Mitigation Steps

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.

Assessed type

Reentrancy


The text was updated successfully, but these errors were encountered:

All reactions

6.8 Medium

AI Score

Confidence

Low