Summary
While the contract is designed to allow only paid individuals to become participants, the ChristmasDinner:changeParticipationStatus function is flawed. It enables anyone who calls the function to become a participant, bypassing the payment requirement.
Impact
The vulnerability in changeParticipationStatus undermines the contract's core purpose by allowing unpaid users to become participants, leading to inaccurate budgeting and potential financial losses for the host. It disrupts event planning, diminishes trust in the system, and opens the contract to potential abuse, such as spamming the participant list. This could result in an unusable or unreliable platform for organizing events.
Proof Of Concept
The below code is the POC
function test_POC_changeParticipationStatus()public{
address EXPLOITER=makeAddr("EXPLOITER");
vm.startPrank(EXPLOITER);
bool check_before_call=cd.getParticipationStatus(EXPLOITER);
cd.changeParticipationStatus();
bool check_after_call=cd.getParticipationStatus(EXPLOITER);
console.log("Before call participation status",check_before_call);
console.log("After call participation status",check_after_call);
}
Add the above code in ChristmasDinnerTest.t.sol:ChristmasDinnerTest
shell forge test --match-test test_POC_receive -vv
You will get output as following
Tools Used
Foundry
Recommendations
+ function getParticipantBalances(address _user)public view returns(uint256 totalBalances){
+ uint256 player_WETH_balance=balances[_user][address(i_WETH)];
+ uint256 player_USDC_balance=balances[_user][address(i_USDC)];
+ uint256 player_WBTC_balance=balances[_user][address(i_WBTC)];
+ uint256 player_ETH_balance=etherBalance[_user];
+ totalBalances=player_WETH_balance+player_USDC_balance+player_WBTC_balance+player_ETH_balance;
+ }
function changeParticipationStatus() external {
+ uint256 participantBalances=getParticipantBalances(msg.sender);
+ if(participantBalances>0){
+ if(participant[msg.sender]) {
+ participant[msg.sender] = false;
+ // @audit some one can directly become the participant by calling this function
+ } else if(!participant[msg.sender] && block.timestamp <= deadline) {
+ participant[msg.sender] = true;
+ } else {
+ revert BeyondDeadline();
+ }
+ emit ChangedParticipation(msg.sender, participant[msg.sender]);
+ }
+ else{
+ revert("You can't change participant status without having funds");
+ }
+ }
Add the ChristmasDinner:getParticipantBalances function at the external getters section and make the above necessary chnages in changeParticipationStatus:changeParticipationStatus function.
The changeParticipationStatus function has been updated to enforce payment validation, ensuring only users who have contributed the required funds can become participants. This fix resolves the vulnerability that previously allowed unauthorized users to gain participant status.
The following is a proof of concept (PoC) demonstrating that the proposed changes will function as intended.
function test_FIX_changeParticipationStatus()public{
address EXPLOITER=makeAddr("EXPLOITER");
startHoax(EXPLOITER,1.5 ether);
uint256 balance_before_depsoit=EXPLOITER.balance;
(bool check1,)=address(cd).call{value: 1 ether}("");
require(check1,"Native ETH depsoit failed");
uint256 balance_after_depsoit=EXPLOITER.balance;
console.log("Balance Before deposit :",balance_before_depsoit);
console.log("Balance After deposit :", balance_after_depsoit);
bool participation_status_before_change_pariticipation=cd.getParticipationStatus(EXPLOITER);
cd.changeParticipationStatus();
bool participation_status_after_change_pariticipation=cd.getParticipationStatus(EXPLOITER);
console.log("Participation status before call :",participation_status_before_change_pariticipation);
console.log("Participation status after call :",participation_status_after_change_pariticipation);
}