FairSide contest - leastwood's results

Decentralized Cost Sharing Network.

General Information

Platform: Code4rena

Start Date: 09/11/2021

Pot Size: $30,000 ETH

Total HM: 6

Participants: 17

Period: 3 days

Judge: pauliax

Total Solo HM: 3

Id: 50

League: ETH

FairSide

Findings Distribution

Researcher Performance

Rank: 2/17

Findings: 4

Award: $5,960.99

🌟 Selected for report: 3

πŸš€ Solo Findings: 1

Findings Information

🌟 Selected for report: leastwood

Also found by: WatchPug, cmichel, hickuphh3, hyh, rfa

Labels

bug
3 (High Risk)
sponsor confirmed

Awards

0.1289 ETH - $610.12

External Links

Handle

leastwood

Vulnerability details

Impact

The updateVestedTokens() function is intended to be called by the FSD.sol contract when updating a user's vested token amount. A check is performed to ensure that _user == beneficiary, however, as _user is a user controlled argument, it is possible to spoof calls to updateVestedTokens() such that anyone can arbitrarily add any amount to the vested contract. Additionally, there is no check to ensure that the call originated from a trusted/whitelisted source.

There are two main reasons as to why the beneficiary or an attacker would want to call this function:

  • To increase the vested amount such that calculateVestingClaim() allows them to withdraw their entire vested amount without waiting the entire duration.
  • An attacker wishes to block withdrawals from other vested contracts by preventing successful calls to claimVestedTokens() by the beneficiary account. This can be done by increasing the vested amount such that safeTransfer() calls fail due to insufficient token balance within the contract.

Proof of Concept

https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/token/FSDVesting.sol#L147-L161 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/token/FSDVesting.sol#L100-L115 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/token/FSDVesting.sol#L125 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/token/FSDVesting.sol#L134

Tools Used

Manual code review. Discussions with dev.

Ensure that the updateVestedTokens() function is only callable from the FSD.sol contract. This can be done by implementing an onlyFSD role.

#0 - YunChe404

2021-11-14T11:32:08Z

#32

#1 - pauliax

2021-11-17T12:06:18Z

Great find, an unauthorized call is of paramount importance. Opening this issue as primary, as it contains a clear and concise explanation.

Findings Information

🌟 Selected for report: hickuphh3

Also found by: leastwood

Labels

bug
duplicate
3 (High Risk)

Awards

0.5895 ETH - $2,789.75

External Links

Handle

leastwood

Vulnerability details

Impact

FSDVesting.claimTribute() attempts to claim staking rewards which are stored as tributes and generated through membership purchases by the FSD network. The FSDVesting.sol contract accrues a conviction score which generates a percentage claim over tributes locked in FSD.sol. However, this function does not directly call TributeAccrual.claimTribute() and as a result, will instead transfer the tribute directly out the contract's vested holdings. This reduces the vested amount in FSDVesting.sol, breaking the contract as the beneficiary account is unable to claim all vested tokens without their call failing.

Therefore, this issue is of high severity as any honest tribute claim will potentially result in locked vested tokens due to a bricked FSDVesting.sol contract.

Proof of Concept

https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/token/FSDVesting.sol#L212-L226 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/dependencies/TributeAccrual.sol#L155-L168

Tools Used

Manual code review.

Consider updating the FSDVesting.claimTribute() function to directly call TributeAccrual.claimTribute() and then transfer the tribute to the beneficiary account. This will ensure that the tribute amount is transferred to the FSDVesting.sol contract before being transferred to the beneficiary.

This could look like the following:

function claimTribute(uint256 num) external onlyBeneficiary { uint256 tribute = fsd.availableTribute(num); fsd.claimTribute(num); fsd.safeTransfer(msg.sender, tribute); emit TributeClaimed(msg.sender, tribute); }

#0 - YunChe404

2021-11-14T11:32:27Z

#28

#1 - pauliax

2021-11-17T16:42:10Z

A duplicate of #28

Findings Information

🌟 Selected for report: leastwood

Labels

bug
2 (Med Risk)
disagree with severity
sponsor acknowledged
sponsor disputed

Awards

0.393 ETH - $1,859.83

External Links

Handle

leastwood

Vulnerability details

Impact

Conviction scores are calculating by taking the user's balance and multiplying it by the time elapsed. This score is updated upon each token transfer, or alternatively by directly calling ERC20ConvictionScore.updateConvictionScore(). The availableTribute() and availableGovernanceTribute() functions calculate a user's share by calculating their percentage of the total conviction score to find the amount owed to them.

This is shown in the following statement where userCS is the user's conviction score and totalCS is the protocol's total conviction score:

return amount.mul(userCS).div(totalCS);

The tribute amount that can be successfully claimed disproportionally favours users who have updated their conviction score more recently and who are early to claim their allocated tribute.

Proof of Concept

Consider the following POC:

  • Two users Alice and Bob both have a conviction score of 5 at time = 1.
  • Time advances such that time = 2.
  • Bob calls updateConvictionScore() which sets his conviction score to 5 * time = 10 where time = 2.
  • Therefore, the total conviction score is now 15 and Alice and Bob's individual conviction scores are 5 and 10 respectively.
  • Bob attempts to call claimTribute() where the tribute to claim is of size 15 tokens. As a result, Bob receives 10 tokens according to the calculation mentioned above ((15*10)/15).
  • Alice updates her conviction score and also attempts to call claimTribute(), receiving 7.5 tokens in the process.
  • As a result, a total of 22.5 tokens were transferred out despite the tribute amount being only 15 tokens in total.

As you can see from the above example, the amount of tokens transferred favours user's who claim early and update their conviction score. It is entirely possible that due to the finite amount of tokens stored in the contract, a number of user's won't be able to successfully claim their allocated tribute.

https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/dependencies/TributeAccrual.sol#L93-L112 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/dependencies/TributeAccrual.sol#L117-L136 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/dependencies/TributeAccrual.sol#L156 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/dependencies/TributeAccrual.sol#L179 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/dependencies/ERC20ConvictionScore.sol#L476-L479

Tools Used

Manual code review.

There is no easy solution to the mentioned issue. Ideally, all users should have an up to date conviction score whenever claimTribute() or claimGovernanceTribute() are called, although this would be an incredibly gas intensive mitigation. Alternatively, the protocol's total conviction score can be calculated by tracking the total minted balance of FSD.sol's token and multiplying this by a running total of time elapsed. This will need to be adjusted whenever tokens are minted or burned and updated during tribute claims.

#0 - YunChe404

2021-11-14T14:28:26Z

The exhibit does not lead to lose of funds, so severity should be lowered to level 2.

#1 - YunChe404

2021-11-14T17:26:17Z

As with any system with a finite supply, the FairSide project is not an exception (and does not intend to be).

#2 - pauliax

2021-11-19T12:53:08Z

Great observation, but I am afraid this issue falls under the nature of blockchains as mentioned by the warden it will practically be impossible to update all the users at once. Because a hypothetical attack path exists that leads to favoring an exploiter over other users, I am assigning this issue a severity of Medium.

Findings Information

🌟 Selected for report: leastwood

Labels

bug
1 (Low Risk)
sponsor acknowledged
sponsor disputed

Awards

0.131 ETH - $619.94

External Links

Handle

leastwood

Vulnerability details

Impact

The FSD token price is calculated using spot price directly on the curve. These price results are useful when calculating the membership fee which is distributed to protocol users and in calculating the bounty amount upon opening Cross Share Requests (CSRs). Spot prices can be easily manipulated by well-funded individuals or via flash loan attacks to generate increased membership and claim fees for the recipient users.

This issue is of medium severity as its possible for user's to extract increased fees from membership purchases.

Proof of Concept

https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/network/FSDNetwork.sol#L178-L189 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/network/FSDNetwork.sol#L268 https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/network/FSDNetwork.sol#L365

Tools Used

https://shouldiusespotpriceasmyoracle.com/ Manual code review

Consider avoid using spot price and adhere to the guide mentioned above in order to avoid price manipulation via flash loans. It could be useful to implement a TWAP oracle which avoids using the current block in its calculations. This should limit flash loan attacks and make it increasingly costly for well-funded individuals to manipulate the token price (assuming token liquidity is substantial). A useful TWAP flash-loan resistant implementation can be found here.

#0 - YunChe404

2021-11-14T17:27:39Z

There is no viable vulnerability by the exhibit described due to the flashloan fees/curve exit tributes.

#1 - pauliax

2021-11-19T13:02:50Z

Based on official recommendations, I think this issue falls under the non-critical category: "Unless there is something uniquely novel created by combining vectors, most submissions regarding vulnerabilities that are inherent to a particular system or the Ethereum network as a whole should be considered non-critical. Examples of such vulnerabilities include front running, sandwich attacks, and MEV. One important caveat to all of the above: unless otherwise specified by the contest sponsor or intended to be handled by the code. For example, flash loans are generally unavoidable, but since MarginSwap had a safeguard against them, we considered these findings relevant in their contest."

However, because lately we have seen some major hacks related to price manipulation (e.g. Rari Fuse VUSD pool), where even TWAP was exploited, I would like to leave this issue with a low severity score, as a reminder that spot prices and oracles are a subject of manipulation and need special attention.

Findings Information

🌟 Selected for report: ye0lde

Also found by: JMukesh, gzeon, leastwood, pants

Labels

bug
duplicate
1 (Low Risk)
disagree with severity

Awards

0.0172 ETH - $81.35

External Links

Handle

leastwood

Vulnerability details

Impact

The FairSideDAO.state() function validates the current state of a target proposal and is used to restrict certain proposal actions. The function defines an active proposal as block.number > proposal.startBlock & block.number <= proposal.endBlock when the first statement should rather be block.number >= proposal.startBlock to better reflect the intended functionality.

Proof of Concept

https://github.com/code-423n4/2021-11-fairside/blob/main/contracts/dao/FairSideDAO.sol#L312-L315

Tools Used

Manual code review.

Consider updating the condition for ProposalState.Pending in FairSideDAO.state() to block.number < proposal.startBlock such that the aforementioned conditions are accurately reflected when determining whether a proposal is active.

#0 - YunChe404

2021-11-14T14:30:50Z

The exhibit does not pose an actual security vulnerability, hence the severity should be lowered to level zero.

#1 - pauliax

2021-11-17T15:03:31Z

A duplicate of #41.

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter