According to analysis from auditing company Certik , by taking advantage of even the smallest loopholes on some of KyberSwap's liquidation pools, hackers have successfully exploited cross-swap to take away large amounts of money on liquidation pools. This.
On November 22, KyberNetwork suffered a Flash Loan Attack on several chain, resulting in losses of approximately $47 million. According to Certik, with this technique, it is important to understand how centralized liquidation generators work.
Automated LP essentially deploy a standard constant product curve (x*y=k) where all trading takes place.
Certik found that liquidation could be better utilized if pools were created with more tightly capped price curves. Having lots of liquidation backing within a narrow price band also effectively mitigates the risk of price slippage, as trade size would have to increase proportionally to the liquidation pool to have the same price impact.
Popularized by Uniswap v3, the centralized liquidation provider model allows LP (LPs) to add liquidation to their preferred price bands.
The result of this design, Certik points out, is that each liquidation provision position (LP) must be tracked individually because liquidation in the pool becomes unmatched. The price range can be Chia into discrete “ticks,” allowing LPs to provide liquidation between any two “ticks.” The index of a tick, denoted i, is defined as the logarithm of the corresponding price.
The net liquidation of liquidation providers and other parameters are stored in a linked list data structure.
Each of these positions, in effect, creates a user-defined price curve. By aggregating all the different positions into a single price curve, it allows a single pool to support a diversity of liquidation provider (LP) preferences.
When adding or removing liquidation to a tick range, the liquidation pool must record the amount of virtual liquidation added or released when these ticks are crossed, and how many Token were used to trigger the transaction. liquidation within that margin. Token Swap 0 for Token 1 causes the current pool price and current “tick” to move down, while Token Swap 1 for Token 0 causes the current pool price and current “tick” to move up. When performing a swap, the total amount of liquidation is added or removed as the “tick” crosses this margin to move left and right, and this is the vulnerability in KyberSwap where the attack takes place.
Simply put, this vulnerability resides in KyberSwap Elastic's computeSwapStep() implementation. This function is responsible for calculating the actual input and output amounts of the transaction that need to be subtracted or added, the swap fee to be collected, and the resulting square root value as the sqrtP function.
This function was initially called the calcReachAmount() function, and it was assumed that the hacker's transaction would not exceed the tick margin, but the mistake occurred by creating a virtual price larger than the targetSqrtP, calculated by calling the function “calcFinalPrice”. As a result, liquidation is not removed and leads to the next attack.
This example is based on an Ethereum transaction whose txhash is: 0x396a83df7361519416a6dc960d394e689dd0f158095cbc6a6c387640716f5475 .
This transaction represents six attacks all using the same method. So, Certik took the attack on the USDC-ETHX pair as an example.
1. First, hackers flash-loaned 500 ETHx from Uniswap and manipulated the KS2-RT pool ((KyberSwap v2 Reinvestment Token), which only had 2.8 ETHx, by swapping an excessive amount of ETHx. Hackers exchanged 246,754 ETHx for 32389.63 USDC, draining the pool's liquidation and increasing currentTick to 305,000.
After the swap, there are 249.5 ETHX and 13.2 USDC left in the pool.
It can be seen that the hacker initially wanted to exchange 500 ETHX for USDC, but 246,754 ETHx was enough to get 32,389.63 USDC and increase currentTick to 305,000. Meaning there was no liquidation available above the 305,000 tick margin for hackers to swap at that time, it was considered a vacuum.
2. The hacker then calls the Mint() function from the KyberSwap: Elastic Anti-Snippingposition Manager contract, to create a new liquidation pool with 16 USDC and 5.87e-3 ETHX. The “ticks” were placed in a tight range of 305,000 to 305,408, meaning the hacker created his own liquidation pool to track the “ticks” at 305,000.
The hacker then removed some of the liquidation, but left some liquidation in the tick range from 305,000 to 305,408.
3. Hackers conduct a second swap from ETHX to USDC. They exchanged 244.08 ETHX at a tick margin of 305,000 to receive 13.6 USDC and increased the tick margin to 305,408.
On the surface, this seems like a strange swap since the hacker was the only one providing liquidation between 305,000 and 305,408, but that is the premise for the next step.
4. KyberSwap uses the compute SwapStep() function to XEM whether a transaction exceeds a tick interval. Part of the function is shown below:
Here, focus on the calcReachAmount() function. This function calculates the number of Token required for a transaction if currentSqrtP (current square root price) reaches targetSqrtP .
Cerik stated that, in this transaction, the value of usedAmount is 244080034447360000000, while the amount of ETHx that the hacker entered to exchange is 244080034447359999999, one less than the value of usedAmount . The computeSwapStep() function determines that this transaction is not enough to exhaust liquidation in the current tick range and does not need to be changed to another tick range. Meaning nextSqrtP is not updated to targetSqrtP .
The calcFinalPrice function is then called to calculate the next nextSqrtP value. The important part here is that during the calculation, the swap fee is factored into the liquidation. Leading to the final price of nextSqrtP actually being higher than initially expected, specifically higher than the price at the "tick" range of 305,408.
Going back to the exchange function above, Certik says the values of sqrtP and nextSqrtP are compared to determine XEM the tick amplitude needs to be exchanged. The condition here only determines XEM sqrtP is 'equal' to nextSqrtP , and only if it is, the lower function updateLiquidityAndCrossTick() is called to add or subtract liquidation ( swapData.baseL ) and pass the margin “tick”.
At this point, sqrtP is now larger than nextSqrtP . This means that the hacker created a situation where the current price has crossed the upper limit of the tick range, but did not trigger the updateLiquidityAndCrossTick() function to reduce liquidation, leading to the existence of fake liquidation . .
5. Finally, the hacker performed a reverse transaction, exchanging USDC for ETHx, reducing the price slightly above the “tick” range of 305,408 – the upper limit of the liquidation range provided by the hacker (from 305,000 to 305,408) down slightly below the price at the “tick” range of 304,982.
When the price entered the “tick” range of 305,000 – 305,408, where the hacker provided liquidation, from a little above 305,408, the updateLiquidityAndCrossTick() function was called. Fake liquidation is added to the liquidation within the “tick” range of 305,000 – 305,408, increasing the liquidation between the values in the “tick” range compared to the actual liquidation . The hacker then converted 493,638 ETHx into 27,517 USDC within this price range (including about 250 ETHx not included in the 305,000 – 305,408 tick range). As a result, the previous costs were restored and at the same time the liquidation pool lost all USDC, Certik concluded.
6. Return the Flash Loan and complete the attack.
This process was repeated for multiple trading pairs on the KyberSwap protocol across multiple blockchains, resulting in the following losses.
POLY: 1,180,097 USD; ETH: 7,486,868 USD; OP: 15,504,542 USD; BASE: 318,413 USD; ARB: 16,833,861USD; AVAX: 23,526 USD.
The attacks confirmed by Certik came from three different Ethereum addresses (EOAs), with address 0x502 accounting for the majority of assets. Initially, address 0x502 contacted the project to inform them that they would negotiate after a period of rest. The KyberSwap team then reached out to propose a 10% reward with a deadline of 6am UTC on November 25.