Description:
The nonReentrant modifier is intended to protect the refund function from reentrancy attacks. To achieve this, a boolean variable, locked, is used as the mutex lock. However, the modifier does not set the locked variable to true after the require statement, causing the modifier to lose its purpose and allowing multiple calls to refund.
Impact:
At first glance, this might seem like a high-severity issue, since nonReentrant allows multiple calls to refund. However, refund ultimately handles the refund process via the _refundERC20 and _refundETH internal functions, which in turn call the transfer function from Solidity. The latter has a predetermined gas limit of 2300, which ensures that there are no multiple calls, reverting in that case with an EvmError: OutOfGas. Therefore, the finding is classified as medium-severity, since there is still the possibility of a gas price increase that could make the reentrancy attack feasible.
For more details, refer to the Slither detector documentation:
https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-4
Tools Used:
Slither, manual review
Proof of Concept:
Create the following contract in ChristmasDinnerTest.t.sol, which will simulate a malicious reentrancy attacker smart contract.
Add the following test to ChristmasDinnerTest.t.sol. The test will fail with an EvmError: OutOfGas, due to the predetermined 2300 gas limit of Solidity's transfer function.
Recommended Mitigation:
The easiest solution to the issue is to set the locked variable to true when passing the first check in the modifier. This would prevent a malicious actor from re-entering refund before its execution is completed.
It is recommended to use well-known and tamper-proof contract libraries to enhance the security level of smart contracts. To avoid reentrancy attacks, ChristmasDinner could inherit from ReentrancyGuard contract from OpenZeppelin, which already has a battle-tested nonReentrant modifier approved by the community.
For reference, see ReentrancyGuard.sol
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.5/contracts/security/ReentrancyGuard.sol
Even though the documentation states that a mutex variable is sufficient for avoiding reentrancy attacks, it is still recommended to write each function following the Checks-Effects-Interactions (CEI) pattern.
For example, _refundETH could be rephrased as follows:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.