风险提示:央行等十部委发布《关于进一步防范和处置虚拟货币交易炒作风险的通知》, 请读者提高风险意识。

Balancer 漏洞分析

本文将对 Balancer 官方于 8 月披露的漏洞细节进行复盘。

慢雾科技
慢雾科技
热度 ...
 

背景

8 月 22 号,Balancer 官方发布公告表示收到影响多个 V2 Boost 池的严重漏洞报告,只有 1.4% 的 TVL 受影响,多个池子已暂停,并通知用户尽快提取流动性 LP。[1] [2]

8 月 27 号,慢雾 MistEye 系统发现疑似 Balancer 漏洞被利用的攻击交易。[3]

由于池子无法暂停,一部分资金仍然受到攻击的影响,Balancer 官方再次提醒用户将受影响池子中的 LP 取回。[4] 随后,Balancer 官方于 Medium 发布了 8 月披露的漏洞细节 [5],慢雾安全团队对其进行复盘,详情如下:

引入

Balancer 官方在其披露中简单指出此次的问题在于,线性池的向下舍入以及可组合池的虚拟供应量导致 bptSupply 为 0。首先让我们来简单了解一下与这次漏洞相关的 Balancer 协议中的内容。

Balancer V2 Vault

Balancer V2 [6] 协议是一种基于以太坊的去中心化自动做市商(AMM)协议,它代表了可编程流动性的灵活构建块。其核心组件是 Vault 合约,该合约维护着所有池子的记录,并管理代币的记账和转移,甚至包括原生 ETH 的包装和解包。也就是说,Vault 的实现是将代币记账和管理与池子逻辑分开。

Vault 中有四个接口,分别是 joinPool, exitPool, swap 和 batchSwap(加入、退出和交换是分开的调用,不存在单次调用时的组合)。其中一个突出的特点是 batchSwap,它能实现多个池子之间多次原子交换,将一个池子交换的输出与另一个池子的输入相连(GiveIn 和 GiveOut)。该系统还引入了闪电交换 [7],类似于一个内部的闪电贷。

Linear Pools 线性池

Balancer 为了提高 LP 的资本效率及 warp 和 unwarp 高额开销的问题,在 V2 中推出线性池作为解决方案,由此引入了 BPT (ERC20 Balancer Pool Token) 代币。

线性池 [8] 包含 main token(底层资产),warpped token(包装代币)和 BPT 代币,通过已知汇率交换资产及其包装的、具有收益的对应物。包装代币的比例越高,收益率和资金池的资本效率就越高。在 warp 的过程中,通常都会通过缩放因子来确保不同代币以相同的精度进行计算。

Composable Pools 可组合池

所有的 Balancer 池都是可组合池,池子包含其他代币,池子本身也有自己的代币。其中 BPT 币指的是 ERC20 平衡池代币,是所有池的基础。用户可以在其他池内使用 BPT 代币自由组合进行兑换。兑换总是涉及一个池和两个代币:GiveIn 和 GiveOut。In 代表送入成分代币并接收 BPT,而 Out 意味着送入 BPT 并接收成分代币。如果 BPT 本身就是成分代币,它就可以像其他代币一样进行交换。这样的实现构成了外部池中的基础资产和代币之间的一个简单 batchSwap 路径,用户可以用 BPT 交换到线性池的底层资产,这也是 Balancer Boosted Pool [9] 的基础。

通过以上的组合,Balancer 的可组合池就形成了。一个 bb-a-USD 可组合稳定池由三个线性池组成,同时向外部协议(Aave)发送闲置流动性。例如,bb-a-DAI 是一个包含 DAI 和 waDAI(包装的 aDAI)的线性池。当用户需要进行 batchSwap 时(如要将 USDT 换成 DAI),交换路径举例如下:


1. 在 USDT 线性池中,将 USDT 兑换 bb-a-USDT(进入 USDT 线性池);

2. 在 bb-a-USD 中,bb-a-USDT 兑换 bb-a-DAI(线性 BPT 之间的交换);

3. 在 DAI 线性池中,bb-a-DAI 兑换为 DAI(退出 DAI 线性池)。

简单了解过以上前置知识后,我们进入漏洞分析环节。

分析

在 8 月 27 号时,慢雾安全团队收到 MistEye 系统识别,一笔疑似 Balancer 漏洞的在野利用发生。交易 [3] 如下:

攻击者首先从 AAVE 通过闪电贷借出 300,000 枚 USDC。接着调用 Vault 的 batchSwap 操作,通过可组合稳定池 bb-a-USD 池进行 BPT 代币的兑换计算,最终将 94,508 枚 USDC 兑换为 59,964 枚 bb-a-USDC,68,201 枚 bb-a-DAI 和 74,280 枚 bb-a-USDT。最后将获得的 BPT 代币通过 Vault 合约的 exitPool 退出池子换取底层资产,偿还闪电贷,并获利约 108,843.7 美元离场。

由此可见,这次攻击的关键在 batchSwap 里,而 batchSwap 中具体发生了什么呢?我们深入了解一下。

攻击者在整个 batchSwap 过程中,先在 bb-a-USDC 池中兑换出 USDC ,接着进行 BPT 代币间的兑换,将 bb-a-USDC 兑换为 bb-a-DAI,bb-a-USDT 和 USDC。最后再将底层的 main 代币 USDC 兑换为 bb-a-USDT。也就是说,bb-a-USDC 作为关键的 BPT 代币充当 GiveOut 和 GiveIn 的成分代币。

攻击者在第一步以固定的缩放因子在 bb-a-USDC 线性池中用 BPT 代币兑换出 USDC main代币,其增加的数量记录在池子中的 bptBalance 中。但是在第二次 onSwap 的兑换后,我们发现,同样的兑换过程,兑换出 USDC 的 amountOut 值却是 0。这是为什么呢?


深入 onSwap 函数,我们发现在这个过程中会先做一次精度处理 nominal 化并计算出对应代币的缩放因子。而在接下来调用 _downscaleDown 函数时,amountOut 存在向下舍入的情况。如果 amountOut 和 scalingFactors[indexOut] 之间的值相差很大,计算出的 _downscaleDown 值就为零。

MarsBit专栏精选

也就是说当我们使用 BPT 代币来兑换 main 代币时,如果 amountOut 过小,返回值将向下舍入为零,且这个值就是小于由 scalingFactors 所计算来的 1e12。但 amountIn 进来的 bb-a-USDC 数量仍然会加入到 bptBalance 虚拟数量当中,而此操作会增加 bb-a-USDC 池子中的余额,可以将其看作为单边添加 bb-a-USDC 流动性。

MarsBit专栏精选

MarsBit专栏精选

接着利用可组合稳定池的特性,通过 BPT 代币之间的相互转换,首先将 bb-a-USDC 兑换为其他 BPT 代币。跟进这个兑换过程,可组合稳定池的以下调用路径 bb-a-DAI onSwap -> _swapGivenIn -> _onSwapGivenIn 先将 bb-a-USDC 依次换成 bb-a-DAI 和 bb-a-USDT。与在线性池中不同的是,可组合稳定池在进行 onSwap 操作之前需要进行汇率的缓存更新。从代码中我们可以看到,在组合池中,onSwap 会先判断是否需要更新缓存的 token 兑换率。

MarsBit专栏精选

关于【Balancer 漏洞分析】的延伸阅读

MarsBit专栏精选

经过之前的兑换,bb-a-USDC 的数量发生了改变,并通过 _toNominal 名义化后的真实总量为 totalBalance 994,010,000,000,虚拟供应的 BPT 代币为 20,000,000,000 。可以计算出,更新后的汇率几乎是之前线性池原始缓存兑换率 1,100,443,876,587,504,549 的 45 倍,即 49,700,500,000,000,000,000。

MarsBit专栏精选

随后,在线性池中将 bb-a-USDC 兑换为 USDC。然而这一次的兑换同第二次的兑换一样,再一次造成 amountOut 向下舍入为 0 的情况,兑换路径和之前相同。

而接下来的这一次兑换则是反向将 USDC 兑换成 bb-a-USDC,兑换路径为 onSwap -> onSwapGivenIn -> _swapGivenMainIn。在这个过程中,我们发现,在计算需要兑换的 amountOut 的时候,其中对于虚拟供应量的计算,是基于兑换后的 BPT 代币 totalsupply 与池中剩余量之间的差值,该差值为0。

MarsBit专栏精选

这是因为 bptSupply 为 0,在计算 BPT Out 时直接通过调用 _toNominal 函数,而此路径的调用使得 USDC 兑 bb-a-USDC 的兑换比例接近 1:1。

MarsBit专栏精选 

MarsBit专栏精选

总结

batchSwap 通过多个池子之间多次原子交换,将一个池子交换的输出与另一个池子的输入相连(tokenIn 和 tokenOut),将 USDC 兑换为 BPT 代币。在这个 batchSwap 中并不会发生实际代币转移,而是通过记录转入和转出的数量来确认最后的兑换数量。又因为线性池是通过底层资产代币进行兑换的,兑换方式是通过一个虚拟供应量且是固定的算法计算出 Rate 。因此,batchSwap 中存在两个安全漏洞:

一是线性池的向下舍入问题,攻击者通过舍入为池子单边添加 main 代币提高缓存代币的比率,从而操纵相应可组合池中的代币兑换率;

二是由于可组合池的虚拟供应量特性,虚拟供应量通过 BPT 代币减去池子中的余额来计算,在兑换的时候如果 GiveIn 是 BPT 代币,那么之后的供应量就会扣掉这部分,攻击者只需要将 BPT 作为 GiveIn 来进行兑换,并将其供应量先操纵为 0 ,之后进行反向 swap,即 BPT 再作为 GiveOut 一方,这时候由于供应量是 0,算法会按照接近 1:1 的比例低于线性池的兑换比例来进行实际兑换,使得 GiveOut 的 BPT 代币数量间接被操控。

我们可以发现,漏洞一为兑换增加了兑换率,而反向兑换时漏洞二再反向降低兑换率,攻击者利用了双重 buff 获利离场。


参考链接:

[1]https://twitter.com/Balancer/status/1694014645378724280

[2]https://forum.balancer.fi/t/vulnerability-found-in-some-pools/5102?u=endymionjkb

[3]https://etherscan.io/tx/0x7020e0ccafff2c86db3df5a2af0cccb4e931fe948f69bf20ea517b0cc99c1f15

[4]https://twitter.com/Balancer/status/1695777503699435751

[5]https://medium.com/balancer-protocol/rate-manipulation-in-balancer-boosted-pools-technical-postmortem-53db4b642492

[6]https://docs.balancer.fi/concepts/overview/basics.html

[7]https://docs.balancer.fi/reference/swaps/flash-swaps.html#flash-swaps

[8]https://docs.balancer.fi/concepts/pools/linear.html

[9]https://docs.balancer.fi/concepts/pools/boosted.html

免责声明:本文仅代表作者个人观点,不代表链观CHAINLOOK立场,不承担法律责任。文章及观点也不构成投资意见。请用户理性看待市场风险,以及遵守所在国家和地区的相关法律法规。
图文来源:慢雾科技,如有侵权请联系删除。转载或引用请注明文章出处!

标签:

分享至
https://www.chainlook.cn/toutiao/1696999814.html

下一篇:

The Block主编:值得关注的5个创新项目

有潜力的新兴项目包括衍生品协议、治理平台及基础设施等。

免责声明:
链观CHAINLOOK作为区块链技术应用与Web3行业研究的智库媒体,旨在为中国区块链专家、学者们提供最新的行业资讯信息与数据样本,用于区块链技术研究与创新。本站所发布的文章仅代表作者的个人观点,不代表链观CHAINLOOK官方立场,本站所发布的区块链行业研究报告与数据分析成果是通过人工智能算法对数据内容进行分析与归纳生成,不代表任何投资暗示与建议,链观CHAINLOOK不承担法律责任。

风险提示:
虚拟货币不具有法定货币等同的法律地位,参与虚拟货币投资交易存在法律风险,链观CHAINLOOK坚决反对各类代币炒作,请读者提高风险意识,理性看待区块链技术应用及市场风险。

© 链观CHAINLOOK All Rights Reserved. 京ICP备18054193号-5