In crvUSD, it was possible to spread debt across different price ranges represented by ticks. Users depositing into a specific tick would receive shares of that tick.
It appeared that each tick was susceptible to a donation attack. This type of attack is detailed
in our blog. In essence, the attacker could steal users’ funds across different price ranges.
How did we manage to donate funds to a specific tick without minting shares? Firstly, the
AMM.exchange() function allowed movement of the active tick and trade between crvUSD and collateral. Any trade took a fee from the trader, which was left in the passed ticks. This fee acted as a donation. However, this only took fractions of a percent from the traded amount, insufficient for a full-scale attack. To overcome this limitation, an attacker had to
combine the calls of the withdraw(), deposit(), and repay() functions in order to perform the same inflation attack on themselves. This increased the donation in the ticks by 1.5 times.
In the end, an attacker could initiate a single transaction that ran a loop of 120 iterations in the smart contract, inflating the donation to a value of
100_000e18. This allowed them to steal any amount from other users.
The Curve team addressed this issue by introducing
virtual shares, exactly as recommended by OpenZeppelin.