Submitted by TresDelinquentes, also found by klau5, 0xadrii, leegh, minimalproxy, rvierdiiev (1, 2), 3docSec, and mahdikarimi
The natural flow of minting a new Card is through contributing to the Crowdfund. The protocol also allows users to contributeFor in the name of someone else.
Lets follow the flow of the contributeFor. The function immediately calls _contribute:
Some checks are done, then we call _processContribution:
Inside we see this if/else statement. It allows for anyone to set delegate for a user, but only if oldDelegate == address(0), meaning its never been set before. If msg.sender == contributor, they can still set the delegate to whoever they wish. Anyone else calling contributeFor after the original contributor has delegated and cant change the delegate.
The rest of the function finishes and we go back to _contribute after which we call party.mint.
Inside mint we see the following:
Here, we check if delegationsByVoter[owner] != address(0); meaning this is the first time a delegate is being set. If a delegate already exists, we set the  new delegate to the old delegationsByVoter[owner]. This is where the problem lies. Note that this function doesnt take into account if msg.sender == contributor, so once delegationsByVoter[owner] is set, there is no changing it through the mint function again.
So lets imagine the following scenario:
maxTotalContributions = 20 ether.
At this point delegationsByVoter[bob] = alice
Since Bob already has a delegate, he cant change it here, so Bobs delegate gets overwritten to Alice and he is unable to change the delegate.
Note that this way a 51% attack can easily be executed by a malicious user. A malicious user can steal multiple delegates this way and influence the Party greatly.
Foundry
We recommend removing the contributeFor function.
KingNFT (lookout) commented:
gzeon (judge) decreased severity to Medium and commented:
arr00 (Party) confirmed and commented via duplicate issue #311:
