Platform: Code4rena
Start Date: 31/10/2023
Pot Size: $60,500 USDC
Total HM: 9
Participants: 65
Period: 10 days
Judge: gzeon
Total Solo HM: 2
Id: 301
League: ETH
Rank: 19/65
Findings: 2
Award: $435.06
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Madalad
Also found by: 0xbepresent, 3docSec, Emmanuel, HChang26, P12473, Shaheen, adriro, ast3ros, bart1e, evmboi32, klau5, pontifex, rvierdiiev, sin1st3r__
199.934 USDC - $199.93
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L636-L638 https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L1098-L1100
When proposal is created, then number of hosts is stored for it. In case if proposal succeeded and all hosts have accepted it, then it's considered as unanimous and execution delay(which is another point of security) is skipped. Pls, note that _hostsAccepted
requires that accepted hosts amount should be strictly equal to host count that was stored in proposal.
So in case, if host accepts proposal, then count of accepted hosts is incremented.
Also PartyGovernance
has abdicateHost
function which allows host to move his role to another account.
https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernance.sol#L457-L472
function abdicateHost(address newPartyHost) external { _assertHost(); // 0 is a special case burn address. if (newPartyHost != address(0)) { // Cannot transfer host status to an existing host. if (isHost[newPartyHost]) { revert InvalidNewHostError(); } isHost[newPartyHost] = true; } else { // Burned the host status --numHosts; } isHost[msg.sender] = false; emit HostStatusTransferred(msg.sender, newPartyHost); }
Using this function, host can accept proposal as many times as needed to make it unanimous. He just needs to accept proposal, then abdicateHost
to another his address and so on. As result, host can ability to use this function in his interests to reach needed amount of hosts needed(and skip execution delay) or even to not allow _hostsAccepted
function show true, when needed amount of hosts has accepted proposal(he can make accepted hosts count be bigger, than needed).
Malicious host can use _hostsAccepted
feature in his own interests.
VsCode
I don't know good solution here. With current design this functionality will not work normally.
Error
#0 - c4-pre-sort
2023-11-12T11:11:20Z
ydspa marked the issue as duplicate of #538
#1 - c4-pre-sort
2023-11-12T14:14:53Z
ydspa marked the issue as sufficient quality report
#2 - c4-judge
2023-11-19T13:32:14Z
gzeon-c4 marked the issue as satisfactory
🌟 Selected for report: TresDelinquentes
Also found by: 0xadrii, 3docSec, klau5, leegh, mahdikarimi, minimalproxy, rvierdiiev
235.1319 USDC - $235.13
https://github.com/code-423n4/2023-10-party/blob/main/contracts/crowdfund/ETHCrowdfundBase.sol#L202-L204 https://github.com/code-423n4/2023-10-party/blob/main/contracts/party/PartyGovernanceNFT.sol#L195-L198
When someone is going to contribute to InitialETHCrowdfund
, then _processContribution
is called. Contribution can be called for yourself, or on behalf of someone.
address oldDelegate = delegationsByContributor[contributor]; if (msg.sender == contributor || oldDelegate == address(0)) { // Update delegate. delegationsByContributor[contributor] = delegate; } else { // Prevent changing another's delegate if already delegated. delegate = oldDelegate; }
As you can see, in case if user already has delegate, then no one else can change it. But in case if it's not set yet, then caller can set delegator for user. This check is done only in the ETHCrowdfundBase._processContribution
function and delegator
inside InitialETHCrowdfund._contribute
is not changed.
After that, InitialETHCrowdfund._contribute
continue it's work and it provides originally passed delegator
, that was provided by called to the Party.mint
function.
So when PartyGovernanceNFT.mint
is executed it actually fetches user's current delegate and rewrites provided one, in case if current delegate is not 0.
So later, voting power is added to the current delegator of token owner.
So what we have here is that attacker can frontrun user's first contribution(when he doesn't have any delegate yet) with minimum contribution and delegator that he would like to have. So then, when user's contribution will be executed, then his delegator will be ignored and the one, provided by attacker before will be used. As result, attacker was able to increase his voting power.
Attacker can steal user's voting power.
VsCode
In this case we can't understand if user has agreed to use provided delegate. Maybe better to use offchain signatures for such cases. In case if someone wants to contribute on behalf of someone, then he should provide user's signature.
Or allow third party to delegate only for current delegator of owner. In case if it's first nft of user, then delegator should be user itself.
Error
#0 - c4-pre-sort
2023-11-12T04:59:23Z
ydspa marked the issue as duplicate of #334
#1 - c4-pre-sort
2023-11-12T04:59:26Z
ydspa marked the issue as insufficient quality report
#2 - c4-pre-sort
2023-11-13T11:38:50Z
ydspa marked the issue as remove high or low quality report
#3 - c4-pre-sort
2023-11-13T11:39:23Z
ydspa marked the issue as insufficient quality report
#4 - c4-judge
2023-11-19T17:28:19Z
gzeon-c4 marked the issue as duplicate of #418
#5 - c4-judge
2023-11-19T17:30:02Z
gzeon-c4 marked the issue as satisfactory
#6 - c4-judge
2023-11-19T17:30:49Z
gzeon-c4 marked the issue as partial-25
#7 - c4-judge
2023-11-20T18:46:41Z
gzeon-c4 changed the severity to 2 (Med Risk)
#8 - rvierdiyev
2023-11-21T21:20:28Z
hello @gzeon-c4 can i ask why this report receives 25%? looks like it describes same as #418
#9 - gzeon-c4
2023-11-23T08:09:25Z
The writeup is ambiguous, "attacker can frontrun user's first contribution" ONLY if it is contributed on behalf by someone else. The user can always rewrite the delegation due to the msg.sender == contributor
check.
#10 - rvierdiyev
2023-11-23T08:27:21Z
@gzeon-c4 i know that it can be done only if is contributed on behalf, i have explained that in the report(i mean that attacker will be the one who will do that first contribution on behalf of user)
As you can see, in case if user already has delegate, then no one else can change it. But in case if it's not set yet, then caller can set delegator for user.
also user can't change real delegation after it is set as i explained here, current one will be used
So when PartyGovernanceNFT.mint is executed it actually fetches user's current delegate and rewrites provided one, in case if current delegate is not 0.
So later, voting power is added to the current delegator of token owner.
And in order to redelagate correctly user should call delegateVotingPower
.
I should say, that this reporst is exactly the same as #418 and i have explained all parts that cause the problem
Can you pls, check this report again, as i believe that it should get full credit.
thank you
#11 - gzeon-c4
2023-11-23T08:57:28Z
Fair, admittedly the report is a bit hard to understand at first sight.
#12 - c4-judge
2023-11-23T08:57:35Z
gzeon-c4 marked the issue as satisfactory
#13 - rvierdiyev
2023-11-23T09:34:54Z
Fair, admittedly the report is a bit hard to understand at first sight.
i am sorry for that i guess it's because of language barrier
🌟 Selected for report: TresDelinquentes
Also found by: 0xadrii, 3docSec, klau5, leegh, mahdikarimi, minimalproxy, rvierdiiev
235.1319 USDC - $235.13
Using InitialETHCrowdfund._contribute
, user can provide some funds together with delegate and receive some voting power instead and delegate it(together with all other user's voting power) to provided delegate.
It's possible that user will provide 0 amount. As comment states it can be used to change user's delegator. The problem is that this will change delegator only inside ETHCrowdfundBase
, which is actually useless for the Party and then function will return. And real delegator in the Party will not be changed in this case.
As result, user will think that he had changed delegator, but in reality it will be same.
Delegator for user will not be changed.
VsCode
In case if amount is 0, then change delegator for user.
if (amount == 0) { delegateVotingPower(delegate); return; }
But be careful with using this by third parties. This should not be allowed or somehow restricted.
Error
#0 - ydspa
2023-11-12T13:50:23Z
#1 - c4-pre-sort
2023-11-12T13:50:29Z
ydspa marked the issue as insufficient quality report
#2 - c4-judge
2023-11-19T18:06:18Z
gzeon-c4 marked the issue as unsatisfactory: Invalid
#3 - rvierdiyev
2023-11-22T06:39:27Z
hello @gzeon-c4 would you consider this report as as duplicate of #311, however partially? i acknowledge that i have missed whole problem and impact, however i find part of it
from primary issue
If voting power is 0, _contribute returns and new delegate is NOT propagated to party contract (InitialETHCrowdfund.sol#L298).
this is exactly what describes this report.
i would be happy to get partial 50 or even less if you think so. thank you
#4 - c4-judge
2023-11-23T08:11:05Z
gzeon-c4 marked the issue as duplicate of #311
#5 - c4-judge
2023-11-23T08:11:10Z
gzeon-c4 marked the issue as partial-25
#6 - c4-judge
2023-11-23T08:52:36Z
gzeon-c4 marked the issue as duplicate of #418