梅开二度 —— PancakeBunny 被黑分析
By:Kong@慢雾安全团队
据慢雾区消息,2021 年 07 月 17 日,DeFi 收益聚合器 PancakeBunny 在 Polygon 上的版本遭受闪电贷攻击,慢雾安全团队第一时间介入分析,并将结果分享如下。
值得注意的是本次攻击与 5 月 20 日 PancakeBunny 在币安智能链上的版本遭受的闪电贷攻击类似。此前慢雾安全团队也进行了简要分析,具体可查看:代币闪崩,差点归零 - PancakeBunny 被黑简析。
攻击细节分析
首先攻击者从 AAVE 中闪电贷借出大量的 USDC、USDT、WETH 代币,并将借来的 USDC 与 USDT 代币转入 SushiSwap 中添加流动性以获得 SLP。
随后攻击者进行了本次攻击最为关键的操作:将添加流动性获得的 LP 的一小部分抵押至 VaultSushiFlipToFlip 合约中,为获得 polyBUNNY 代币奖励做准备。而其余大部分 LP 都抵押到 MiniChefV2 中,并将存款收益地址指定为 VaultSushiFlipToFlip 合约。
接下来为了减少攻击成本,使用借来的 100,000 个 WETH 代币,在 QuickSwap 中将其兑换成 WMATIC 代币 (在后面的分析将会说明为何这样会减少攻击成本)
随后攻击者调用了 VaultSushiFlipToFlip 合约的 withdrawAll 函数,以获取抵押的 SLP 与 polyBUNNY 代币奖励。我们切入此函数进行具体分析:
在上图代码第 162 行与 163 行,分别通过 balanceOf 函数与 principalOf 函数获取攻击者抵押凭证换算成用户通过 VaultSushiFlipToFlip 合约在 MiniChefV2 中抵押的 SLP 数量以及用户在 VaultSushiFlipToFlip 合约中抵押时记录的 SLP 数量。理论上这两个数量不应相差太多,但通过分析 balanceOf 函数我们发现:其是通过 balance 函数获取 VaultSushiFlipToFlip 合约在 MiniChefV2 中抵押的 SLP 数量与用户持有的凭证占比相乘得到 amount 的。
由于攻击者已提前将大量的 SLP 未通过 VaultSushiFlipToFlip 合约直接在 MiniChefV2 合约中抵押并将受益地址指给 VaultSushiFlipToFlip 合约,因此 balance 函数将会获取到一个比预期大得多的值,而攻击者的凭证占比却又是正常的,最后导致赋给 amount 的是个比预期大得多的值。这就直接导致了在上图代码第 176 行计算出的 performanceFee 参数是个非预期的巨大值。
随后将此异常的 performanceFee 参数传入 mintForV2 函数中参与铸造 polyBUNNY 代币奖励的计算,我们切入此函数进行分析:
我们可以发现其通过调用 mintFor 函数进行具体的逻辑处理,首先在上图代码第 205 行将 SLP 发送给 Pair 合约,为后续移除流动性做准备。由于 shouldMarketBuy 函数最终价格比较计算为 false,因此将通过 if 逻辑调用 _zapAssets 函数。我们切入 _zapAssets 函数继续分析:
在 _zapAssets 函数,由于 asset 为 SLP 因此将通过 else if 逻辑先通过调用 SushiSwap Router 合约的 removeLiquidity 函数进行移除流动性,随后通过 _tokenToAsset 函数将移除流动性获得 USDC 与 USDT 代币分别在 QuickSwap 中兑换成 polyBUNNY 与 WETH 代币并在 QuickSwap 中添加流动性。接下来我们对 _tokenToAsset 函数进行分析:
通过简单分析我们可以发现,其将通过 if 逻辑直接调用 zapPolygon 合约的 zapInToken 函数,这里不再赘述判断条件的分析。接下来跟进 zapInToken 函数分析:
同样通过对判断条件的简单分析,我们可以指定其将通过上图代码第 133 行进入 else 逻辑,接下来调用 _swapTokenForMATIC 函数通过下图代码第 221 行 if 逻辑设定的代币兑换路径 “USDC -> WETH -> WMATIC",USDT 代币的兑换路径同样如此。
由于在此之前,攻击者已经使用借来的 100,000 个 WETH 代币在 QuickSwap 中 通过 “WETH -> WMATIC” 将其兑换成大量的 WMATIC 代币,所以上述通过 “USDC/USDT -> WETH -> WMATIC” 路径兑换到的 WMATIC 代币变得很少,因此接下来通过 _swapMATICToFlip 函数将 WMATIC 代币兑换成的 WETH 与 polyBUNNY 代币就会较少,导致最后转给 BUNNY_POOL 的 LP 会较少,达到减少消耗攻击者付出的 SLP 目的,最终减少了一部分攻击成本。
接下来我们再看 mintFor 函数,在完成上述一系列操作后,将通过 amountBunnyToMint 函数计算需要铸造的 polyBUNNY 代币数量,而通过下图我们可以看出参与计算的 contributionInETH 参数来自于 priceCalculator 合约的 valueOfAsset 函数,但 valueOfAsset 函数接收的参数却是我们先前所说的 _performanceFee 参数,这是一个非预期的巨大值。
我们可以对 valueOfAsset 函数进行简单的分析:
由于 asset 是 SLP,因此将通过上图代码第 155 行调用 _getPairPrice 函数,通过对此函数进行分析我们可以发现,参与价格获取的途径都为可信的预言机,因此攻击者无法直接操控预言机进行攻击。但由于参与计算的 amount 即 _performanceFee 是个非预期的巨大值,因此最终通过乘法计算得出的 valueInETH 与 valueInUSD 都是非预期的巨大值。
随后会将获得的非预期的 contributionInETH 参数传入 amountBunnyToMint 函数进行铸币数量计算。
而这里的 priceOfETH 与 priceOfBunny 都取自可信的 ChainLink 与 官方自行喂价的预言机,因此最终经过乘法计算后,得到的铸币数量 mintBunny 是个非预期的巨大值。导致最终攻击者获得了大量的 polyBUNNY 代币奖励。
攻击流程
1. 攻击者在 AAVE 闪电贷借出 USDT/USDC/WETH 代币,并在 SushiSwap 中添加流动性获得 SLP。
2. 将大部分的 SLP 转入 MiniChefV2 中并将受益地址指定给 VaultSushiFlipToFlip 合约,小部分抵押到 VaultSushiFlipToFlip 合约中。
3. 由于攻击者的大部分抵押到 MiniChefV2 中的 SLP 受益地址指定给 VaultSushiFlipToFlip 合约,导致 VaultSushiFlipToFlip 获取到错误的 balanceOf 造成 performanceFee 变成一个非预期的巨大值。
4. 最终铸造 polyBUNNY 代币奖励使用了此非预期的 performanceFee 参数,导致额外铸造了大量的 polyBUNNY 代币给攻击者。
5. 最后攻击者抛售 polyBUNNY 代币,归还闪电贷,获利走人。
总结
此次攻击与第一次不同的点在于:并非是预言机被操控导致铸币数量错误,而是 VaultSushiFlipToFlip 合约获取在 MiniChefV2 中抵押的抵押品数量时,错误地获取了攻击者未通过 VaultSushiFlipToFlip 抵押的抵押品数量,导致参数铸币的参数异常。最终铸造出额外的 polyBUNNY 代币。
参考交易链接:
https://polygonscan.com/tx/0x25e5d9ea359be7fc50358d18c2b6d429d27620fe665a99ba7ad0ea460e50ae55
往期回顾
假币的换脸戏法 —— 技术拆解 THORChain 跨链系统“假充值”漏洞
慢雾导航
慢雾科技官网
https://www.slowmist.com/
慢雾区官网
https://slowmist.io/
慢雾 GitHub
https://github.com/slowmist
Telegram
https://t.me/slowmistteam
https://twitter.com/@slowmist_team
Medium
https://medium.com/@slowmist
币乎
https://bihu.com/people/586104
知识星球
https://t.zsxq.com/Q3zNvvF
火星号
http://t.cn/AiRkv4Gz
链闻号
https://www.chainnews.com/u/958260692213.htm