Summary
The order of operations in PuppyRaffle:refund facilitates a reentrancy attack.
Vulnerability Details
The refund function is susceptible to a reentrancy attack due to the fact that the .sendValue
function is called before the balance of the address requesting the refund is adjusted only after the transfer.
function refund(uint256 playerIndex) public {
address playerAddress = players[playerIndex]
require(playerAddress == msg.sender, "PuppyRaffle: Only the player can refund");
require(playerAddress != address(0), "PuppyRaffle: Player already refunded, or is not active");
payable(msg.sender).sendValue(entranceFee);
players[playerIndex] = address(0)
emit RaffleRefunded(playerAddress);
}
Impact
Malicious actor can drain the contract.
Tools Used
Manual review
Recommendations
Please follow CEI pattern by adjusting the player refund status before sending the refund amount.
function refund(uint256 playerIndex) public {
address playerAddress = players[playerIndex];
require(playerAddress == msg.sender, "PuppyRaffle: Only the player can refund");
require(playerAddress != address(0), "PuppyRaffle: Player already refunded, or is not active");
++ players[playerIndex] = address(0);
payable(msg.sender).sendValue(entranceFee);
-- players[playerIndex] = address(0);
emit RaffleRefunded(playerAddress);
}
Moreover, the sendValue
function available in OpenZeppelin's Address.sol
has the following comment:
IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html