成都链安:Balancer 项目漏洞分析

成都链安:Balancer 项目漏洞分析

一、事件背景

Balancer 官网(https://balancer.finance/)上对于其具体功能的描述为『Easily swap ERC20 tokens. Exchange tokens without deposits, bids/asks, and order management. All on-chain.』。简单来说 Balancer 就是提供在链上进行 tokens 交换的区块链智能合约应用。

2020 年 6 月 29 日凌晨(北京时间),Balancer 项目的两个资金池遭受攻击。攻击者在此次事件中获利约 46 万美元,资金池市商损失约 50 万美元。

根据此次安全事件的具体过程,可以将此次事件比喻为攻击者『偷梁换柱』。

二、抽丝剥茧还原攻击者『偷梁换柱』经过

2.1、安全事件概述

▷根据链上交易数据显示:

1、 攻击者利用自建合约

(0x81d73c55458f024cdc82bbf27468a2deaa631407)对存在通缩货币 STA 的资产池

(0x0e511Aa1a137AaD267dfe3a6bFCa0b856C1a3682)进行了攻击;

2、 攻击者利用自建合约

(0xab9a95521107bc40dfa2e795059319f8b1302866)对存在通缩货币 STONK 的资产池

(0xb9eaf49d9f913bC1314e37bb5482891840c8e3C1)进行了攻击。

2.2、攻击步骤简介

攻击者首先通过闪电贷借款大量 WETH,而后使用借得的 WETH 将被攻击资金池中的通缩货币(STA 或 STONK)兑换出来,仅留下 1e-18 个通缩货币。完成上述准备工作后,攻击者开始发动攻击,不断使用 1e-18 个通缩货币(STA 或 STONK)兑换资金池内的其他代币,以达到 『偷梁换柱』 的目的。直到池内资金基本被转移完后,攻击者将获利存入如下地址:

0xBF675C80540111A310B06e1482f9127eF4E7469A

攻击过程如下图所示:

成都链安:Balancer 项目漏洞分析

△图 1

此次事件发生后,Balancer 团队表示已对资产池进行审计,正在进行第三次审计,并将在 UI 界面启用通缩货币黑名单,禁止用户建立存在通缩货币的资产池。

2.3、漏洞原理详细分析

在分析漏洞具体信息之前我们需要知道以下两点:

1、Balancer 项目允许个人建立资金池。 资金池本质上是一个智能合约,用户可以调用资金池的函数进行代币兑换。资金池中可以存在多种货币,用户可以使用资金池中存在的货币进行兑换,兑换的比例按照一种固定的算法,如图所示:

成都链安:Balancer 项目漏洞分析

△图 2

我们以用 STA 兑换 WETH 为例:

▷TokenAmountOut 表示可以兑换出的 WETH 的值

▷TokenBalanceOut 表示当前池内的 WETH 的值

▷TokenBalanceIn 表示当前池子内的 STA 的值

▷TokenAmountIn 表示用户输入的 STA 的值

▷TokenweightIn 表示 STA 的权重,为一个固定值,只能由资金池的管理者更改

▷TokenweightOut 表示 WETH 的权重,为一个固定值,只能由资金池的管理者更改

▷SwapFee 表示手续费,为一个固定值,只能由资金池的管理者更改

综上所述,当一种货币 STA 在一个资金池中的存量较少时,也就是 bI 较小时,就可以使用 STA 兑换更多的 WETH。

2、STA 代币是一种通缩货币,当进行转账操作时,会自动销毁一定量的 STA。 如下图所示:

成都链安:Balancer 项目漏洞分析

△图 3

tokensToBurn 即为每次交易销毁的值,其销毁数额是转账数额的 1/100 (向上取整),如当数值为 1e-18 时,其销毁值也是 1e-18。销毁值的计算源码如下图所示:

成都链安:Balancer 项目漏洞分析

△图 4

成都链安:Balancer 项目漏洞分析

△图 5

接下来我们对本次攻击事件进行分析,以存在 STA 的被攻击资金池为例。攻击者向自建合约

(0x81d73c55458f024cdc82bbf27468a2deaa631407)发起了一笔交易

(0x013be97768b702fe8eccef1a40544d5ecb3c1961ad5f87fee4d16fdc08c78106)。在此笔交易中,攻击者首先从闪电贷借出了 104331 个 WETH,如下图所示:

成都链安:Balancer 项目漏洞分析

△图 6

而后使用借来的 WETH 兑换被攻击资产池中的 STA,因为 STA 是通缩货币,每次 transfer 都会使得 STA 销毁转账金额的 1/100 (向上取整)。如下图为一次兑换:

成都链安:Balancer 项目漏洞分析

△图 7

这笔交易共进行了 20 余次兑换,使得被攻击资金池中的 STA 余量为一个极小值(10-18)后开始使用 STA 兑换其他代币(WETH),如下图所示:

成都链安:Balancer 项目漏洞分析

△图 8

我们可以发现,在此笔交易中,攻击者转给被攻击合约的 STA 个数是『0』,但却扣除了 1e-18 个 STA,这不符合正常兑换情况。于是我们对此进行深入分析,通过事件日志确定攻击者发送了 1e-18 个 STA。如下图所示:

成都链安:Balancer 项目漏洞分析

△图 9

由此可得出结论,在发送过程中,因为 STA 的通缩机制,发送给资金池的 STA 会被销毁,导致被攻击资金池无法收到 STA,但资金池合约仍然会认为收到了 1e-18 个 STA,并更新 STA 的存量(inRecord.balance)。如下图所示:

成都链安:Balancer 项目漏洞分析

△图 10

如果 STA 的存量增加,就会使得 STA 能够兑换其他代币的比例下降,因此攻击者又调用了 gulp() 方法来更新 STA 的余额,使得资金池的 STA 余额等于实际余额。如图所示:

成都链安:Balancer 项目漏洞分析

△图 11

每进行一次兑换,攻击者就会调用一次 gulp() 对 STA 的余额进行更新,这样使得 STA 的余额始终为 1e-18 个,因此每次攻击取出余额的比例都是不变的(1/2),如下图所示:

成都链安:Balancer 项目漏洞分析

△图 12

攻击者使用这种方式,将资金池中的所有代币以每次 1/2 的比例进行兑换,最终几乎将资金池中的所有代币全部提出。

2.4、攻击事件总结

根据我们日常智能合约安全审计经验来看,本次事件产生的原因,可能是资金池合约对流入资金的处理方式不够完善,并没有考虑到通缩性代币的情况,在计算应当输出的值 tokenAmountOut 和货币余额 inRecord.balance 的增减时,都是使用由用户控制的 tokenAmountIn 参数,而不是实际收到的代币数,导致实际池中的流入资金与记录资金不相符,如下图所示:

成都链安:Balancer 项目漏洞分析

△图 13

另外,用于更新代币余额的 gulp ()函数的权限是 external,这两点组合起来,导致了本次事件漏洞的产生,如下图所示:

成都链安:Balancer 项目漏洞分析

△图 14

成都链安:Balancer 项目漏洞分析

成都链安:Balancer 项目漏洞分析

成都链安:Balancer 项目漏洞分析

成都链安:Balancer 项目漏洞分析

成都链安:Balancer 项目漏洞分析

来源链接:mp.weixin.qq.com