Christmas Dinner

First Flight #31
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

H-3: Unauthorized Participation Status Change Vulnerability

Summary

The changeParticipationStatus function in the contract allows users to toggle their participation status. However, the function does not verify whether the caller has any valid balances or prior participation history, enabling unauthorized users to modify their participant status arbitrarily. This oversight can be exploited by malicious actors to manipulate contract states, bypassing protocol restrictions and undermining system integrity.

Vulnerability Details

Root Cause:

  • The function permits any user, regardless of their balance or history of interaction, to change their participant status.

  • There is no validation mechanism to ensure that only eligible participants can toggle their status.

  • Expected Behavior:

    • Only users who have valid balances (in ETH or supported tokens like i_WETH, i_USDC, or i_WBTC) or previous participation should be allowed to toggle their participant status.

    • Once the deadline has passed, no further modifications to the participant status should be allowed.

  • Current Behavior:

    • Any user, even those without valid balances, can toggle their participant status before the deadline.

    • This behavior allows malicious actors to manipulate the state without meeting participation requirements.

Impact

  • Unauthorized Access: Malicious users can modify their participant status to exploit any mechanisms that rely on this state.

  • System Manipulation: The lack of validation introduces potential exploits where non-participants may unfairly benefit from features meant for valid participants.

  • Protocol Integrity: The vulnerability undermines the protocol’s intended behavior, potentially leading to misuse of resources or unintended financial consequences.

Tools Used

  • Manual Code Review

  • Foundry for Testing

Recommendations

1 - Ensure Comprehensive Balance Validation
The current implementation uses a conditional to check that the caller has sufficient balances in supported tokens or ETH. This is a good safeguard; however, consider encapsulating this logic in a reusable modifier for cleaner code and easier maintenance.

modifier hasSufficientBalance() {
if (
balances[msg.sender][address(i_WETH)] <= 0 &&
balances[msg.sender][address(i_USDC)] <= 0 &&
balances[msg.sender][address(i_WBTC)] <= 0 &&
etherBalance[msg.sender] <= 0
) {
revert InsufficientBalance();
}
_;
}
  • Updated function with modifier:

    function changeParticipationStatus() external hasSufficientBalance {
    if (participant[msg.sender]) {
    participant[msg.sender] = false;
    } else if (!participant[msg.sender] && block.timestamp <= deadline) {
    participant[msg.sender] = true;
    } else {
    revert BeyondDeadline();
    }
    emit ChangedParticipation(msg.sender, participant[msg.sender]);
    }
  • Use a beforeDeadline Modifier
    Replace the inline block.timestamp check with the beforeDeadline modifier for consistency across the contract:

modifier beforeDeadline() {
if (block.timestamp > deadline) {
revert BeyondDeadline();
}
_;
}

Combined implementation:

function changeParticipationStatus() external hasSufficientBalance beforeDeadline {
participant[msg.sender] = !participant[msg.sender];
emit ChangedParticipation(msg.sender, participant[msg.sender]);
}

3 - Optimize State Transitions

Simplify the if-else logic to minimize redundant checks. The updated function toggles the status directly and relies on the modifiers to enforce eligibility and timing constraints.

4 - Enhance Testing

Add robust unit tests to validate the following scenarios:

  • A user with sufficient balances can toggle their status before the deadline.

  • A user without sufficient balances cannot call the function.

  • Attempts to toggle the status after the deadline result in a BeyondDeadline revert.

5 - Event Emission Validation

Confirm through testing that the ChangedParticipation event accurately reflects the updated participant status for all valid transitions.

PoC

function testUnauthorizedParticipationStatusChange() public {
address maliciousUser = makeAddr("maliciousUser");
assertEq(cd.balances[maliciousUser][address(weth)], 0);
assertEq(cd.balances[maliciousUser][address(usdc)], 0);
assertEq(cd.balances[maliciousUser][address(wbtc)], 0);
assertEq(cd.etherBalance[maliciousUser], 0);
vm.prank(maliciousUser);
cd.changeParticipationStatus();
bool isParticipant = cd.getParticipationStatus(user1);
assertTrue(cd.getParticipationStatus[maliciousUser]);
}
Updates

Lead Judging Commences

0xtimefliez Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

usage of change participation logic circumvents deposit

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!