Fault nonReentrant() will not prevent refund() to be re-entered, attackers could re-enter refund() to get extra funds.
https://github.com/Cyfrin/2024-12-christmas-dinner/blob/main/src/ChristmasDinner.sol#L77-L81
1, modifier nonReentrant is used to prevent refund() from being re-entered, however, in modifier nonReentrant(), after check "locked == false", locked is not set to "true" to lock the process.
2, _refundERC20() and _refundETH() are not written to following the check Effects Interactions pattern.
Attacker calls deposit with a malicious contract as the receiver.
The malicious contract call refund to get back deposit asset.
refund calls _recipient.receive()
, leading to multiple refunds.
add the following code to ChristmasDinnerTest.t.sol:
forge test --match-path test/ChristmasDinnerTest.t.sol --match-contract ChristmasDinnerTest --match-test "test_refund_reenter"
test result:
Ran 1 test for test/ChristmasDinnerTest.t.sol:ChristmasDinnerTest
[PASS] test_refund_reenter() (gas: 9223372036854754743)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.26ms (223.50µs CPU time)
attackers could re-enter refund() to get extra funds.
manually review and forge
change _refundERC20() and _refundETH() to the check Effects Interactions pattern
fix the fault nonReentrant() as following:
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.