Summary
The openBox
function in the smart contract fails to account for dynamically added rewards. When the owner adds a new reward using addReward
, users are unable to receive this newly added reward because the openBox
function is hardcoded to only distribute the original set of rewards (Coal, Bronze Coin, Silver Coin, and Gold Coin). The current implementation does not accommodate newly added rewards or dynamically update the probabilities, resulting in the new reward being inaccessible to users.
Vulnerability Details
function openBox() public {
require(boxesOwned[msg.sender] > 0, "No boxes to open");
uint256 randomValue = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender))) % 100;
if (randomValue < 75) {
rewardsOwned[msg.sender].push(Reward("Coal", 0 ether));
} else if (randomValue < 95) {
rewardsOwned[msg.sender].push(Reward("Bronze Coin", 0.1 ether));
} else if (randomValue < 99) {
rewardsOwned[msg.sender].push(Reward("Silver Coin", 0.5 ether));
} else {
rewardsOwned[msg.sender].push(Reward("Gold Coin", 1 ether));
}
boxesOwned[msg.sender] -= 1;
}
POC
function testBug_NewRewardNotAssigned() public {
vm.startPrank(owner);
mysteryBox.addReward("Diamond Coin", 2 ether);
MysteryBox.Reward[] memory rewards = mysteryBox.getRewardPool();
assertEq(rewards.length, 5);
assertEq(rewards[4].name, "Diamond Coin");
assertEq(rewards[4].value, 2 ether);
vm.stopPrank();
vm.deal(user1, 2 ether);
vm.startPrank(user1);
mysteryBox.buyBox{value: 0.1 ether}();
mysteryBox.openBox();
MysteryBox.Reward[] memory userRewards = mysteryBox.getRewards();
for (uint256 i = 0; i < userRewards.length; i++) {
require(
keccak256(bytes(userRewards[i].name)) != keccak256(bytes("Diamond Coin")),
"Bug: User received the new reward which should not be possible"
);
}
console2.log("Bug confirmed: User cannot receive newly added rewards due to hardcoded probabilities.");
vm.stopPrank();
}
Impact
user will never receive the newly added reward
Tools Used
Manual Rivew
Recommendations
Dynamic Reward Pool: Replace the hardcoded conditions in openBox
with a loop that dynamically checks the cumulative probabilities of all rewards in the rewardPool
.