-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PartyGovernanceNFT.sol#increaseTotalVotingPower - Malicious actor can front-run increaseTotalVotingPower() and rageQuit() by taking more funds from the Party and leaving less for the others #351
Comments
ydspa marked the issue as duplicate of #545 |
ydspa marked the issue as insufficient quality report |
gzeon-c4 marked the issue as unsatisfactory: |
Hey @gzeon-c4 We don't see how this is invalid and intended behavior. The description and the PoC, of this issue, clearly showcase a massive difference in funds if the attack is executed successfully. Also Because the impact and the likelihood of this attack are both High, we argue that this issue and all other dupes of #545 are valid High severity findings. |
gzeon-c4 marked the issue as not a duplicate |
gzeon-c4 marked the issue as duplicate of #414 |
gzeon-c4 marked the issue as selected for report |
gzeon-c4 marked the issue as satisfactory |
gzeon-c4 marked issue #414 as primary and marked this issue as a duplicate of 414 |
gzeon-c4 marked the issue as not selected for report |
gzeon-c4 marked the issue as not a duplicate |
gzeon-c4 marked the issue as duplicate of #545 |
gzeon-c4 marked the issue as unsatisfactory: |
See #414 (comment) |
Lines of code
https://github.com/code-423n4/2023-10-party/blob/b23c65d62a20921c709582b0b76b387f2bb9ebb5/contracts/party/PartyGovernanceNFT.sol#L166-L202
Vulnerability details
Impact
The protocol offers enhanced functionality and flexibility to trusted actors (hosts) via the use of trusted smart contracts called authorities.
PartyGovernanceNFT#increaseTotalVotingPower()
is one such function which is exclusively available to authorities. As the name implies, it is used to increase the total voting power of the party.An authority which leverages that function is
AddPartyCardsAuthority
. It is an authority which atomically distributes new party cards and updates the total voting power as needed.A malicious actor can front-run
AddPartyCardsAuthority
which callsincreaseTotalVotingPower()
andrageQuit()
by taking more funds out of the Party than the rest of the users would be able to.The reason is that
increaseTotalVotingPower()
dilutes the voting power of each user. Thus, when a user front-runs a call toincreaseTotalVotingPower()
they would be able to rage quit before their voting power is being diluted and would benefit from this fact by taking out more value from their Party tokens in comparison with the rest of the users.Proof of Concept
The
increaseTotalVotingPower()
function is pretty straightforward - it simply addsvotingPower
to thetotalVotingPower
of the party:Let's explore
rageQuit()
, which if enabled, allows users to burn a Party card and withdraw ETH and/or ERC20 tokens proportionally to the card's voting power:As we can see in
getVotingPowerShareOf(tokenId)
, the voting power share of a card is calculated by dividing it to thetotalVotingPower
of the Party, denominated in1e18
:Given the above, let's replace the voting power share formula in the calculation of withdraw amounts in
rageQuit()
:where
rageQuit()
Let's take the following example.
Prerequisites:
exchangeRateBps = 1e4
, i.e. 1:1 conversion between ETH and voting powerExample 1:
AddPartyCardsAuthority#addPartyCards()
to mint additional cards worth an additionalAddPartyCardsAuthority#addPartyCards()
callsParty#increaseTotalVotingPower()
increaseTotalVotingPower()
call and callsrageQuit()
just before it with her total voting power of5e18
This updates the Party state as follows:
increaseTotalVotingPower(5e18)
gets executed after Alice's rage quit which results in:rageQuit()
as well. His withdraw amount is:You see how Alice and Bob had the same amount of voting power. But just because Alice was able to front run the
increaseTotalVotingPower()
she was able to not only take out more funds of the Party by rage quitting but this also impacted Bob in a negative way.Now let's see what's going to happen if Alice doesn't front run
increaseTotalVotingPower()
and callsrageQuit
after its execution.Example 2
AddPartyCardsAuthority#addPartyCards()
to mint additional cards worth an additionalAddPartyCardsAuthority#addPartyCards()
callsParty#increaseTotalVotingPower(5e18)
The Party state gets updated as follows:
rageQuit()
with her total voting power of3.75e18
:The Party state gets updated as follows:
rageQuit()
with his total voting power. His withdraw amount is3.75e18
:To summarize, given all other parameters being equal, here is what the figures look like:
Alice front-runs
increaseTotalVotingPower()
:Alice doesn't front-run
increaseTotalVotingPower()
:POC
Note: The test
test_rageQuitBeforeIncreaseTotalVotingPower
showcases what's going to happen if the front running scenario occurs, i.e. Example 1.Paste the following inside
test/crowdfund/InitialETHCrowdfund.t.sol
in contractInitialETHCrowdfundTest
and runforge test --mt test_rageQuitBeforeIncreaseTotalVotingPower -vvvv
:Tools Used
Manual Review
Foundry
Recommended Mitigation Steps
The current best solution is enforcing the usage of a private mempool service for all authority actions.
Assessed type
Other
The text was updated successfully, but these errors were encountered: