Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

Rate manipulation in Balancer Boosted Pools — technical postmortem- 1278

JuaniPosted 2 Years Ago
  • Balancer V2 is a key lending and borrowing protocol with lots of interesting functionality. Within V2, arbitrary contract is capable of being a vault; this is to maximize innovation and flexibility. The batchSwap() function can be used to perform multiple swaps atomically to get the best path. This also contains a flash swap by only having to pay for the funds at the end.
  • Balancer was trying to be as capital efficient as possible. In a pool, the ratio is what calculates the price of the token. To have stability, lots of tokens are required; this ends up with a large amount of idle tokens that are doing nothing useful. They tried a few different things to fix this but settled on linear/boosted pools.
  • Lending protocols have an underlying token in exchange for the platform/LP token, such as aTokens on Aave. From this, users earn yield for liquidity. Since these are always rebasing, a wrapped version of these tokens in a Balancer vault is used. To prevent the constant wrapping and unwrapping of assets, they created linear pools.
  • Within a linear pool, there is a third token: the Balancer Pool Token (BPT). Pools that contain tokens that are also pools themselves are called composable or recursive. What's cool about this is that the BPT can be swapped like any other token in the pool itself.
  • Now, for the vulnerability! The issue existed in the common library ScalingHelpers. In DeFi security, the rounding direction is critical to get right for security. Any rounding errors should always favor the pool. To be efficient, they decided to always round down and expect that the consequence would be minimal.
  • This was true for everything except linear pools. A bunch of crazy things coming together made the rounding error significant:
    • Linear pools have zero fees when balanced and no minimum balances.
    • Initialized with pre-minted BPT, creating a near infinite supply. This is available for flash swap operations.
  • Why is this significant? The batch swaps settle at the end. Individual swaps perform calculations on scaled balances - including rates - which depend on the intermediate pool state. Since this doesn't work based upon the vaults pool balances, the math is deeply effected. There is a quick and dirty attack path:
    • Borrow BPT via a flash swap with the rate slightly greater than 1 and trade it for main and wrapped to reduce the token balances to near zero.
    • Craft a trade that exploits the rounding error from above to make the total balance equal the virtual supply. This will result the rate to 1.
    • Repay the flash swap at a new lower rate for a profit.
    This can also be done on the main and wrapped tokens as well.
  • This is where the story gets wild. While trying to get people to take the funds out of the paused and effected pools, a different vulnerability was found! The exploit from before dropped the rate; they found a rounding error to increase the rate. This exploits some decimal precision and rounding issues described above. When the rate is high, the BPT trades at a premium within the Composable Stable pool.
  • Raising the rate was fine because they couldn't get the rate back to profit from it; this was discussed during the design. However, the attacker found a way to drop the rate back down. During the initialization stage of a pool, the check is that the total supply is zero. By using the methods from above, this condition is possible to hit, recreating the initialization scenario. With this, the attacker could profit from the attack.
  • Trying to mitigate this was interesting: it's tough being a "fully decentralized" protocol. You want to be able to shut stuff off but you shouldn't be able to with fully decentralizion. Some items had a pause function, some were upgradable and a recovery mode. But, these weren't implemented in everything.
  • At the end of the article, the author reflects on many things. First, they had several audits done and this bug lurked for over 2 years without getting discovered by a whitehat. The complexity of the protocol became too much. In particular, the bootstrapping of functionality over and over again in strange ways. Overall, a fascinating postmortem about an immensely important protocol.