DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: high
Invalid

Lack of Reentrancy Protection in important functions like claimAndSwap

Summary

The StrategyMainnet contract uses the Checks-Effects-Interactions (CEI) pattern, which mitigates reentrancy risk but does not entirely eliminate it. By not employing additional safeguards such as reentrancy guards, an attacker could potentially exploit an external contract call to re-enter functions like claimAndSwap and repeatedly withdraw or manipulate funds.

Vulnerability Details

A malicious or compromised transmuter or router contract could invoke a callback to StrategyMainnet during the external call.

If the callback attempts to call claimAndSwap (or another critical function) again before the first call completes, the contract could allow multiple re-entries, enabling the attacker to drain funds.

Proof of Concept (PoC)

The following illustrates how an attacker could exploit reentrancy if the transmuter is replaced or upgraded to a malicious version.This PoC assumes an attacker has deployed a malicious contract masquerading as a legitimate transmuter.

// ITransmuter Interface snippet for reference
interface ITransmuter {
function claim(uint256 _amountClaim, address _recipient) external;
function deposit(uint256 _amount, address _recipient) external;
// ... other functions ...
}
contract MaliciousTransmuter {
address public strategy;
// This sets the strategy address (StrategyMainnet)
constructor(address _strategy) {
strategy = _strategy;
}
// Pretend to perform the claim, but re-enter the Strategy during the same call
function claim(uint256 _amountClaim, address _recipient) external {
// Attack logic: Re-enter the Strategy's claimAndSwap (or another vulnerable function)
// by calling it again. The attacker can pass arbitrary parameters if not restricted.
// This direct re-call simulates reentrancy if the Strategy has no guard:
(bool success, ) = strategy.call(
abi.encodeWithSignature(
"claimAndSwap(uint256,uint256,uint256)",
_amountClaim, // attacker-chosen
1, // minimal _minOut just for example
0 // routeNumber
)
);
require(success, "Reentrancy attack failed");
}
// Stub for deposit to satisfy interface
function deposit(uint256 _amount, address _recipient) external {
// Could be left empty or do further malicious actions
}
}

Whenever claimAndSwap calls transmuter.claim(...), the malicious contract re-enters claimAndSwap before the first call finishes.

Impact

Likelihood: Medium

Exploitation requires that an attacker either controls or compromises the external contract (e.g., transmuter or router). In scenarios where the external contracts are well-audited and upgradable only through secure governance, the likelihood decreases. However, if there's any possibility of an attacker deploying or swapping in a malicious contract, the risk elevates to high.

Impact: High

In the event of a successful reentrancy exploit, attackers could repeatedly execute sensitive functions to drain funds or corrupt the strategy’s accounting. This could lead to substantial financial losses and undermine the strategy’s integrity.

Severity Level: HIGH

Tools Used

Aderyn, Slither, Manual code review, AI

Recommendations

Use OpenZeppelin’s ReentrancyGuard to add the nonReentrant modifier to critical functions like claimAndSwap.

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract StrategyMainnet is BaseStrategy, ReentrancyGuard {
// ...
function claimAndSwap(
uint256 _amountClaim,
uint256 _minOut,
uint256 _routeNumber
) external onlyKeepers nonReentrant {
//...
}
}
Updates

Appeal created

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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