Hardhat 复现详细解析了 Discover 项目的闪电贷攻击,通过 攻击者合约的分析、交易执行流程的重现,以及时序图展示,完整还原了攻击过程。
闪电贷(Flash Loan)是 DeFi 生态中一种创新的金融工具,它允许用户在单笔交易中借入大量资金,并在交易结束前归还,无需提供抵押。这种机制极大降低了资金运作成本,同时也为攻击者提供了新的套利手段。闪电贷攻击便是利用该特性,结合智能合约漏洞,对 DeFi 平台进行恶意操纵的一种攻击方式。
闪电贷攻击主要分为两类:
去中心化交易所(DEX)和借贷协议通常采用某种询价机制(Pricing Mechanism)来确定资产价格。然而,这种机制的单一性使其容易被操纵,尤其是在大额交易的影响下,价格会产生剧烈波动。过去,此类攻击需要大量资金支撑,而闪电贷的出现降低了攻击门槛,使得攻击者可以在零成本的情况下操纵市场。
基于价格操纵的闪电贷攻击一般包括以下四个步骤:
闪电贷借款
抵押资产
操纵市场价格
获利并归还贷款
除了操纵价格外,闪电贷还可用于放大基于智能合约漏洞(如重入漏洞、定价错误、整数溢出等)的攻击。例如,在正常情况下,攻击者可能因资金有限而无法利用某些漏洞获利,而闪电贷提供的大额资金让攻击者可以执行更大规模的攻击,放大潜在损失。
闪电贷攻击的发生通常与以下因素有关:
事件概述
攻击者使用闪电贷借入资金,在 bZx 交易池中执行大额交易,人为制造价格异常,使 bZx 在借贷时错误评估资产价值,从而获利。这是史上首例利用闪电贷的 DeFi 价格攻击,之后类似攻击事件频发。
攻击手法: 本次攻击者结合闪电贷与预言机操纵,利用闪电贷借出大量资金,在PancakeSwap 交易池中进行交易,推高目标代币价格,进而利用该价格变化进行套利。
闪电贷借款
操纵市场
利用抵押套利
市场价格回调
套利退出
0x446247bb10B77D1BCa4D4A396E014526D1ABA277
0x06B912354B167848a4A608a56BC26C680DAD3D79
0x5908E4650bA07a9cf9ef9FD55854D4e1b700A267
)闪电贷攻击的频率和影响范围不断扩大,主要有以下原因:
攻击门槛低
攻击规模大
代码 Fork 现象严重
去中心化金融的组合性
2022 年 6 月 6 日,Discover 项目 遭遇 闪电贷攻击,攻击者巧妙地结合了闪电贷与预言机价格操纵,利用大额资金短时间内在去中心化交易池(PancakeSwap)中进行交易,人为推高目标代币价格,从而实现套利。这属于基于询价机制的闪电贷攻击。
可以简单理解为:
攻击者通过快速借贷(闪电贷)获取大量资金,在 PancakeSwap 交易所中大量购买 Discover 代币(萝卜),抬高其价格,再利用高估值的 Discover 代币进行抵押借贷,从而获得利润。
PancakeSwap 采用自动化做市商(AMM)模型,其交易池本质上是包含两种代币(例如萝卜和白菜)的智能合约。交易者可以向交易池添加流动性,或通过池子执行买卖操作,价格则通过公式自动计算。
PancakeSwap 采用常数乘积公式:
$$
x \times y = k
$$
其中:
该公式确保在交易前后,交易池的总价值保持不变。
初始设定
交易影响
攻击者正是利用这一价格变动机制,通过大量买入目标代币,使其价格短时间内暴涨,进而进行套利。
0x446247bb10B77D1BCa4D4A396E014526D1ABA277
0x06B912354B167848a4A608a56BC26C680DAD3D79
0x5908E4650bA07a9cf9ef9FD55854D4e1b700A267
攻击者通过PancakeSwap 交易所借入大额资金:
第一笔借款
第二笔借款
🔗 交易详情:点击查看 BlockSec 交易分析
在 BscScan(币安区块链浏览器) 中查看到,攻击者的账户地址为:
攻击者在攻击前部署了 两个智能合约,作为整个攻击的重要组成部分:
0x06b912354b167848a4a608a56bc26c680dad3d79
0xFa9c2157Cf3D8Cbfd54F6BEF7388fBCd7dc90bD6
由于攻击者不会公开智能合约源码,所以需要通过分析攻击交易来推测两个合约的功能。
invest
函数
pledge
质押函数,将 Discover 代币进行抵押。0x3e30e90f
(定义为 attack
函数)
可以看到,整个攻击交易是由79合约中的0x77db1582函数(我们定义为attackBegin函数)发起的。attackBegin函数只调用了pancakeSwap中BUSD-BSC-USD交易池的swap函数,借出2100个BSC-USD。在pancakeSwap项目中进行闪电贷需要在自己发起闪电贷的合约中执行pancakecall函数,这是一个回调函数,用来在一笔交易中向借款的交易池还款。
attackBegin
函数
swap
函数,借出 2100 BSC-USD(即 USDT)。pancakeCall
回调函数
pancakeCall
回调函数,否则交易会失败。pancakeCall
在本次攻击中被 调用 2 次,且两次调用执行了不同的逻辑。flag
变量 来区分 两次回调 的执行逻辑。pancakeCall
两次回调逻辑attack
函数,将 Discover 代币转发给 79 号合约。在 Hardhat.config.js 中配置 Binance Smart Chain(BSC)主网:
18446846
,需要将 Hardhat fork 到攻击发生前的区块 以进行复现。invest
函数,将资金存入 ETHplege 合约。
-- Plegein函数的功能是质押,并且将质押人相关信息存储到ETHplege合约中。30合约还一个attack函数,用以整个攻击交易中的调用。
构造函数目的是拿到79合约需要交互的合约实例,方便79合约与其他合约进行调用,简化操作。
--attackBegin是整个攻击交易发生的入口。该函数执行的是执行pancakeswap交易所的闪电贷功能。改swap函数需要执行调用者合约中的pancakecall的回调函数,如果没有回调函数改交易就会失败被revert。所以79合约要一个pancakecall回调函数。
之前说到pancakeCall函数在2次调用中要实现不同的的函数逻辑,所以需要通过flag来标识第几次执行pancakeCall函数的内部逻辑。
--第一次pancakeCall函数回调。
--第二次pancakeCall函数回调。
至此,在智能合约上的准备和分析完成。 接下来我们需要在hardhat上去实现攻击复现。
该函数是异步函数,传入的参数为合约名和合约构造函数需要的参数。然后将该函数模组导出,在attack-reproduction脚本中引用。
首先通过ethers拿到本地网络的用户数组,使用用户0作为攻击复现者。
然后在beforeAttack_tranferToAttack函数中进行攻击前的资金准备。调用部署函数并传入attack30 字符串参数。这样我们就能够将30合约部署上链。然后获取到部署到的attack 30 合约的地址,作为部署attack 79 合约的第二个参数。此时我们将attack30合约和attack79合约都部署上链。
调用attack30.invest()和attack79.attackBegin()
目的是向ETHpeldge项目方以30合约的地址进行质押,留下30合约的信息。
79合约的attackBegin执行借款。是整个攻击交易的开始。
yarn hardhat node
,该命令的功能是运行起hardhat本地私链。
2.然后运行以下命令:
可以在控制台窗口看到打印出的信息。
时序图如下:
本次 Hardhat 复现详细解析了 Discover 项目的闪电贷攻击,通过 攻击者合约的分析、交易执行流程的重现,以及 时序图展示,完整还原了攻击过程。
本次复现与 KEN 共同实现,欢迎各位技术研究者批评指正。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!