本文将介绍闪电贷攻击的一种攻击方式——价格操纵攻击。
视频链接:《价格操纵攻击原理与分析——理论分析与安全开发措施》
分享嘉宾:BradMoon,MetaTrust 安全审计专家
整理:小胡
价格操纵攻击是闪贷攻击中影响最大的类别之一。这种影响最大,既体现在被盗金额上,也体现在审计难度上。
纵观 2020~2022 年,价格操纵攻击,次数和总损失,都「名列前茅」。不仅有 Pancake Bunny,还有 Pancake Hunny;同一个漏洞,不仅在原项目被盗,在分叉项目也被盗。
价格操纵攻击,可谓防不胜防。
2021 年的情人节,PancakeBunny 项目就受到价格操纵攻击。并且由于该项目「门徒」众多,很多分叉项目由于有相同的代码,在两个月内,那些反应迟钝的「门徒」,也一一遭到攻击。
价格操纵攻击之所以影响巨大,不仅体现在盗窃金额上,还体现在审计难度上。
DeFi 的逐步完善,来源于一个又一个真金白银买来的教训。现在的合约开发者,都会时刻绷紧「闪电贷」的弦。但在早期,闪电贷攻击往往是防不胜防。尤其是价格操纵攻击,更是由于其逻辑的隐秘性,更加容易被黑客利用。
我们给出的证据,来自最新的顶级软件工程会议,不仅获得了区块链领域专家的认可,还获得了传统计算机领域的认可。
论文中可以看到,审计发现的 Bug 仅有 5.9% 是价格操纵攻击,但真实世界里,发生频率最高的漏洞却是价格操纵攻击。
审计结果和实际情况如此失衡,足以证明价格操纵攻击的隐蔽性。
我们在下面详细解读,价格操纵攻击是什么,以及为什么有很强的隐蔽性。
我们先来看看什么是价格操纵攻击。
价格操纵攻击是指操控市场的买卖行为,人为地影响产品或资产价格,以谋取不正当的利益。
我们曾经说过,闪电贷赋予攻击者,在短时间内用于巨量资金的权利,很多 DeFi 项目的漏洞因此暴露出来。我们先来看价格是如何被操控的。
价格操纵攻击,就是某些项目的「代币价格」计算公式中,包含重大漏洞。
比如我们熟知的 Uniswap,就是用存入合约的代币数量,来计算交易价格。没有闪电贷时,逻辑没有任何问题。但一旦考虑了闪电贷,这个神奇的金融工具,一切就变得不同了。
闪电贷的借贷期限,实际上是无限趋近于零(具体可见前两期推文)。我们在无限短的时间内,将巨额资金充入 Uniswap 中,会发生什么呢?
(注意:代码仅仅展示算法逻辑,实际上很多细节问题,比如应该使用安全的数学库)
巨量的资金大幅增加了资金的供给,相对于资金来说,要购买的代币数量就显得「奇缺」,价格自然就被合约设置得「奇高」。我们看看价格飙升会发生什么:
对于 Uniswap 本身,由于用户会手动设置「滑点」,过高的价格会被驳回。
但是如果其他合约贪图省事,调用 Uniswap 的代币数量计算价格,就会遭殃了。这些合约很多都没有设置价格限制,无论看到多高的价格都会信以为真。
简单来说就是被打肿脸冒充的胖子忽悠了。
我们在前文提到了 Pancake Bunny。熟悉 DeFi 的读者可能知道,Pancake Bunny 是一个收益聚合器。
所谓收益聚合器,简单来说就是借钱给别人,而且谁利润高借给谁。用专业的术语讲,叫「Liquidity Provider」。
让我们看看 ChatGPT 如何看待收益聚合器:
「当然,使用收益聚合器也并非没有代价。首先,你需要支付一定比例的服务费给收益聚合器,这样它们才能维持运营和开发。其次,你需要信任收益聚合器的智能合约是正确和安全的,否则你可能会遭遇黑客攻击或者代码漏洞。最后,你需要了解收益聚合器的工作原理和风险,因为它们并不是万能的魔法棒,也有可能出现意外或者亏损。」──可谓三句不离合约安全和风险。
黑客事件对于一个 DeFi 项目的打击往往是毁灭性的。Pancake Bunny 几小时内价格暴跌 93%,并且从此再也一蹶不振。
整个攻击的流程和以往闪贷攻击基本相同,因此我们直击问题的核心。
我们先来从数学的角度审视一下问题:
Pancake Bunny 用铸造 Bunny 代币的形式发放奖励,下面的公式计算了铸造的代币数。
我们可以看到,最关键的是铸造的代币数量,和 WBNB_Balance 成正比!
也就是说,如果合约获取 WBNB_Balance 时,得到了很高的数字,铸造的数量也会以同样的速度增长!
在代码上,我们看到了和前文一样的逻辑,都是基于池子大小 reserve0 和 reserve1 计算代币价格。
由于此时的「以 BNB 计的价格」没有设置上限,合约就错误地接受了异常的价格,最终发放了超额奖励。
让我们再次回看一下价格操纵漏洞的本质。
漏洞的核心在于「获取价格」的错误。而原因在于引用了「Uniswap 池子大小」变量。
我们将其中的本质抽象出来:
当一个敏感操作,依赖于一个敏感变量,就很容易产生危险。
敏感操作,包含所有和钱打交道的操作。Mint、转账、询价…… 都是和真金白银强相关的操作。
敏感变量,指的是很容易被操纵的变量,特别是潜在的没有被操纵过的变量。包括代币价格、池子大小、其他项目的数据、地址余额数据等等。
我们还是以价格操纵攻击为例。
预言机问题,是区块链的一个经典问题。智能合约想要获取链下信息,往往需要一个人,不断地把链下信息上传到链上。这也就是 Chainlink 等实体 EOA 预言机作的。缺点也显而易见,由于每隔几个区块就要更新数据,积年累月下来 Gas 费将成为一笔不小的开支。
而另外一个折衷的办法,是使用链上数据。价格操纵攻击正是利用了这一点。由于数据源单一,只要一个被污染了,整个价格都会受到影响。因此正确的做法是使用链上的去中心化预言机。
当然,类似于 Uniswap 可以手动设置滑点,智能合约开发时,也可以手动设置上限,作为最后的安全底线。如何区分异常价格上升,和正常价格上升,则又成为了另一个难点。
在智能合约开发规范还未普及的当下,合约的安全性很大程度上,依赖于开发者个人能力和经验。这其中有一些通用的设计理念,被不断地提及和总结出来。
基于合约开发和算法设计的经验,合约开发者应该遵循下面的安全开发三原则:
程序员的看家本领便是不断抽象,以函数式编程的观点抽象自己的项目,严格限制输入和输出范围,减少全局变量的使用。
随着智能合约开发的逐步规范、还有开发和审计的不断努力,合约安全问题才可能得到真正意义上的缓解。
这将是迎来 Web3 大规模爆发的前奏。
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。