-
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
rageQuit()
can be front-ran by an attacker to cause permanent fund loss for rage-quitters and potentially profit off from that.
#529
Comments
ydspa marked the issue as sufficient quality report |
ydspa marked the issue as primary issue |
Valid. In practice, |
0xble (sponsor) confirmed |
gzeon-c4 marked the issue as selected for report |
gzeon-c4 marked the issue as satisfactory |
@gzeon-c4 This is the same as #383, which is OOS since it was already reported in code-423n4/2023-05-party-findings#22. |
Not only it was reported as you correctly state, but the slippage check (ie the fix) should also prevent this from happening (except for the detail reported in #469, which doesn't seem to be mentioned in this issue). |
gzeon-c4 changed the severity to 2 (Med Risk) |
gzeon-c4 marked the issue as not selected for report |
gzeon-c4 marked the issue as duplicate of #469 |
gzeon-c4 marked the issue as partial-50 |
gzeon-c4 marked the issue as satisfactory |
gzeon-c4 marked the issue as partial-25 |
gzeon-c4 marked the issue as not a duplicate |
gzeon-c4 marked the issue as primary issue |
gzeon-c4 marked the issue as unsatisfactory: |
After more consideration, I think this is out-of-scope due being the same issue as code-423n4/2023-05-party-findings#22 and failure to mention the bypass #469 |
Hey @gzeon-c4 #469 is by no means a "bypass" that the reporter forgot to mention. When the attacker executes his front-running attack, the following "if condition" won't be executed and therefore the comparison between "amount" and "minAmount" won't occur. The if conditional won't be executed because the attacker would distribute all the assets and therefore the "amount" will be zero, and therefore "minAmount" won't even be checked. Again, if we are being fair, the fact that the reporter didn't mention this, doesn't make this issue invalid/OOS. Since it's not a "bypass" and it doesn't affect anything and doesn't make this attack unexploitable or even less impactful. Final point, the previously reported issue (code-423n4/2023-05-party-findings#9) in the previous contests just mentioned that if distribution proposals were to be executed just before a user ragequits, then the user will lose funds. So they were saying it's a "potential" attack and there needs to be a distribution proposal there and at a specific time there needs to be a user trying to rageQuit for this to work. And that's probably why the devs didn't fix this on code-level. However, In this finding, the reporter clearly states that if distributionsRequireVote is disabled (default), this will allow an attacker to cause the permanent fund loss for the user without the need for a distribution proposal, no one ever mentioned this crucial detail. Final point, the sponsor marked this issue as valid, "sponsor confirmed" and not "sponsor acknowledged" so it's clear that he intends to fully fix this and wasn't aware of the attack vector or at least wasn't aware of how serious it can be. This report should be credited for that and not dismissed because someone "hinted" at this in a previous contest while missing crucial details. |
The report didn't directly mention the It was stated that "So there will be no remaining ETH balance in the Party contract" (that means that Hence, I think this should be a dup of #469 |
Hey @Emedudu, thanks for the comment. I don't think this one should be a duplicate of #469 because of the following:
For the following 4 reasons, I don't think both reports should be treated as duplicates. @gzeon-c4 Thank you |
Hello @stalinMacias , I still believe #469 is the root issue, and once it gets fixed, the scenario described in this report won't be possible.
Both reports spotted a bug, and proposed different fixes. Isn't this why they should be dups?
The reason why attacker will be able to cause rageQuitter to lose funds is because he frontruns him with a
The minWithdrawAmount serves as slippage protection for the rageQuitter, so if he inputs a minWithdrawAmount of 0, it means he is comfortable with receiving 0 or more tokens.(This is how slippage protections work)
Although the proposed fix in this report will prevent the attack scenario described in this report, the mother bug(i.e. the bypass) won't get fixed, which could lead to problems in future as more functionalities get added. IMO, #469 spotted and directly mentioned the root cause of this issue, while this report spotted the issue, but didn't give a clear/direct description, and that's why I think it should be a dup of #469 |
Thanks for all the inputs, those are very helpful. While this report does set amount to 0, it is not an issue unless there is also the Some other arguments such as user not specifying |
gzeon-c4 changed the severity to QA (Quality Assurance) |
Lines of code
https://github.com/PartyDAO/party-protocol/blob/37dae4292dde2547a3b1ced8a041f926e1b37d58/contracts/party/PartyGovernanceNFT.sol#L389
Vulnerability details
Pre-requisite knowledge & an overview of the features in question
The
distributionsRequireVote
flag: ThedistributionsRequireVote
flag is a governance value flag set tofalse
by default in the governance values. It determines whether or not a party member can simply create a distribution without the need to create a proposal, get it to pass, or not.By default, it is set to as
false
because creating token distributions isn't seen as a sensitive action. If a party member creates a distribution, then everybody can simply claim his share of the distribution. No funds will be lost.By default, the distribution creation without voting feature/flag is toggled on unlike the governance values like
enableAddAuthorityProposal
,allowArbCallsToSpendPartyEth
,allowOperators
. Those are flags you have to toggle on by creating a proposal and getting it to pass because they are seen as sensitive actions.Overview of the vulnerability / PoC
There is no front-running protection on the
rageQuit()
function inPartyGovernanceNFT
contract. If this is coupled with the fact that the governance values of the party allows creation of distributions without voting (distributionsRequireVote
is set to false), this will allow an attacker each time someone ragequits to cause a permanent fund loss for the rage-quitter. If, for example, a party member wants to rage quit and he is supposed to get 10 ETH in exchange for burning his governance NFT tokens, he is going to get none and his governance NFT will be burnt so it's irreversible damage.How the attack looks like
Suppose we have a party with the following values:
100e18
100 ether
A party member who wants to rage quit has a governance NFT token with a voting power of
10e18
. (10% of the total voting power).The rage quitter wants to rage quit and get his claimable share in ETH balance. So he calls the
rageQuit()
function and asks the party contract to burn his governance NFT token with a voting power of10e18
and get his share of the party's ETH balance, which will be 10% so he should get 10 ETH.The attacker front-runs his transaction with a
distribute()
call to create a distribution of native ETH balance.The
distribute()
function will transfer all of the party's ETH balance to theTokenDistributor
. So there will be no remaining ETH balance in the Party contract.The victim's
rageQuit()
function call/tx will start executing. It will retrieve the current balance of the party of the token/coin the ragequitter wants to claim, in this case it is ETH. Theaddress(this).balance
call will return 0.Based on the result of the balance, it will conduct a mathematical operation to determine the ETH/ERC20 withdraw amount. https://github.com/PartyDAO/party-protocol/blob/37dae4292dde2547a3b1ced8a041f926e1b37d58/contracts/party/PartyGovernanceNFT.sol#L371
(balance * shareOfVotingPower) / 1e18;
. Since the remaining ETH balance in the party is zero, and since zero is still zero after being multiplied or divided by any other number. The total withdraw amount or in other words, the amount of ETH (or ERC20) the ragequitter will get is zero (he should've gotten 10 ETH).Next, the
rageQuit
will burn the governance NFT tokens of the ragequitter and then decrease the total voting power of the party (will be decreased by 10% or 10e18). https://github.com/PartyDAO/party-protocol/blob/37dae4292dde2547a3b1ced8a041f926e1b37d58/contracts/party/PartyGovernanceNFT.sol#L385C1-L393C10At this point, the ragequitter received zero ETH when he should've received 10 ETH and his intrinsic voting power among with his burnt governance NFT are completely gone. The attacker has caused permanent fund loss for the rage-quitting member.
Since the total voting power is decreased, that means that anytime ETH money moves to the party contract, an attacker will be able to rage-quit and leave with a share that is higher than if he hadn't executed his attack and the ragequitting party member received his fair share.
Impact
Allows a malicious member to cause permanent fund loss for other party members as well as potentially profit out of it (when totalVotingPower of the party decreases, the attacker's share of the party's assets increases).
Remediation
Can be fixed in two ways.
First method is to completely remove the
distributionsRequireVote
flag and require distributions to always be by proposal creationSecond method is to not allow a
rageQuit()
call to be executed in the same block as adistribute()
call with the same token address the ragequitter is trying to withdraw.If you want to go with this way of fixing the issue, you have to have some sort of "cooldown" mechanism between each
distribute()
call. Because if you didn't, then an attacker can keep causing a DoS for the ragequitter preventing him from ragequitting by just sending 1 wei to the party and creating a distribution of that 1 wei.Assessed type
MEV
The text was updated successfully, but these errors were encountered: