Closed c4-bot-1 closed 7 months ago
Picodes marked the issue as duplicate of #46
Picodes marked the issue as satisfactory
Hey @Picodes,
thanks for reviewing this submission. This issue is not a duplicate of #46. I have already submitted the vulnerability, which is described in #46 in my separate issue #187.
The 2 described vulnerabilities look similar, but target different issues and also require different fixes.
This issue:
Issue #46:
Hi, the fact that the user has to burn its SALT (i.e lose money) only to be able to propose pass a proposal, plus the fact that this can only happens when less that 0.5% SALT are staked make it very unlikely to happen.
So I hardly see how this submission can be considered a medium risk too.
Hi @InfectedIsm,
First I would like to add that you seem to misunderstand the issue. The minimumQuorum
is enforced when passing ballots, not when proposing them. Additionally I would like to answer your added points:
the fact that the user has to burn its SALT (i.e lose money) only to be able to propose a proposal, ..., make it very unlikely to happen
In a Med Severity issue, there does not needs to be any profit and there can be a loss for the exploiter. The requirement, as per the Severity Classification is "Assets not at direct risk, but the function of the protocol or its availability could be impacted". In this case the intended functionality of the minQuorum
is clearly impacted. If it is unlikely that an attacker abuses it or not does not impact the severity. If permanently loosing salt to make a ballot pass makes sense will depend on the ballot. If a user can gain something worth more than the salt lost from the ballot being passed, it can be a reasonable call.
the fact that this can only happens when less that 0.5% SALT are staked make it very unlikely to happen
The restriction of it only being possible while less than 0.5% of salt are staked does not affect the validity of the issue in any way, it just restricts in which protocol states it can happen. As 0.5% of salt being staked is a valid protocol state, this does not change the issue.
But lets wait for @Picodes judging :)
Hi @J4X-98, my bad for having mixed "propose" and "pass" when expressing myself as this is obviously worse to be able to pass a proposal than to be only able propose it. That was clearly "pass" that I had in mind, so correcting it.
I'm definitely not saying that this finding is invalid, but rather expressing the fact that likelihood of being executed is far away from #716 and wanted to highlight it.
"If permanently loosing salt to make a ballot pass makes sense will depend on the ballot. If a user can gain something worth more than the salt lost from the ballot being passed, it can be a reasonable call." I totally agree, and no scenario has been demonstrated here on how this could be exploited to gain an advantage, which seems pretty difficult by the fact that proposals are very limited on Salty, as parameters can only be modified by ~10% steps of the value.
But I completely see your point here, I think I made mine and as you said will leave this to judge now
@J4X-98 to me it's the same issue and the proof is that your mitigations are in fact the same suggestion: store the quorum during the proposal creation to avoid any manipulation during the voting phase. Let me know if I am missing something.
Hi @Picodes,
thanks for reviewing my comment. In my opinion, the 2 are different issues, although they are fixed similarly. They result from different root issues, break different functionalities in the code, and are exploited in different ways.
Attack on stakedSalt
Root Issue: Missing storage of stakedSalt variable in the ballot struct Broken Functionality: requiredQurorum gets set lower than intended allowing to pass votes when a lot of people have staked Attack: Vote->Unstake Salt->Pass Ballot->CancelUnstake Fix: Save the stakedSalt parameter in the ballot struct
Attack on totalSalt
Root Issue: Missing storage of totalSalt variable in the ballot struct Broken Functionality: minimumQuorum will not overwrite the requiredQuorum as the following snippet does not get executed:
if ( requiredQuorum < minimumQuorum )
requiredQuorum = minimumQuorum;
}
This will allow ballots to pass easier than intended if very few have staked which should result in triggering the overwrite of requiredQuorum with minimumQuorum.
Attack: Vote->Unstake Salt at worst possible conditions -> Wait 1 week -> Finish Unstake and burn Salt -> Pass Vote Fix: Save the totalSalt parameter in the ballot struct
Argumentation:
This differentiates the issues in my opinion. From my knowledge of the C4 judging guidelines to be duplicated, issues have to result from the same root issue, which is not the case here.
Additionally, I would like to add that even if the issues were bundled into a root, then all issues that only found the incorrect use of stakedSalt would be 50% dups of the main issue, and this issue and #187 would together make up the main issue describing the full impact of the vulnerability. This is especially the case as the current primary issue does not in any way describe the problem of totalSalt being decreased by burning, or mentions a mitigation for it. Which could result in a sponsor reading the report, potentially only fixing 1 of the 2 issues.
@J4X-98 I still respectfully disagree.
To me, the root issue is clearly that the quorum isn't stored. Note how the mitigation of https://github.com/code-423n4/2024-01-salty-findings/issues/716 solves both issues. Then, as the quorum depends on 2 variables (the staked SALT and the total supply) you very smartly came with 2 scenarios. But that doesn't make it 2 distinct issues in my opinion.
Regarding how to handle duplicate findings and partial credits, the rule is:
"All issues which identify the same functional vulnerability will be considered duplicates regardless of effective rationalization of severity or exploit path. However, any submissions which do not identify or effectively rationalize the top identified severity case may be judged as “partial credit” and may have their shares divided at judge’s sole discretion."
So here all issues identifying the fact that you can manipulate the quorum by unstaking should get full credit as it is "the top identified severity case".
Lines of code
https://github.com/code-423n4/2024-01-salty/blob/main/src/dao/Proposals.sol#L335 https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/Staking.sol#L114-L120
Vulnerability details
Bug Description
In the Salty DAO's voting system, the
requiredQuorumForBallotType()
function is pivotal in ensuring a ballot receives votes from a sufficient percentage of stakers. This function calculates a required quorum based on the total stakes and compares it to aminimumQuorum
, set as 0.5% of the total SALT token supply. If the calculated quorum is below this minimum, theminimumQuorum
is used instead.However, a vulnerability exists due to the fluctuating
totalSupply
of SALT, which changes when tokens are burned. A user close to the 0.5% ownership threshold can vote, then unstake and burn their SALT, effectively lowering the minimum quorum threshold. This allows the user to finalize a proposal even though they voted with less than 0.5% of the total supply.Impact
This issue enables a malicious staker to finalize a ballot below the intended minimum quorum, potentially manipulating the outcome of key protocol decisions.
Proof of Concept
A test scenario demonstrates how this issue can be exploited:
The test must be added to
/dao/tests/Dao.t.sol
and can be run by callingCOVERAGE="yes" NETWORK="sep" forge test -vvvv --rpc-url https://rpc.sepolia.org --match-test "testQuorumReducedByBurning"
.Tools Used
Manual Review
Recommended Mitigation Steps
To mitigate this issue, the
Ballot
struct should include thetotalSupply
of SALT at the time of ballot creation. This value should then be used to calculate the minimum quorum when finalizing the ballot:Incorporating the
totalSupply
into theBallot
struct ensures that the minimum quorum reflects the SALT supply at the time of voting, preserving the integrity of the voting process and preventing manipulation through token burning.Assessed type
Governance