Doug Colkitt Profile picture
Nov 23 30 tweets 10 min read Twitter logo Read on Twitter
1/ Finished a preliminary deep dive into the Kyber exploit, and think I now have a pretty good understanding of what happened.

This is easily the most complex and carefully engineered smart contract exploit I've ever seen...
2/ First thing to note is this exploit is specific to Kyber's implementation of concentrated liquidity

There's no reason to believe that other reputable concentrated liquidity dexes, like Ambient or Uniswap, are at risk from this exploit. (Though Kyber forks obviously are)
3/ We'll look at the first pool the attacker drained on Ethereum, ETH/wstETH. Though all of the other pools followed a similar strategy.

The attack on this pool can be found in this transaction:

etherscan.io/tx/0x09a3a12d5…
4/ This particular transaction drained three separate pools, but for now we'll only look at the ETH/wstETH drain.

The process to drain each pool is independent, so we only need to understand one. Each pool exploit is carried out in a flash loan to manipulate price and liquidity Image
5/ In the wstETH/ETH case, the exploit started with a flash loan of 10 thousand wstETH (worth $23 million)

The next step was to swap 2800 wstETH ($6mn) into the pool to push the price from 1.05 ETH to 0.0000152. Unlike most flash loans, the reason wasn't to manipulate an oracle
Image
Image
6/ The point was to move the pool price to an area of the concentrated liquidity curve, where there was 0 existing liquidity. Since the attack relied on an extremely precise manipulation of Kyber's concentrated liquidity math, this basically created a "fresh canvas".
7/ The exploit mints 3.4 wstETH of liquidity in the price range of 0.0000146 to 0.0000153. Then the exploiter burns 0.56 wstETH of liquidity for some reason (probably to make the subsequent numerical calculations line up perfectly).

Image
Image
Image
8/ The exploit executes two swaps around this price. Now remember there is no other liquidity here. In the absence of a numerical bug, someone doing this would just be trading back and forth with their own liquidity, and all the flows would net out to zero (minus fees)
9/ However what happens is an infinite money glitch.

The first swap is the exploiter selling 1056 wstETH for 0.0157 ETH pushing the price down to 0.0000146 (just barely below his liquidity's price range).
Image
Image
10/ The second swap is in the opposite direction. The exploiter buys 3911 wstETH from the pool for 0.06 ETH pushing the price of the pool back up to 0.00001637 (a bit above the upper edge of his liquidity range. Image
11/ And that's it. The exploit is complete. The pool is drained

Notice how in the second swap he received more money than he paid in the first (3911 vs. 1052 wstETH). Remember the only liquidity here is the ~3 wstETH he minted at the outset. Where did the extra money come from?
12/ This is where things get really tricky. It took me hours to even figure out what happened.

The first clue came from looking at the resting state of the pool's liquidity at the end of the second swap. Notice anything weird? Image
13/ In Kyber the value of the resting in-range liquidity is in the `baseL` poolData variable.

Shouldn't this be 0? Remember the second swap ended at a price *outside* the attacker's liquidity range. There should be no liquidity here.
14/ Somehow the exploit was able to make the pool think it had more liquidity than it actually did at these price ranges. And now we understand where the infinite money glitch comes from

If the pool thinks there's more liquidity than there actually is, it overpay for large swaps
15/ The second clue comes from comparing the call trace stack for the first and second swap.

In Kyber when tick boundaries are crossed, the `updateLiquidityAndCrossTick` is invoked. It adjusts the curve's liquidity value based on the LP range positions at that tick
Image
Image
16/ The second swap handles it correctly. It starts by being out of range above the LP position. updateLiquidityAndCrossTick is called once when the price moves in range. Then the swap ends at a price below the LP position. So it's called again as the price moves out of range
17/ However in the first swap updateLiquidityAndCrossTick is *never* called. Remember the curve price started in range, then the swap moved the price until it was just slightly out of range (carefully note "slightly")

It should have been called, but never was on swap 1
18/ And now the pieces are falling into place.

When an LP position moves out of range updateLiquidityAndCrossTick is responsible for removing that liquidity from the curve. When it moves back in range, it adds the liquidity back into the curve.

What would happen if it broke?
19/ If you could get it not to invoke when your LP position moved out of range, liquidity would never be removed from the curve. You've now tricked the pool into thinking it has more liquidity then it does.
20/ But when you move back in range, you make sure it invokes. And the liquidity gets added back in, even though it was never removed the first time. You're double dipping! The pool is double counting the liquidity from the original LP position.

The infinite money glitch
21/ How was the exploiter able to bypass the call to updateLiquidity on swap 1? This is where things get really technical

In concentrated liq AMMs, swaps are calculated as a series of steps. At each step you have to determine if you'll reach a tick boundary or exhaust the swap
22/ Kyber runs this swap step, and it checks to see if the ending price of the step is the same as the next tick price. If it is isn't it assumes the swap exhausted, it didn't reach the tick boundary and `updateLiq` doesn't need to be called.
Image
Image
23/ However note the check is inequality, not directional comparison. If you were somehow able to execute a swap step and get the price to end *outside* the tick boundary, the check would fail and `updateLiquidity` would never be called, even though you crossed a tick boundary
24/ Normally this shouldn't happen because `computeSwapStep` function first calculates an upper limit of the amount that can be swapped before reaching the tick

If that amount is less than remainder of the swap, it confidently predicts the ending price will *not* reach the tick Image
25/ However in this case something funny happened. calcReachAmount predicted the swap quantity would not reach the tick boundary, yet somehow the ending price ended just slightly *beyond* the tick boundary.
Image
Image
26/ And that's because the "reach quantity" was the upper bound for reaching the tick boundary was calculated as ...22080000, whereas the exploiter set a swap quantity of ...220799999

That shows just how carefully engineered this exploit was. The check failed by <0.00000000001% Image
27/ This has to do with how Kyber implements the quantity calculation (for the upper limit until a bounds is hit) and the price change. The two use very slightly different arithmetic.
Image
Image
28/ In a very carefully controlled and precisely engineered case, the bounds check will tell you that anything less than X swap qty will keep you inside the tick price.

But the parallel calculation price change calculation will apply X swap qty and wind up outside the tick bound
29/ I remember being endlessly paranoid about this when writing the smart contracts for @ambient_finance

For that reason, our code includes explicit checks on every step that were explicitly inside the tick boundary if the swap quantity exhausts. Image
@ambient_finance 30/ The good news at least, it would be pretty straightforward to patch the existing Kyber contracts with a similar assertion on the swap step to prevent this exploit in the future.

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with Doug Colkitt

Doug Colkitt Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @0xdoug

Apr 6
1/ How to lie with statistics advanced edition...

This meta-study claims to overturn the long established pattern that moderate drinkers have lower mortality than abstainers. After adjusting for confounders there was "no significant reduction in mortality". Very misleading... ImageImage
2/ At the bottom of the chart, you see moderate drinkers have a 0.86 RR (i.e. 13% lower mortality). The 95% confidence interval is 0.83-0.88. This is extremely significant

This study then throws in a kitchen sink of confounder variables. Everything from BMI to publication year
3/ If you look at the top of the chart, after all those adjustments are made, moderate drinkers do look not quite as healthy as before. The RR moves about closer to one at 0.93 (i.e. 7% lower mortality)

However even after all that, moderate drinkers *still* look healthier
Read 11 tweets
Mar 7
1/ A thing I keep hearing more and more is that RFQs are always better for swappers. This is wrong

We can all agree that the RFQ provider has a free option. The value of that option comes at the expense of someone. Most people think it comes entirely from the passive LPs. Not so
2/ The simple mechanics behind pre-chain RFQs is that the RFQ provider is given "first look" at the swap intention. The RFQ provider is given the option to fill at a better price than the indicative on-chain AMM price. If the RFQ provider passes the swap is routed to the AMM.
3/ Surely this must be better from the swapper's perspective. The swapper can only get price improvement, and the worse case is just the base case (swap against the AMM), without the RFQ. Right?

Wrong. The key distinction is between indicative AMM price and true arrival price
Read 16 tweets
Jan 20
1/ “Yes, it probably does need a token…”

Recently it’s become fashionable in crypto circles to be critical of that app-layer governance tokens. Builders are encouraged to build public good and carefully think twice whether adding a token is really necessary

This is psyops…
2/ At best the notion is misguided. At worse, it’s proffered by L1 bag holders, cynically trying to retain all value accrual at the chain layer, leaving the app layer out in the cold

Regardless it’s near impossible to build sustainably decentralized apps without a token
3/ Anything beyond the simplest protocol is going to require some level of governance that can’t be reduced to an autonomous algorithm.

For example lending protocols have to constantly and intelligently update collateralization parameters as market conditions shift.
Read 10 tweets
Nov 17, 2022
1/ There's a lot of debate and speculation on crypto Twitter about how Alameda managed to lose so much money. But this may give a false impression of ambiguity in other aspects

There's one thing that's unambiguously and indisputably true. SBF and Alameda committed fraud. Period.
2/ For anyone who's been close to the chaos, this will seem so obviously true that it may seem laughable that I even have to make a thread to drive the point home. Not a single credible voice in the industry would tell that this wasn't naked, malicious and criminal fraud
3/ But Sam is engaging in a coordinated attempt to whitewash his crimes by painting a picture. It's a picture that many in the finance industry will quickly pattern match to. A picture of a stereotypical over-leveraged over-confident hedge fund where risk gets out of control Image
Read 27 tweets
Nov 11, 2022
1/ Very rough and speculative sketch of what I increasingly think happened at FTX as more info comes out…

The central question is where did the money go? Yes malfeasance and fraud is necessary, but at one point in the cycle cash actually has to go out the door
2/ At 3AC we knew it went to losses on leveraged long positions. At Lehman it went to bad mortgages. At Enron it went to boondoggle mega projects.

Some FTX money obviously went to seed rounds in bad or illiquid projects. But AFAICT nowhere near enough to explain the hole.
3/ Let’s rewind to 2017/18. Alameda the prop firm is a big fish in a little pond. They’re mediocre traders (there’s a video of SBF bragging about how their quoter latency is down to something like two seconds). But crypto is still a weird asset class that most won’t touch
Read 23 tweets
Oct 14, 2022
1/ IMO settling with the Mango exploiter was the correct move. It's very unlikely the exploiter would have been criminally prosecuted, even if they were doxxed.

To understand why, it's important to distinguish "computer fraud" from "securities fraud".
2/ The vast majority of hackers (crypto or otherwise) are prosecuted under the Computer Fraud and Abuse Act. Computer fraud is very easy to prosecute, and US Attorneys are very comfortable bringing cases, having a clear template for prosecution.
3/ But... computer fraud requires some type of "breach" or "unauthorized access" to a computer system

SCOTUS clarified in Van Buren that simply using the authorized part in an unauthorized way is insufficient. You have to explicitly touch a part of the system that is off-limits
Read 10 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us on Twitter!

:(