KyberSwap Elastic pool 在2023年11月23日遭受攻击,损失约5470万美元。
根据 SlowMist 安全团队的情报,2023 年 11 月 23 日,去中心化交易平台 KyberSwap 遭到攻击,导致攻击者窃取了约 5470 万美元。我们立即对此事件进行了分析,以下是我们的发现。
由于 KyberSwap Elastic 池的 Reinvestment Curve(再投资曲线)功能,当 base liquidity(基础流动性)和 reinvestment liquidity(再投资流动性)都被视为实际流动性时,它使用 calcReachAmount 函数计算在规模边界处交换所需的 token 数量。此计算导致数量高于预期,导致下一个价格 sqrtP 超过边界规模的 sqrtP。该池使用不等式检查 sqrtP,导致协议未更新流动性,并且没有如预期那样通过 _updateLiquidityAndCrossTick 跨越 tick。
在深入分析之前,必须了解关于 KyberSwap 的一些关键概念,才能掌握此分析的内容。
KyberSwap 是一个链上去中心化交易平台,其特点是一种名为 KyberSwap Elastic 的新型流动性优化模型。该模型使用 Concentrated Liquidity Market Maker (CLMM)(集中流动性做市商)机制,允许 Liquidity Providers (LPs)(流动性提供者)将流动性分配到自定义价格范围。它还引入了 reinvestment curve(再投资曲线),该曲线自动为 LP 复利结算池中闲置的流动性费用。
首先,什么是 Concentrated Liquidity Market Maker (CLMM)?与 Uniswap v3 类似,流动性提供者可以在自定义价格范围内存入资金,并且他们的流动性仅在该价格范围内才会被使用。Uniswap v3 (https://blog.uniswap.org/uniswap-v3) 和 KyberSwap (https://docs.kyberswap.com/liquidity-solutions/kyberswap-elastic) 都提供了详细的文档来解释这个概念。出于本文的目的,让我们使用简化的 ETH/USDC 池示例来理解必要的知识。
来源:KyberSwap Elastic 流动性概念 (https://docs.kyberswap.com/liquidity-solutions/kyberswap-elastic/concepts/concentrated-liquidity#liquidity-tracking-lp-contributions-at-a-specific-price)
上图表示一个 ETH/USDC 池,具有三个相同的流动性位置,当前价格为 1995。在 CLMM 中,价格范围被称为 tick-range(tick 范围)。位置 1 的流动性在 1960–2020 的 tick 范围内,位置 2 的流动性在 1980–2000 的 tick 范围内,而位置 3 的流动性在 1990–2000 的 tick 范围内。
从图中可以明显看出,当前范围是流动性最理想的范围,所有三个位置都重叠在 1990–2000 的 tick 范围内。当 ETH 的价格跌至 1985 时,它将向左移动,穿过 1990 的 tick 范围。这将导致它退出位置 3 的流动性范围,但仍保留在位置 1 和 2 的范围内,从而更新新范围内的流动性并排除位置 3 的流动性。相反,当 ETH 价格上涨至 2005 时,它将向右移动,穿过 2000 的 tick 范围,此时它将排除位置 2 和 3 的流动性,同时仍保持在位置 1 的范围内。换句话说,每当价格穿过流动性边界时,流动性就会更新,无论是增加还是减少。
与 Uniswap v3 不同,KyberSwap Elastic 引入了一项名为 Reinvestment Curve(再投资曲线)的创新功能。这是一个额外的 AMM 池,积累了从用户在池中的 swap 中收取的费用,曲线支持从 0 到无穷大的价格范围。KyberSwap Elastic 将再投资曲线与原始价格曲线相结合(即,曲线是分开的,但资金保留在同一个池中),允许 LP 将其费用复利,即使价格超过其位置范围也能获得回报。
对 KyberSwap Elastic 机制有了基本的了解后,让我们分析一下攻击的步骤。
在这里,我们以交易 0x485…0f3 为例分析攻击:
1. 攻击者首先通过 AAVE 的闪电贷借入 2000 WETH,并使用 6.8496 WETH 在 KyberSwap 池中 swap 为 frxETH,导致 frxETH 价格超过所有流动性提供者位置的范围。结果,当前价格值 sqrtP(当前价格的平方根乘以 2⁹⁶)被提升到 20282409603651670423947251286016,位于 tick 110909。
2. 接下来,攻击者在指定价格范围 [110909,111310] 内添加 0.006948 frxETH 和 0.1078 WETH 的流动性。在此之后,攻击者部分移除此流动性,最终将此价格范围内的流动性数量控制为 74692747583654757908。这种操纵确保了流动性数量与攻击后续阶段的需求相符。此时,攻击者是 tick 范围 [110909,111310] 内唯一的流动性提供者,并且 tick 111310 处的价格值 sqrtP 为 20693058119558072255662180724088。
3. 之后,攻击者使用 387.17 WETH 以当前价格 tick 110909 交换 0.005789 frxETH。这种显着的 swap 将当前价格值 sqrtP 增加到 20693058119558072255665971001964,超过了边界 tick 111310 处的 sqrtP。
4. 最后,攻击者使用 0.005868 frxETH 以略高于 tick 111310 的 sqrtP 的价格反向 swap 为 396.2 WETH。swap 后,价格回落到 [110909,111310] tick 范围内。此时,攻击者已经获利,因为反向 swap 产生的 WETH 比正向 swap 中交换的 WETH 多大约 9 个。
为什么看似如此简单的攻击步骤会导致比预期更多的资金?这与 KyberSwap Elastic 的 Reinvestment Curve 密切相关。让我们深入分析,揭示攻击者获利的背后方法。
从上述步骤中,我们知道最终的反向 swap 导致了超出预期的资金。在这次 swap 时,当前 sqrtP 为 20693058119558072255665971001964,高于攻击者添加流动性的 tickUpper 111310 的价格。让我们使用一个比例图来表示这些值的位置。
由于当前 sqrtP 超过了攻击者添加的流动性 tick 范围 [110909,111310],理论上,其位置应该没有流动性。在交换过程中,价格需要向左移动,穿过 111310 tick,才能找到有效的流动性进行交换。让我们调查一下交换是否按预期进行。
如下图所示,当检查当前 sqrtP tick 的流动性时,我们发现在一个范围内的流动性数量出乎意料,如果没有考虑再投资曲线,则该范围应该没有流动性。此外,存在的流动性数量明显大于再投资曲线所建议的数量,并且与 [110909,111310] 范围内的流动性相匹配。
这意味着该 swap 将在 tick 111310 处有效执行,如下图所示。
在 tick 111310 处进行有效交换后,sqrtP 将穿过此 tick 并进入 [110909,111310] 范围以交换剩余的 token。回顾我们的预备知识,我们知道穿过流动性位置范围会触发流动性更新。在 KyberSwap Elastic 池中,这通过 _updateLiquidityAndCrossTick 函数完成,该函数将 [110909,111310] 范围内的流动性添加到曲线中以参与 token 交换,如下图所示。
这导致 [110909,111310] 范围内的有效流动性被添加到 tick 111310 右侧意外的额外流动性中,从而导致此范围内的交换期间的总有效流动性远高于预期。下图说明了这一点,显示有效流动性与预期数量相比翻了一番。
由于当前 tick 范围内流动性的增加,池深度比预期的要好,从而允许攻击者获得比预期更多的资金。这些额外的资金来自池中其他 tick 范围的流动性。
问题是为什么 tick 111310 的右侧会有意想不到的额外流动性,以及为什么它与 [110909,111310] 范围内的流动性数量相匹配。唯一的解释是,池在之前的交换过程中没有按预期更新流动性。下图从理论上显示了在之前的交换过程中,当穿过 tick 111310 时,应该调用 _updateLiquidityAndCrossTick 函数,从而在 sqrtP 进入 tick 111310 的右侧后更新流动性。
在我们对该交换过程的实际分析中,池使用 computeSwapStep 函数计算用于交换的实际金额、交换费用和新的 sqrtP 价格值。理论上,当穿过流动性范围时,计算出的 sqrtP 结果应落在边界 tick 111310 的 sqrtP 上。但是,实际的新 sqrtP 超过了 tick 111310 的 sqrtP。下图显示 tick 111310 的 sqrtP 为 20693058119558072255662180724088,但实际的 sqrtP 最终为 20693058119558072255665971001964。
新的 sqrtP 未落在边界 tick 111310 的 sqrtP 上,这意味着 swapData.sqrtP != swapData.nextSqrtP,这导致池认为当前 sqrtP 仍然在 [110909,111310] 范围内。这导致池跳过了流动性检查操作,并且没有触发 _updateLiquidityAndCrossTick 函数进行流动性更新!
但为什么新的 nextSqrtP 没有落在边界上呢?对 calcReachAmount 计算的分析表明,swap 数量 387170294533119999999 略小于当前范围内的流动性数量 387170294533120000000。
这导致 nextSqrtP 未被分配 targetSqrtP 的值,而是保持为 0。因此,最终的 sqrtP 计算是直接使用 calcFinalPrice 函数完成的,从而导致计算结果超过了 tick 111310 处的 sqrtP。
因此,calcReachAmount 函数至关重要,因为它计算了在 swap 中从 currentSqrtP 到 targetSqrtP 所需的 token 数量。通过分析其公式,我们可以看到结果主要取决于当前流动性 L,它是基础流动性和再投资流动性的总和。
鉴于 Uniswap v3 没有再投资曲线的特性,那么包含再投资流动性是否是 calcReachAmount 计算导致数量大于预期的原因呢?
经过测试,发现如果不包括再投资流动性,则 calcReachAmount 计算结果为 387160697969657129472,小于攻击中使用的 swap 数量 387170294533119999999。
此外,如果没有再投资流动性,则 computeSwapStep 函数计算出的 sqrtP 正好落在 tick 111310 上:
因此,真相变得清晰。由于 KyberSwap Elastic 的 Reinvestment Curve 功能,当使用基础流动性和再投资流动性计算从当前 sqrtP 到边界 sqrtP 的 swap 所需的 token 数量时,所需的 token 数量大于预期。这导致 swap 后的 sqrtP 超过边界 sqrtP,从而导致协议认为当前 tick 范围内的流动性已满足 swap 要求,从而停止边界所穿过的 tick 的流动性更新过程。
KyberSwap 攻击者地址
1. KyberSwap 攻击者 1: 0x50275e0b7261559ce1644014d4b78d4aa63be836
2. KyberSwap 攻击者 2: 0xc9b826bad20872eb29f9b1d8af4befe8460b50c6
3. KyberSwap 攻击者 3: 0xae7e16cAa7a4d572FfF09924Bf077a89485850Cb
4. KyberSwap 攻击者 4: 0xd01896e3D4F130Ffd6f6a5A9d6780bbd7008d71d
根据我们对 AML 平台 MistTrack 的分析,KyberSwap 攻击者总共窃取了超过 5470 万美元,其活动遍及多个区块链网络,包括 Ethereum、BSC (Binance Smart Chain)、Arbitrum、Optimism、Polygon、BASE、Scroll 和 Avalanche。
在 Ethereum 网络上,KyberSwap 攻击者 1 的初始资金来源于从 Tornado Cash 转移的 20 ETH。其中,0.1 ETH 被转移到 KyberSwap 攻击者 2,2 ETH 被转移到 FixedFloat。此外,6.5 ETH 被拆分并转移到各个链,包括 Arbitrum、Optimism、Scroll 和 BASE。与此同时,KyberSwap 攻击者 2 设法窃取了超过 758 万美元的 token,包括 USDC、WETH、KNC 等,这些 token 尚未移动。
在 Binance Smart Chain (BSC) 上,KyberSwap 攻击者 1 从 FixedFloat 收到了 4.2678 BNB 作为余额,该余额保持不变。
在 Arbitrum 上,KyberSwap 攻击者 2 窃取了超过 2029 万美元的 token,包括 WBTC、WETH、ARB、DAI 等。其中,500 WETH 被转移到 0x98d69d3ea5f7e03098400a5bedfbe49f2b0b88d3,然后它将 300 WETH 跨链移动到 Ethereum,并且保持不变。值得注意的是,KyberSwap 攻击者 2 向 Indexed Finance 攻击者的地址 0x84e66f86c28502c0fc8613e1d9cbbed806f7adb4 发送了 1,000 WETH。
在 Optimism 上,KyberSwap 攻击者 2 窃取了超过 1564 万美元的 token,包括 wstETH、WETH、OP、DAI 等,这些 token 也未被触及。
在 Polygon 上,KyberSwap 攻击者 1 的初始资金来自从 FixedFloat 转移的 2,666.1243 MATIC。然后将 100 MATIC 发送到 KyberSwap 攻击者 2,为攻击者 1 留下 2,564.0016 MATIC 的余额。KyberSwap 攻击者 2 窃取了超过 293 万美元的 token,包括 WBTC、WETH、DAI 等,并且也保持不变。KyberSwap 攻击者 3 窃取了超过 575 万美元的 token,包括 wstETH、USDT、USDC 等,并将这些 token 中的大部分转移到地址 0xa4c92d7482066878bb1e2c0510f42b20d79a7ea9。
在 BASE 上,KyberSwap 攻击者 2 窃取了超过 195 万美元的 token,包括 USDC、WETH 等,并且保持不变。
在 Avalanche 上,KyberSwap 攻击者 1 的初始资金来自从 FixedFloat 转移的 49 AVAX。KyberSwap 攻击者 2 窃取了超过 23,500 美元的 token,包括 293.0756 WAVAX、17,316.0305 USDC,这些 token 保持不变。KyberSwap 攻击者 4 获得了超过 565,000 美元的 token,包括 WAVAX、USDC 等,并将 USDC 转移到地址 0x9296fa3246f478e32b05d4dde35176d927be703f。
SlowMist 安全团队已将相关地址列入黑名单,并且大部分资金尚未转移。我们将继续监控资金动向并提供有关此案例的更新。
此次攻击的根本原因在于计算从当前价格到边界 tick 价格的 swap 所需的 token 数量。由于 KyberSwap Elastic 的 Reinvestment Curve,流动性被复利结算的费用无意中增加,导致计算出的数量大于预期。这种过剩的数量满足了用户的交换需求,但实际价格已经穿过了边界 tick。因此,协议错误地认为当前 tick 范围内的流动性足以满足交换需求,因此没有更新流动性。这种疏忽导致了跨边界 tick 的反向交换期间流动性增加的重复,从而允许攻击者获得比预期更多的 token。
SlowMist 安全团队建议,在设计经济模型时,应彻底测试边界条件,并严格评估流动性和价格计算,而不是依赖于不等式检查。
参考
- 攻击者地址: 0x50275e0b7261559ce1644014d4b78d4aa63be836
- 攻击合约: 0xaf2acf3d4ab78e4c702256d214a3189a874cdc13
相关攻击交易:
在 SlowMist,我们以成为区块链安全领域的先行者而自豪,多年来致力于掌握威胁情报。我们的专业知识基于为不同的客户提供全面的安全审计和先进的反洗钱追踪。我们已经建立了一个强大的威胁情报协作网络,将自己定位为全球区块链安全领域的重要参与者。我们提供量身定制的安全解决方案,从识别威胁到实施有效的防御机制。这种全面的方法赢得了全球众多领先和知名的项目(包括 Huobi、OKX、Binance、imToken、Crypto.com、Amber Group、Klaytn、EOS、1inch、PancakeSwap、TUSD、Alpaca Finance、MultiChain 和 Cheers UP 等)的信任。我们的使命是确保区块链生态系统不仅具有创新性,而且安全可靠。
SlowMist 提供各种服务,包括但不限于安全审计、威胁信息、防御部署、安全顾问和其他安全相关服务。他们提供 AML(反洗钱)软件、Vulpush(漏洞监控)、SlowMist Hacked(加密黑客档案)、FireWall.x(智能合约防火墙)、Safe Staking 和其他 SaaS 产品。他们与国内外公司建立了合作伙伴关系,例如 Akamai、BitDefender、FireEye、RC²、TianJi Partners、IPIP 等。
通过提供针对各个项目的全面安全解决方案,他们可以识别风险并防止其发生。他们的团队能够发现并发布多个高风险区块链安全漏洞。通过这样做,他们可以传播意识并提高区块链生态系统中的安全标准。
- 原文链接: slowmist.medium.com/a-de...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!