撰文:曹一新,就职于 HashKey Capital Research
2020 年以来,以太坊 DeFi 生态中有十多起利用闪电贷的大规模攻击事件陆续被媒体曝光(如表 1 所示),而这些事件已经呈现出明显的模式化和重复性特征。有别于技术漏洞导致的攻击事件(编号 6、8、11),其它几起事件呈现了对 DeFi 生态经济系统漏洞的攻击手法。从表面上看,其共性是主攻协议都与某些 AMM 协议关联,而攻击者通过操纵 AMM 资产池内的资产价格或者资产数量使关联协议蒙受损失,我们不妨称之为「经济攻击」。至今出现的经济攻击分为「哄抬套利」和「操纵预言机」两种手法,本文归纳发动经济攻击的必要非充分条件及一般攻击模式,从而推断出攻击者攻击 DeFi 系统的「命门」所在,为抵御此类安全风险提出若干警示。
表 1. 闪电贷攻击事件
「哄抬套利」攻击
「哄抬套利」攻击的原理与 CeFi 常见的 Pump-and-Dump 市场操纵行为或者链上交易容易碰到的「front running」攻击本质上并无差异,都是先想办法借助他人的资本抬高自有资产的价格再高价卖出获利。DeFi 生态中此类经济攻击事件之所以能够成功,都与至少两个核心模块——攻击目标和 AMM 有关,并且这两个模块通过条件一相互关联。
条件一:攻击目标与 AMM 之间存在资产转移关系,并能由用户自主触发相关智能合约执行资产转移。
这里的攻击目标可以是机枪池、借贷平台、杠杆交易平台等 DeFi 模块。机枪池是运行着一定投资策略的智能合约,可以把它类比于一个基金,为用户提供代理理财业务,用户将自有资产存入机枪池以期获得收益,例如 Yearn 、Harvest ;借贷平台为出借方和贷方提供服务,赚取利息差,贷方一般要先超额抵押一部分资产,例如 Compound 、 Aave ;杠杆交易平台允许投资者抵押一定资产作为保证金进行杠杆交易,例如 bZx 。AMM 通过一个定价函数实现自动做市商交易,用户可兑换资产或作为流动性提供商(LP)参与流动性挖矿,在此不再赘述。
「哄抬套利」攻击的一般步骤
条件一是 DeFi 系统存在被「哄抬套利」攻击的必要非充分条件,实际攻击过程中还要考虑到资金量、手续费、智能合约在执行交易前设置的检查点等因素。攻击者可通过建立优化模型找到最优参数,预测其「哄抬套利」收益来决定是否采取行动。这种攻击手法的一般操作步骤如下(结合图 1 所示):
图 1. 哄抬套利攻击的基础模型(序号代表攻击步骤,实线表示必要步骤,虚线代表或有步骤;攻击目标的净值计算环节和 AMM 的定价函数在设计上存在被黑客利用的风险)
假设 AMM 资产池里的流动性资产为 X、Y,流动性代币为 C。
第一步,准备。持有即将被哄抬的初始资产 Y 及用于触发攻击目标自动执行策略的初始资产 A。
第二步,哄抬。将资产 A 发送至攻击目标的相关智能合约,获得代币 B (代表在机枪池、借贷平台、杠杆交易平台等攻击平台中的头寸,以下称为「头寸代币」),并触发智能合约向 AMM 资产池投入资产 X,获得资产 Y 或流动性代币 C,并抬高 AMM 资产池内资产 Y 的价格。
第三步,套利。攻击者将步骤二中的资产 Y 投入 AMM 资产池,以抬高后的价格获得资产 X 或流动性代币 C。需要说明的是,第二、三步中的操作对应于 swap (X、Y 之间交换)或流动性挖矿(X 或 Y 与 C 之间交换)。对于三个及以上币种的 AMM,这里的 X 或 Y 可视为资产组合。
第四步,收尾。攻击者根据代币 B 的最新净值及后续交易计划决策是否赎回资产 D。
对于流动性充足的资产池,为了在 AMM 里制造可观的价格滑点,往往需要投入很大的资金量,故攻击者一般会从闪电贷借出初始资产。若闪电贷可供借贷的资产类别不满足要求,攻击者会去某些 AMM 或借贷平台通过 swap、流动性挖矿、借贷等方式获得;也不排除攻击者直接去与攻击目标关联的 AMM 获取。若攻击者在第一步的准备过程中采用了闪电贷,那么在第四步就还需在同一笔攻击交易中归还闪电贷。
攻击者赢利分析
若攻击者不赎回头寸代币 B,那么当初投入的资产 A 就是成本上限,只有当套利所得大于这一成本才能赢利,这种情况目前只在提供杠杆资金的攻击目标中成功过,攻击者以较少的保证金成本 A 撬动攻击目标的大量资金 X 为其在 AMM 里抬高资产 Y 的价格。但前提是在执行交易前攻击者要避开攻击目标对其保证金净值是否跌破清算线的检查点,表 1 中编号为 1 的攻击事件就是典型案例。
这里有两点值得注意:
这里的关键是攻击目标发行的份额代币 B 为攻击者的初始资产提供了隔离保护作用,攻击者往往会先操纵 AMM 里的价格使得其中资产 X 大幅贬值,牺牲攻击目标的基金资产撬动 AMM 内的价格滑点往有利方向偏移,使攻击者在保证自有资产不大幅贬值的情况下实现可观的套利,表 1 中编号为 10 的攻击事件就是典型案例。
亏损分析
小节 1.2 分析了在「哄抬套利」攻击模式下攻击者获利的原理,本节进一步分析谁是亏损的「冤大头」?
站在攻击目标的视角,在整个攻击过程中,其底层资产净值的变化来源于:
-
攻击者投入资产 A 获得份额;
-
与 AMM 发生资产交换的差额,即公式(6)所示;
-
攻击者赎回份额。
若攻击者最终用头寸代币 B 赎回份额,则攻击目标资产价值变化为:
若攻击者没有赎回初始资产,则相当于攻击目标净收入资产 A,公式(10)变为,
对于 AMM 而言,其恒定乘积定价方程保证兑换操作前后,其资产池内资产数量乘以资产价格的总价值保持恒定;而在注入流动性的情况下,新发行流动性代币的每份额价值与原有流动代币每份额代币的价值一致。故每次攻击者或机枪池与之交互时,都是按 AMM 内部价格等额交换的。即使攻击者利用巨额资金使得 AMM 的价格偏离市场价格,AMM 的流动性提供者只会暂时遭到价格偏差带来的无常损失,后续会有套利者将价格搬平,而套利者获得的受益来源于金融业务模块损失的一部分。
案例举例
表 1 中编号为 1 和 10 的事件都符合上述「哄抬套利」攻击的一般模式,且易于复刻。在 Yearn 机枪池闪电贷攻击事件中,攻击者按照前面 1.1 总结的做了四个步骤:
第一步,从 Aave 和 dYdX 闪电贷借出 ETH,抵押到 Compound 获得大量 DAI 和 USDC。一部分 DAI 留用,将另一部分 DAI 和 USDC 抵押至 Curve 的 3Pool 做流动性挖矿,获得流动性代币 3CRV。用全部 3CRV 赎回 USDT,这样就准备好了资产 Y (USDT)和资产 A (DAI)。
第二步,将 DAI 抵押至 Yearn 的 yDAI Vault,获得 yDAI (即头寸代币 B)。这个合约会自动触发将 DAI (资产 X)投入到 Curve 3Pool 的流动性挖矿投资策略,获得 3CRV (代币 C)。由于 3CRV 中 DAI 的数量变多,根据定价函数更新后 DAI 发生贬值,USDT 增值。
第三步,将步骤一准备好的 USDT 投入 Curve 3Pool,高价换出流动性代币 3CRV (代币 C)。这时候攻击者持有 3CRV 数量大于步骤一中的初始数量,套利成功。
第四步,用步骤二中的 yDAI 赎回 DAI (资产 D)。虽然 yDAI 的净值有所下降,但这部分亏损小于套利所得。攻击者在一笔闪电贷交易中重复上述步骤 10 次,归还闪电贷,最终累计得到大量 3CRV 和 USDT,编号为 1 的 bZx 攻击事件中,攻击者从闪电贷借出 ETH (资产 A),先抵押一部分至 Compound 借出 WBTC (资产 Y),再抵押一部分 ETH 至 bZx 触发其 5 倍杠杆做空 ETH 交易,获得代表杠杆头寸的 sETHwBTC5x (头寸代币 B)。bZx 合约为其提供杠杆资金向 Uniswap 卖出大量 ETH (资产 X)获得 WBTC (资产 Y),将 WBTC 价格抬升 3 倍。攻击者卖出 WBTC (资产 Y),得 ETH (资产 X),一部分归还闪电贷,另一部分用于赎回 Compound 里的抵押物,而没有赎回杠杆头寸 sETHwBTC5x,因为此时早已触发清算线,而攻击者发现合约漏洞避开了检查点。
编号为 3 的攻击事件利用了 Balancer 支持的一种利用销毁自身代币的方式来替代收取手续费行为的「通缩型」代币 STA,通过反复交易 STA 来使得其数量不断减少,从而价格不断抬升,属于特定条件下的案例,不易复刻。
「操纵预言机」攻击
「操纵语言机」攻击可以认为是「哄抬套利」攻击的一种对称操作,其必要条件为:
条件二:攻击目标依赖 AMM 提供的信息对其内部资产进行定价。
图 2. 操纵预言机攻击的基础模型(序号代表攻击步骤,实线表示必要步骤,虚线代表或有步骤;攻击目标的净值计算环节和 AMM 的定价函数在设计上存在被黑客利用的风险)
这种情况下攻击者虽然无法利用攻击目标内的资产去哄抬 AMM 内某资产的价格,但可以考察 AMM 模块作为预言机能否被操纵,从而哄抬攻击目标内的资产。
攻击目标依赖 AMM 提供信息的主要目的有两种:
-
对抵押物进行估值;
-
对头寸代币进行定价。
我们也可将其视为「净值计算」,而攻击者则专门寻找在净值计算中与实际情况出现偏差的合约进行操纵。
表 1 中编号为 5 和 9 的事件中攻击者利用了同一种预言机漏洞。Cheese Bank 和 Warp Finance 这两个机枪池都允许用户超额抵押 Uniswap 的流动性代币 UNI-V2 来借出稳定币,而抵押品 UNI-V2 的价值通过一个自行编写的预言机合约来计算。该合约利用相应 Uniswap 流动性池的资产数量、资产价格及 UNI-V2 的发行量计算,但资产数量和资产价格是从两个不关联的渠道获取——资产数量直接从 Uniswap 流动性池余额获取,而资产价格从 Uniswap 官方提供的一个时间加权平均预言机获取。这使得攻击者能够通过改变 Uniswap 流动性的资产数量,在资产价格维持不变的情况下提高抵押品 UNI-V2 的名义价值从而借出更多的稳定币。这种类型的攻击主要时因为预言机合约设计不够严谨导致的,但仍然重复发生两次,足以引起 DeFi 项目的警惕。
表 1 中编号为 2 的事件中,攻击者通过操纵 bZx 为抵押物 sUSD 估值的预言机(Uniswap 和 Kyber),抬高 sUSD 的价格,从而借出更多 ETH。
表 1 中编号为 3、7 的事件则通过操纵预言机报价,提高机枪池内份额代币的净值,从而兑换出更多资产,重复多次累计收益。
「操纵预言机」攻击的一般步骤为:
第一步,准备。获得用于操纵 AMM 预言机的资产 Y 及准备存入攻击目标的资产 A。
第二步,抵押。将资产 A 抵押至攻击目标,获得代表抵押物的头寸代币 B,有些情况下,不发放头寸代币 B,只在智能合约内部记账。
第三步,操纵。将资产 Y 投入 AMM 兑换资产 X,改变 AMM 流动性池内资产的比例从而改变报价,更新攻击目标合约内资产 A 或头寸代币 B 的定价;
第三步,收尾。若为借贷业务,则通过抬高的抵押物估值借出更多资产并不再归还;若为机枪池业务,则通过抬高价格后的头寸代币 B 赎回资产,获得增值收益。
讨论与启示
本文总结了 DeFi 系统遭受经济攻击的一般模式和命名所在,发现「哄抬套利」和「操纵预言机」攻击模式本质上都是对净值计算环节的利用和操纵行为。故而在设计 DeFi 系统的时候,妥善处理这一环节至关重要。最根本的预防措施就是取消用户自动触发交易策略链式执行或更新净值的权限,从根本上阻断攻击者完成一整套连贯的操纵行为。
例如针对「哄抬套利」的攻击策略,具体的做法可能是通过一个智能机器人识别用户的投资行为,按固定时间段批量计算净值及分配资金至策略池。但这种方法打破了原有原子交易的优势,可能会引发用户体验不佳、成本抬高、机器人发送的交易被控制等新问题。另一方面,目前出现的 DeFi 组合模型大部分还停留在简单的 A->B 模式,而生态内已经出现了 C->A->B (例如 Alchemix)的多层嵌套关联,这些关联关系可能无法避免涉及到智能合约之间的自动触发,仍然可能存在系统性的经济漏洞。
针对 「操纵预言机」所描述的攻击风险,目前一些机枪池设置了滑点限制,约束被报价资产的价格变动范围。但即便如此,攻击者还是能找到一些超过成本的套利空间并通过多次实施来累积套利总额(例如编号为 4 的 Harvest 攻击事件)。
闪电贷在整个攻击环节中属于锦上添花的角色,可为攻击者提供大量初始资金及试错成本仅为交易 gas 费的攻击机会。不过近期随着 ETH 价格的增长,构建如此复杂的攻击策略的智能合约所需花费的交易 gas 费已经非常昂贵,最近发生的 yearn 机枪池的单笔闪电贷攻击交易就耗费了攻击者 1.933 个 ETH (当时折合 3088 美元)。
综上所述,这些经济攻击事件表明,AMM 的设计机制很容易被攻击者利用来对关联产品进行攻击,并且具备特定的攻击模式。通过破除攻击模式依赖的必要条件可能会引发新的问题,还没有一种万全的策略完全消除此类风险。通过添加一定的约束条件来使得套利空间大大小于攻击者的期望值,或许是一种值得探讨的方法,这需要项目方对自己及关联协议的经济系统有全面且深入的研究。