Summary
Player with address(0) can enterRaffle
Vulnerability Details
When we call function enterRaffle
with an array of address newPlayers
, one of value in newPlayers
can be address(0)
POC:
function testCanEnterRaffleWithAddress0() public {
address[] memory players = new address[](2);
players[1] = playerTwo;
puppyRaffle.enterRaffle{value: entranceFee * 2}(players);
console.log("Player: ", players[0]);
}
Output:
[⠢] Compiling...
[⠢] Compiling 1 files with 0.7.6
[⠆] Solc 0.7.6 finished in 2.00s
Running 1 test for test/PuppyRaffleTest.t.sol:PuppyRaffleTest
[PASS] testCanEnterRaffleWithAddress0() (gas: 71055)
Logs:
Player: 0x0000000000000000000000000000000000000000
Traces:
[71055] PuppyRaffleTest::testCanEnterRaffleWithAddress0()
├─ [51058] PuppyRaffle::enterRaffle{value: 2000000000000000000}([0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000002])
│ ├─ emit RaffleEnter(newPlayers: [0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000002])
│ └─ ← ()
├─ [0] console::log(Player: , 0x0000000000000000000000000000000000000000) [staticcall]
│ └─ ← ()
└─ ← ()
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.83ms
Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)
Impact
Address(0) can be a player and also be a winner.
Tools Used
Foundry
Recommendations
Check if one of players is address(0) or not
function enterRaffle(address[] memory newPlayers) public payable {
+ for (uint256 i = 0; i <= newPlayers.length - 1; i++) {
+ require(newPlayers[i] != address(0), "PuppyRaffle: Empty players");
+ }
require(msg.value == entranceFee * newPlayers.length, "PuppyRaffle: Must send enough to enter raffle");
for (uint256 i = 0; i < newPlayers.length; i++) {
players.push(newPlayers[i]);
}
// Check for duplicates
for (uint256 i = 0; i < players.length - 1; i++) {
for (uint256 j = i + 1; j < players.length; j++) {
require(players[i] != players[j], "PuppyRaffle: Duplicate player");
}
}
emit RaffleEnter(newPlayers);
}
TEST:
function testCanEnterRaffleWithAddress0() public {
address[] memory players = new address[](2);
players[1] = playerTwo;
vm.expectRevert("PuppyRaffle: Empty players");
puppyRaffle.enterRaffle{value: entranceFee * 2}(players);
console.log("Player: ", players[0]);
}
RESULT:
[⠆] Compiling...
[⠘] Compiling 3 files with 0.7.6
[⠃] Solc 0.7.6 finished in 2.52s
Running 1 test for test/PuppyRaffleTest.t.sol:PuppyRaffleTest
[PASS] testCanEnterRaffleWithAddress0() (gas: 23737)
Logs:
Player: 0x0000000000000000000000000000000000000000
Traces:
[23737] PuppyRaffleTest::testCanEnterRaffleWithAddress0()
├─ [0] VM::expectRevert(PuppyRaffle: Empty players)
│ └─ ← ()
├─ [790] PuppyRaffle::enterRaffle{value: 2000000000000000000}([0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000002])
│ └─ ← "PuppyRaffle: Empty players"
├─ [0] console::log(Player: , 0x0000000000000000000000000000000000000000) [staticcall]
│ └─ ← ()
└─ ← ()
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 7.53ms
Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)