Skip to content

Latest commit

 

History

History
89 lines (56 loc) · 6.7 KB

2022-11-01.md

File metadata and controls

89 lines (56 loc) · 6.7 KB

Vulnerability disclosure 2022-11-01

Summary

  • During a routine check, irregularities were discovered in the amount of SPELL bribes being claimed by some users of the BribeV2 contract. Following analysis, a flaw was found in the way the contract calculates bribe allocations.
  • The flaw causes bribes to be allocated based on each user's locked amount of CRV rather than allocating based on their veCRV balance.
  • Following the investigation, no users were determined to have intentionally exploited this flaw. This is based on no evidence of users with short lock times voting on bribed gauges.
  • Some BribeV2 users were unknowingly subject to faulty bribe calculations due to the fact that lock time was not taken into account.
  • Yearn developers patched the vulnerability and deployed a new version of the contract (yBribe) which properly allocates bribes to users.*

Disclaimer: The Yearn team did not write or deploy the original BribeV2 contract. However, as a heavy user of it, decided to act quickly to deploy a new contract so that operations could resume.

Background

Curve Finance have pioneered a now popular tokenomics system known as veCRV which allows users to lock tokens for up to 4 years with a balance that decays every block until the lock expires or is extended. As their veCRV balance decays, so does their relative influence over Curve governance, including gauge voting.

The BribeV2 contract was released as a trustless mechanism for entities (usually protocols) to incentivize veCRV voters to cast votes for their desired gauge, increasing the amount of CRV emissions to that gauge. A briber would simply deposit tokens into BribeV2, and the contract reads from Curve's Gauge Controller to calculate token allocations for each voter according to their global influence on the gauge weights.

Importantly, gauge weights are determined not by a user's locked amount, but by their veCRV balance which is a time-based decayed representation of their locked amount.

Whilst preparing an internal report for Yearn's Curve Voting + Bribes committee, Yearn devs discovered irregularities in the amount of SPELL bribes being claimed each week from BribeV2[1].

In particular, one user was claiming over 20% of the weekly SPELL rewards, via multiple wallets[2], despite having relatively small veCRV[3] balances in each.

Details of vulnerability

The source of the irregularities had to do with BribeV2 utilizing the slope value for users and gauges in the Gauge Controller rather than bias value [4].

A user's slope value in the veCRV system is a representation of the decay rate per second on their locked amount, but completely ignores their lock duration. This is a critical flaw because it allows someone with a short lock to get paid out at an equal rate to someone with a long lock on the same amount. Crucially, this method does not match how the Gauge Controller assigns gauge weights, which does indeed take lock time into account.

This issue can lead to the following exploit:

  1. The numbers used by BribeV2 are stale. Only up-to-date from the last time the claimer voted. A claimer can withdraw all of their veCRV after voting and BribeV2 won’t know.
  2. BribeV2 checks voting power based on how much is locked rather than what the voting balance is. A user locking for one week will get the same share as a user locking for 4 years.

BribeV2 incorrectly uses a user's slope (which is determined by the amount of CRV they lock). As a part of this report, Yearn devs produced a detailed comparison of slope and bias[5].

The combination of the two means there is an exploit where a user can:

  1. Lock 1m CRV for the minimum amount of time (7 days)
  2. Vote for a gauge with a veCRV balance of 4,808 (1m CRV / 208 weeks) but claim rewards based on a gauge vote of 1m veCRV.
  3. Withdraw 1m CRV as soon as possible
  4. Continue to claim rewards every week forever

Because the CRV can be withdrawn after a week, an exploiter can cycle the same CRV through multiple wallets getting perpetual rewards forever on each, as can be seen in this proof of concept of the exploit[6].

Details of fix

To remedy the issues in BribeV2, we have written a new BribeV3 (aka yBribe) contract with the following fixes:

  1. BribeV2's incorrect use of slope is changed to bias - which is computed from slope and lock end.

This fix addresses both problems identified above. There is a full diff[7] available for BribeV2 vs BribeV3.

Timeline of events

October 27, 2022 (Thursday)

  • 11:58: Irregularities in claimed SPELL bribe amounts discovered in BribeV2.
  • 12:36: BribeV2's incorrect use of slope is investigated as the potential cause of the problem
  • 14:07: Finalized PoC confirming the exploit.

October 28, 2022 (Friday)

  • 14:33: An internal Yearn group is formed to start work on Bribe v3 (aka yBribe).

October 31, 2022 (Monday)

  • 16:13: Abracadabra are informed of the vulnerability before they transfer their weekly SPELL bribes to BribeV2.
  • 20:20: Contact is made with Curve (via the Crypto Risk Team) to make them aware of the vulnerability before they transfer any further CRV bribes to BribeV2.

November 1, 2022 (Tuesday)

  • 17:00: BribeV3 (aka yBribe) is deployed[8].
  • 18:00: Public disclosure through this report.

Third party disclosure

As per Yearn's security process document, the project does not currently have any established bilateral disclosure agreements.[9] However, in this case disclosures were made to Abracadabra and Curve (the only two protocols currently bribing via BribeV2) prior to this publication.

References

  1. BribeV2: 0x7893bbb46613d7a4fbcc31dab4c9b823ffee1026
  2. Suspect TX: 0x47e10eebda1b9afbabe622d3deece757d6d49a0510081635fc7dc21efe50aeeb
  3. veCRV: 0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2
  4. Gauge Controller: 0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB
  5. Slope vs Bias: https://hackmd.io/uHlrUlTVTQajHQISDCfaIA?view
  6. Proof of Concept: https://gist.github.com/flashfish0x/f8067de4084f3d010cab29d27b6c75f2
  7. BribeV2 vs BribeV3: https://www.diffchecker.com/U1GZrBDM
  8. BribeV3: 0x03dFdBcD4056E2F92251c7B07423E1a33a7D3F6d
  9. Bilateral Responsible Disclosure Agreements: /~https://github.com/yearn/yearn-security/blob/master/SECURITY.md#bilateral-responsible-disclosure-agreements