合约安全[1] Balancer中通货紧缩货币的套利攻击详解
2021-05-29

Abstract

消息来源:2020.6.29 链闻看天下
当红流动性挖矿 DeFi 项目 Balancer 如何被攻击?官方这么说
文中简要描述了攻击发生时间和危害,没有详细描述漏洞细节。

漏洞详细介绍可参考文章《Balancer Pool with STA Deflationary Token Incident》

概念介绍

滑点 slippage

在买卖 token 时,期望的交易价格和实际支付的价格之间的差别,称之为交易滑点。

流通池

用户将资产转入交易平台之中获得收益,而在平台中的总资产额度,就构成了流通池。
流通池中代币总额越多,深度越好,大额交易带来的影响也就更小,不至于因为一笔几十万美元的交易就让价格产生大幅波动。
Defi的一个风险点就是流通池不够大,容易产生较大滑点。

流动性挖矿 liquidity mining

用户将自己的token放到交易所的流通池中,交易所则将等值的交易所代币发放给用户。
这样一来,用户为交易所提供了流通池的部分深度。同时,用户可以享受交易所挖矿带来的分红。

自动化做市商 AMM

AMM是一种即时兑换模式。
通常的交易所(中心化/去中心化),需要买卖双方对价格达成一致,才能完成交易;而AMM 只需买方认可流通池中的token价格即可完成交易。

类比到游戏当中,就是玩家交易所 和 游戏NPC商人的区别。
在玩家交易所中,需要买方玩家的出价 和 卖方的出价一致,才能达成交易。 这需要双方的互动。
在游戏NPC商人中,无论是买方还是卖方,玩家只需要认可NPC给出的一口价,就可以完成交易。(例如把打怪获得的装备卖给武器商人,多少钱武器商人说了算,没有回旋余地)至于NPC给出的价格,则是按照游戏中的规则,对武器进行估值(蓝色装备10金币,紫色装备100金币等)
AMM就是这样一个武器商人,他会按照既定规则对token进行估值,并跟用户进行即时交易。

Uniswap

Uniswap 就是一个AMM实例,官网是 : https://uniswap.exchange
Uniswap 采取的算法为固定乘积的方式 : 可以用方程式 X * Y= K 来表示。这里,K 是个常数。
假设有这样一个交易池,其中有 50 个以太坊,有 1 万美元,而 K 的值恒定,为 1 万。
假设 X 表示以太坊的供应量 50, Y 是 ETH 交易时的价格。
在这种情况下,Y 的价格会是 10,000/50 = 200, 因为 K = 10000 是个常数值。
如果有人购买了以太坊,交易池子中的以太坊会减少,而美元的总量会增加。

  • 假设用户用 200 美元的价格,购买了 2 个以太坊。X 变为 48,Y 变成 10000/48 = 208 元。
  • 假设用户出售 7 个以太坊,X 变为 55 。Y 变成 10000/55 = 181 美元。

通缩货币模型

即被设计成"越用越少"的token。
举例如下

func token.Transfor(from,to,Amount):
	from.Amount -= Amount
	to.Amount += ( Amount - 1ETH )

当from把10000 ETH 转给 to时,from虽然减少了10000 个 ETH,但是 to 只收到了9999个 ETH。这就导致token总量越来越少。
实现了货币的通缩。

攻击过程

攻击对应的transaction是https://etherscan.io/tx/0x013be97768b702fe8eccef1a40544d5ecb3c1961ad5f87fee4d16fdc08c78106

其中Token Transfer如下:
交易内容如下
etherscan自带的explorer不太好看细节,这里推荐使用OKO contract explorer
界面如下,可以看到函数调用顺序
Call Tree

Step1 flash loan 借钱搞事

Defi相关攻击中,有部分攻击是需要资金成本的。闪电贷的出现,使这种资金成本不再是攻击者的障碍。

  • 我们看到Transaction首先调用了两次approve函数,来批准 WETH9和STA 代理用户转账。
    approve
  • 接着是一记operate函数,从dYdX进行闪电贷。一共借了104331.302778 × 10¹⁸ WETH
    在这里插入图片描述

Step2 用WETH换STA,使STA数量减少,并升值。

  • 然后攻击者分多次调用 swapExactAmountln函数,将借来的WETH换成STA。
    swapExactAmountln
    这里注意,由于STA是通货紧缩模型货币。上述swapExactAmountln函数发生时,流通池中的STA总数回减少,攻击者得到的STA也减少了,收到的STA的总数量是小于WETH数量除以两者价值比的。
    数量的下降,导致了STA单价上升(因为Uniswap中的K值不变,数量少则单价升高)。

Step3 用STA换WETH,配合gulp函数,获得额外WETH。

  • 最后,攻击者多次先后调用 swapExactAmountln 和 gulp函数,将STA换回WETH。
    在这里插入图片描述
    这里注意,如果没有配合gulp使用,STA的单价会随着swapExactAmountln的调用,越来越低。
    到最后STA单价会低到攻击者无法获利,不过是把WETH换成了STA,然后又换了回来。
    我们看一下gulp函数的定义。
function gulp(address token)
        external
        _logs_
        _lock_
    {
        require(_records[token].bound, "ERR_NOT_BOUND");
        _records[token].balance = IERC20(token).balanceOf(address(this));
    }

可见gulp是对STA单价进行一个校准,校准函数是ERC20的balanceOf。而不是用的当前流通池中预期的价格。
所以,这里会产生一个偏差,STA的单价会被固定在高位。不会随着swapExactAmountln的调用而减少。

Step4 还清flash loan贷款,套利离开

  • 最终,攻击者得到了超量的WETH,并向dYdX归还了借来的104331.302778 × 10¹⁸ WETH
    还钱
    并取出利润。
    拿钱走人

Discussion

攻击发生在2020年6月29日,造成了50w美元的损失。6月30日,balancer Labs宣布已经奖励了Bug finder Hex_Capital。并将会为所有funder报销整个攻击事件中的所有损失。

References

  • 攻击交易详情 https://oko.palkeo.com/0x013be97768b702fe8eccef1a40544d5ecb3c1961ad5f87fee4d16fdc08c78106/
  • 攻击事件记录 https://medium.com/@1inch.exchange/balancer-hack-2020-a8f7131c980e
  • balancer 官方twitter https://twitter.com/BalancerLabs
  • ERC20智能合约实现代码 https://zhuanlan.zhihu.com/p/104527883