Bent Sandstone Oyster
Medium
The lack of reentrancy protection in NumaMinter.sol will cause potential fund loss for users as malicious actors can reenter the mint function through the external call.
In NumaMinter.sol:31
the mint
function makes an external call to another contract without implementing reentrancy protection and following the CEI (Checks-Effects-Interactions) pattern:
- Contract must have minting permissions on the Numa token
- Caller needs to be in the
allowedMinters
mapping numa
token address must be set to a non-zero address
- The Numa token contract must be controlled by a malicious actor or have a vulnerability that allows reentrancy
- Attacker deploys a malicious token contract implementing INuma interface
- Owner calls
setTokenAddress()
with the malicious token address - Attacker (who is an allowed minter) calls
mint()
- During the external call to
numa.mint()
, the malicious contract calls back into NumaMinter'smint()
function - The cycle repeats until gas is exhausted or other limits are hit
The protocol suffers from uncontrolled minting of tokens. The attacker gains an excessive amount of tokens through repeated minting operations before any limits can be enforced.
contract MaliciousNuma is INuma {
NumaMinter public minter;
uint256 public count;
constructor(address minter) {
minter = NumaMinter(minter);
}
function mint(address to, uint256 amount) external override {
if (count < 5) {
// Prevent infinite loop in test
count++;
minter.mint(to, amount); // Reenter mint function
}
}
// ... other required interface implementations
}
- Add ReentrancyGuard from OpenZeppelin.
contract NumaMinter is Ownable2Step, ReentrancyGuard {
function mint(
address to,
uint256 amount
) external nonReentrant onlyMinters {
require(address(numa) != address(0), "token address invalid");
numa.mint(to, amount);
}
}
- Consider implementing additional checks and balances such as minting limits per transaction or time-based restrictions.