深入到闪电贷的源代码,理解什么是闪电贷和闪电贷攻击。
分享嘉宾:Frog,Numen 区块链安全研究员
整理:小胡
编辑:辰一
我们在上一期文章中,主要介绍了针对领先 DeFi 借贷协议 Euler Finance 的闪电贷攻击。在本期文章中,我们会更进一步,深入到闪电贷的源代码,理解什么是闪电贷和闪电贷攻击。
正如我们上一期文章(「暴雷」启示录:Euler Finance 被攻击前因后果复现),闪电贷有如下关键词:
1. 不需要抵押资金的贷款
2. 放贷、使用资金和还贷「同时」进行
因此利用无需抵押的大额资金,黑客可以:
1. 操纵价格,砸盘或拉盘
2. 成倍地放大已有漏洞
闪电贷(Flash loan)是一种去中心化金融(DeFi)的创新产品,它允许用户在不提供任何抵押的情况下,借用协议池中的任意数量的资产,只要在同一笔交易(一个区块)中归还本金和利息。闪电贷的优势在于,它可以让用户利用市场上的套利机会,实现低成本、高收益的操作。闪电贷的风险在于,如果用户无法在规定时间内还款,那么交易会被撤销,用户将损失交易费用和利息。
提到闪电贷,你可能会立刻联想到 Aave,这家公司是闪电贷的发明者和领导者。由于闪电贷的资金本身有低风险的特性,较高的需求立刻涌现了一大批闪电贷平台。提供闪电贷服务的头部平台还有:
- Uniswap: 一个去中心化的交易平台,提供闪电贷服务。用户可以通过 Uniswap 借用任何支持的代币,并在同一笔交易中进行兑换、做市、套利等操作。
- Compound:一个开源的借贷协议,支持多种数字资产的借贷和借款,提供闪电贷服务。用户可以通过 Compound 借用任何可用的资产,并在同一笔交易中进行还款、抵押、借款等操作。
- MakerDAO:一个去中心化的稳定币平台,支持 DAI 稳定币的发行和交易,提供闪电贷服务。用户可以通过 MakerDAO 借用 DAI,并在同一笔交易中进行还款、兑换、偿还清算罚金等操作。
- dYdX:一个去中心化交易所,提供闪电贷服务和开放式借贷市场。用户可以通过 dYdX 借用任何可用的资产,并在同一笔交易中进行还款、交易、杠杆等操作。
除此之外,常见的还有 Nuo、Fulcrum、bZx、DeFi Money Market、ETHLend 等。
这些闪电贷平台在功能上都提供了无抵押、即时借贷的服务,细节上的差异更多的是体现在平台的安全性、用户体验和手续费上。
Uniswap 是 DeFi 中最受欢迎的去中心化交易所之一。一般用户接触到 Uniswap 的途径是网页界面。比如下图展示了 ETH-DAI 的兑换。我们称两个代币之间的兑换为一个交易对。
但除了使用网站进行交互,我们还可以直接调用智能合约交互。当你想在他们的平台上购买 token 时:
Uniswap V2 版有一个新的功能,叫做 Flashswap,是 Uniswap 对闪电贷的称呼。这一功能是集成在常规的 swap() 函数中的。
一个常见的闪电贷操作逻辑有四步,必须在同一个区块中完成:
1. 发送交易请求,并上传智能合约
2. 向协议发送代币
3. 上传的智能合约使用代币交互
4. 归还代币给协议
因为 Uniswap 的 swap() 函数模式是先转账再进行校验,所以第二步可以先将需要的代币借出后,第三步调用自己合约中的 uniswapV2Call() 函数进行其它操作,完成后第四步再将借的代币归还,完成闪电贷服务。
在代码中,红框部分就是用户预先部署的合约。要想在同一个区块中完成上面的所有操作,用户要将如何使用借出的资金用合约预先定义好,毕竟手工操作的速度没有代码执行快。
而在红框上方的两行中,我们看到有两个转账函数。如注释所言,这是「乐观的转账」──即不校验是否支付就先转账。在最后的部分中,调用完用户合约之后,swap() 函数才会慢悠悠地 require 支付金额。
而无论是否使用闪电贷服务,Uniswap 都会对每笔交易收取高达 0.3% 的手续费。
总体而言,AAVE 的 LendingPool 合约中的 Flashloan 函数提供了类似的闪电贷功能。通过调用 Flashloan 函数,传入借贷的金额和代币类型借贷地址等参数。执行过程中将代币转给传入的接收地址后会调用传入地址的 executeOperation 函数,这时可以在 executeOperation 函数完成其它操作。
AAVE 和 Uniswap 有一点点不同之处在于还款的流程。AAVE 闪电贷的还款,是需要借贷者向 LendingPool 合约授权,在完成闪电贷过程后,由 AAVE 合约将借用的钱和手续费从借款地址中转回。而在 Uniswap 中,是由用户调用 safeTransfer 进行手动转账。
这样可以保证更高的成功率,但相应的可能有一定的合约漏洞风险。
关于闪电贷攻击的讨论是不断的。有人说闪电贷攻击严格来讲名不副实,因为并没有利用闪电贷的漏洞,而是利用大额的资金提前发现了已有的漏洞。比如利用闪电贷低成本的大额资金,可以:
1. 利用闪电贷大的资金量操纵价格,一些项目中价格获取逻辑存在问题。
2. 一些项目在抵押或其它过程中会产生瞬时奖励,利用闪电贷获得大额奖励。
3. 项目中存在其它的逻辑漏洞,利用闪电贷大的资金量放大套利空间。
我们先从案例进行分析,然后再讨论哪些漏洞容易被闪电贷攻击。
简单来讲,针对 DFX 的攻击分为三步:
1. 从 UniswapV3 完成对 USDC 和 XIDR 的闪电贷。
2. 从 DFX 的合约中调用 flash 函数借贷到 UCDC 和 XIDR 后又去调用了 deposit 函数进行了抵押,抵押时可以看到又将黑客借出的钱抵押回了 DFX 合约。
3. 因为用户抵押时将代币又转回到了 DFX 的合约导致借款前后的余额验证通过,不用再归还借出的闪电贷,但是黑客有抵押操作,此时便可以调用提取函数,提取抵押后离场。
让我们来盘点一下下面代码都有哪些漏洞。
1. 通过代币数量获取价格:
这段代码直接读取了去中心化交易所 pair 合约中的代币数量,将两者之商作为代币的价格。对于缺乏代码审计经验的人来说,这样写乍一看似乎没有什么问题。但是如果考虑到 Uniswap 支持闪电贷这一事实,事情就变得有趣了起来。
如果没有闪电贷,两种代币的数量一定是一个减少另一个就增加。但是有了闪电贷,很有可能发生的局面是一个代币减少,而另一种代币没有增加。如果闪电贷的数额不大,两种情况下的差额不大。但如果借出了巨额闪电贷,这个价差将会被立刻拉大,形成巨大套利空间。
这样的漏洞对于没有代码审计经验的人来说很难发现,需要在安全领域有较长时间的深耕和积累,才能形成一套成熟的代码审计体系。
2. 发放奖励:
在这段代码中,合约会根据抵押量的大小,立刻将奖励返还给用户地址。这里的问题我们已经提到过了,也是比较好想到。如果奖励发放没有延迟,闪电贷的钱在合约中质押了仅仅一瞬间就能获得奖励,这样无疑是一个巨大的套利漏洞。
在 DeFi 还没有成熟的时间段内,闪电贷攻击仍将持续不断,成为悬在人们头上的一把达摩克利斯之剑。不同的闪电贷攻击影响的范围也不同,有的可能导致某个看起来毫不相干的 DeFi 协议暴雷,有的可能波及众多用户。我们能做好的就是做好防范措施,静待 DeFi 不断成熟:
1. 质押挖矿功能和借贷合约做分离,不要将用户抵押的代币转移到借贷合约或者记录变量做运算。
2. 借贷时添加特殊重入锁,借贷时不允许抵押。
3. 项目在价格获取时采用多数据源的形式,数据对比后减小价格误差。
4. 移除流动性时应当先销毁 LP,再进行其它调用。
5. 奖励的产生尽量采用隔区块或者时间差的形式,可以有效避免闪电贷攻击。
6. 项目逻辑问题是导致黑客利用闪电贷放大套利最常见得情况。因此,项目在上线前要做好安全审计和测试,保证项目安全性。
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。