The 'PuppyRaffle::refund' function() is vulnerable to a reentrancy attack, which can put the potential funds of the participants of the Raffle at risks.
The untrusted contract can then make a recursive call back to the original function, potentially leading to unexpected behavior or even draining of funds. Also there are no security checks for transferring the funds whether the transfer is success or not.
An attacker can deploy a malicious contract with a fallback function that can receive Ether and a function that can call the refund function of the "PuppyRaffle" contract.
The attacker calls the refund function, providing their own address as "playerAddress".
Inside the refund function, the "entrance fee" is sent back to the "attacker's" contract address using payable(msg.sender).sendValue(entranceFee).
The "attacker's" contract, having received the Ether, triggers the fallback function, which calls the refund function of your contract again, potentially leading to a "recursive loop of refund calls".
Since there is no check to prevent reentrancy, the attacker's contract can repeatedly call the refund function and easily steal the contract's balance.
Here is my proof of test code to show that the function is vulnerable to reentrancy attacks:
Firstly copy paste this test is your Truffle set up:
In the MaliciousContract.sol file, implement the malicious contract with a function that exploits the reentrancy vulnerability:
Finally in the deploy_contracts.js migration script, make sure to deploy both the PuppyRaffle and MaliciousContract contracts:
And then run the test, the test will demonstrate that the "refund function" in the PuppyRaffle contract can be easily exploited through a reentrancy attack by the malicious contract.
Remix IDE, Truffle
To mitigate this follow the "Checks-Effects-Interactions" pattern, where you perform all checks and state modifications before any external interactions
This way, even if a reentrant call is made, the contract’s state will already be updated and the reentrant call will fail the require(playerAddress != address(0)) check .
reentrancy in refund() function
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.