code-423n4 / 2024-01-salty-findings

5 stars 3 forks source link

BootstrapBallot can be DOS preventing the exchange from initializing at launch #955

Closed c4-bot-4 closed 5 months ago

c4-bot-4 commented 5 months ago

Lines of code

Vulnerability details

Description

At the launch of the Salty protocol, there would be a BootstrapBallot which initiates the exchange. This ballot allows users with authorized wallets to vote for the exchange launch via the BootstrapBallot.vote function.

File: src/contracts/launch/BootStrapBallot
    function vote( bool voteStartExchangeYes, bytes calldata signature ) external nonReentrant
        {
        require( ! hasVoted[msg.sender], "User already voted" );

        // Verify the signature to confirm the user is authorized to vote
        bytes32 messageHash = keccak256(abi.encodePacked(block.chainid, msg.sender));
        require(SigningTools._verifySignature(messageHash, signature), "Incorrect BootstrapBallot.vote signatory" );

        if ( voteStartExchangeYes )
            startExchangeYes++;
        else
            startExchangeNo++;

        hasVoted[msg.sender] = true;

        // As the whitelisted user has retweeted the launch message and voted, they are authorized to the receive the airdrop.
        airdrop.authorizeWallet(msg.sender);
        }

This function checks the user is authorized to vote by verifying their signature using an offchain verifier in SigningTools._verifySignature. The function tracks a vote tally via BoostrapBallot.startExchangeYes and BootstrapBallot.startExchangeNo and increments either of these depending on whether the user votes for or against the ballot. At the end of the ballot period an external function BootstrapBallot.finalizeBallot is called. This function is extremely important as it initializes the protocol through the InitialDistribution contract and sets pools.startExchangeApproved to true. In order for this to be possible, the number of for votes must be higher than the number of against votes. Additionally, this ballot does not have a minimum or maximum limit on how many votes are required for this ballot to be considered valid.

File: src/launch/BoostrapBallot.sol
function finalizeBallot() external nonReentrant
        {
        require( ! ballotFinalized, "Ballot has already been finalized" );
        require( block.timestamp >= completionTimestamp, "Ballot is not yet complete" );

        if ( startExchangeYes > startExchangeNo )
            {
            exchangeConfig.initialDistribution().distributionApproved();
            exchangeConfig.dao().pools().startExchangeApproved();

            startExchangeApproved = true;
            }

        emit BallotFinalized(startExchangeApproved);

        ballotFinalized = true;
        }

A malicious user with sufficient motivation and resources may generate multiple (as many as possible) addresses, ensure they are whitelisted and authorized to vote, and then these addresses can sybil the ballot at launch to prevent the exchange from ever going live. This user can achieve this by either ensuring both votes tallies are exactly equal or startExchangeNo votes are higher than startExchangeYes votes at the end of the validity period. Alternatively, the user can themselves make the tallies unfavourable for the protocol and then call the BootstrapBallot.finalizeBallot once the completionTimestamp is reached. If this function is ever called with unfavourable vote tallies, the exchange would never go live as the ballot would get finalized anyway.

Impact

The most important functions of this protocol relies initialDistribution.distributionApproved and pools().startExchangeApproved being set when the ballot passes. This would affect staking and adding of liquidity. Essentially, the protocol would be non-functional. Mitigation would require the protocol to be be relaunched.

Proof of Concept

Tools Used

Manual Review

Recommended Mitigation Steps

Assessed type

DoS

c4-judge commented 5 months ago

Picodes marked the issue as primary issue

c4-sponsor commented 5 months ago

othernet-global (sponsor) disputed

othernet-global commented 5 months ago

Generating whitelisted addresses to vote in the BoootstrapBallot is not possible. The addresses are whitelisted offchain and cannot be added arbitrarily.

c4-judge commented 5 months ago

Picodes changed the severity to QA (Quality Assurance)

Picodes commented 5 months ago

Downgrading to Low for the following reasons:

c4-judge commented 5 months ago

Picodes marked the issue as grade-c