攻击者通过 Popsicle Finance 在机枪池实现上的漏洞发起攻击,获利超过 2000 万美元。
撰文:BlockSec
北京时间 2021 年 8 月 4 日早上 6 点(区块 12955063),Popsicle Finance 项目下的多个机枪池被攻击,损失金额超过两千万美元,是迄今为止 DeFi 领域发生的损失数额最大的单笔攻击之一。通过分析 攻击交易 及项目代码我们发现,此次攻击是一个利用项目的记账漏洞进行多次提取的攻击(Double-Claiming Rewards)。下面我们通过代码和攻击流程分析此次攻击。
代码分析Popsicle Finance 是一个涉及多个链的机枪池(Yield Optimization Platform)。
用户首先调用 deposit 函数向机枪池存入一定的流动性,并获得 Popsicle LP Token (以下简称 PLP Token)作为存款的份额证明。Popsicle Finance 会将用户提供的流动性存入 Uniswap 等底层池子并获得收益。
![[虚拟印钞机] Popsicle Finance 双花攻击分析](https://www.fxajax.com/zb_users/upload/2021/08/20210805110009162813240990454.jpg)
用户还可以调用 withdraw 函数,根据用户持有的 PLP Token 所代表的流动性份额,从机枪池取回流动性。Popsicle Finance 会将 PLP Token 对应的流动性从 Uniswap 等底层池子中取回给用户。
![[虚拟印钞机] Popsicle Finance 双花攻击分析](https://www.fxajax.com/zb_users/upload/2021/08/20210805110011162813241172975.jpg)
最后,用户在机枪池中存的流动性会随着时间产生一定的收益(Yield),会累计在合约的用户状态中。用户可以调用 collectFees 函数取回部分存款奖励。
![[虚拟印钞机] Popsicle Finance 双花攻击分析](https://www.fxajax.com/zb_users/upload/2021/08/20210805110012162813241264102.jpg)
本次攻击的核心函数正是 collectFees 函数。下面我们逐步分析其代码。首先获得存储在 userInfo 中的用户状态。其中用户状态中的 token0Rewards 和 token1Rewards 是由于用户存款而累积的奖励。
接下来计算该合约中,对应机枪池的 Token 对的 Balance。如果在合约中有足够的 Balance,就按金额将 Reward 支付给用户;否则会调用 pool.burnExactLiquidity 从底层 pool 取回流动性返回给用户。
最后,会将记录在 userInfo 中的 Rewards 状态进行更新。看到这里,机枪池的代码实现还是比较符合逻辑的。但是在函数开头我们发现了 updateVault modifier,这个函数会在 collectFees 的函数体之前运行,漏洞也许在 updateVault 相关的函数中。
![[虚拟印钞机] Popsicle Finance 双花攻击分析](https://www.fxajax.com/zb_users/upload/2021/08/20210805110013162813241339927.jpg)
以上是 updateVault 相关函数的实现。过程如下:
首先调用_earnFees 向底层 pool 获取积累的 Fee;随后调用_tokenPerShare 更新 token0PerShareStored 和 token1PerShareStored 参数,这两个参数代表了池子中每个 share 代表的 token0 和 token1 的数量,即机枪池的每个份额计代表的 Token 对数量;最后调用_fee0Earned 和_fee1Earned 更新对应到这个用户的存款 Rewards (即 user.token0Rewards 和 user.token1Rewards)。![[虚拟印钞机] Popsicle Finance 双花攻击分析](https://www.fxajax.com/zb_users/upload/2021/08/20210805110014162813241476923.jpg)
以上是_fee0Earned 和_fee1Earned 函数的实现,两个函数实现相同,都实现了这样一个公式(以_fee0Earned 为例):
user.token0Rewards += PLP.balanceOf(account) * (fee0PerShare - user.token0PerSharePaid) / 1e18
也就是说,该函数会在原有的 user.token0Rewards 基础上,根据用户拥有的 PLP Token 数量计算应给用户发放的 Fee 的份额。
但我们注意到这个函数是增量的,也就是说即使用户并没有持有 PLP Token (PLP.balanceOf(account) 为 0),该函数仍会返回保存在 user.token0Rewards 中记账的存款奖励。
因此对于整个合约,我们发现两个重要的逻辑缺陷:
用户的存款奖励是记录在 user.token0Rewards 和 user.token1Rewards 中的,并不与任何 PLP Token 或其他东西有任何形式的绑定。用于取回存款收益的 collectFees 函数仅仅依赖于记账的 user.token0Rewards 和 user.token1Rewards 状态,即使用户并未持有 PLP Token,仍可以取出对应的存款奖励。我们假想一个攻击流程:
攻击者向机枪池中存入一定的流动性,获得一部分 PLP Token。攻击者调用 collectFees(0, 0),后者会更新攻击者的存款奖励,即状态变量 user.token0Rewards 的值,但并没有真正取回存款奖励。攻击者将 PLP Token 转给自己控制的其他合约,再调用 collectFees(0, 0) 更新状态变量 user.token0Rewards。也就是说通过不断地流转 PLP Token 并调用 collectFees(0, 0),攻击者复制了这些 PLP Token 对应的存款奖励。最后,攻击者从以上各个地址调用 collectFees 函数,取回真正的奖励。此时虽然这些账户中并没有 PLP Token,但由于记账在 user.token0Rewards 没有更新,攻击者因此得以取出多份奖励。用现实生活中的例子来描述这个攻击,相当于我向银行存钱,银行给了我一张存款凭证,但这张凭证没有防伪措施也没有和我绑定,我把凭证复印了几份发给不同的人,他们每个人都凭借这个凭证向银行取回了利息。
攻击流程分析通过以上的代码分析,我们发现了 Popsicle Finance 在机枪池实现上的漏洞。下面我们对攻击交易进行深入分析,看攻击者是怎样利用这个漏洞的。
攻击者的总体流程如下:
攻击者创建了三个交易合约。其中一个用于发起攻击交易,另外两个用于接收 PLP Token 并调用 Popsicle Finance 机枪池的 collectFees 函数取回存款奖励。通过闪电贷从 AAVE 借出大量流动性。攻击者选择了 Popsicle Finance 项目下的多个机枪池,向 AAVE 借出了对应这些机枪池的六种流动性。进行 Deposit-Withdraw-CollectFees 循环。攻击者一共进行了 8 次循环,分别攻击了 Popsicle Finance 项目下的多个机枪池,取出了大量流动性。向 AAVE 归还闪电贷,并将获利通过 Tornado Cash 洗钱。![[虚拟印钞机] Popsicle Finance 双花攻击分析](https://www.fxajax.com/zb_users/upload/2021/08/20210805110015162813241557016.png)
本次攻击交易主要由数个 Deposit-Withdraw-CollectFees 循环构成,每一个循环的示意图如上图所示。根据我们的分析,逻辑如下:
攻击者首先将闪电贷借来的流动性存入机枪池中,获得一定量的 PLP Token。攻击者将 PLP Token 转给攻击合约 2。攻击合约 2 调用机枪池的 collectFees(0, 0) 函数,设置合约 2 对应的 user.token0Rewards 和 user.token1Rewards 状态。攻击合约 2 将 PLP Token 转给攻击合约 3。和攻击合约 2 的操作类似,攻击合约 3 调用机枪池的 collectFees(0, 0) 函数,设置合约 2 对应的 user.token0Rewards 和 user.token1Rewards 状态。攻击合约 2 将 PLP Token 转回攻击合约,后者调用机枪池的 withdraw 函数 Burn 掉 PLP Token,取回流动性。攻击合约 2 和攻击合约 3 调用 collectFees 函数,用虚假的 tokenRewards 状态取回了存款奖励。根据我们的以太坊交易 追踪可视化系统 给出的交易调用图如下,其中部分重要交易用红字进行标注:
![[虚拟印钞机] Popsicle Finance 双花攻击分析](https://www.fxajax.com/zb_users/upload/2021/08/20210805110018162813241839156.png)
本次攻击一共获利:2.56k WETH,96.2 WBTC,160k DAI,5.39m USDC,4.98m USDT,10.5k UNI,获利共计超过 20,000,000 美元。
在此次攻击之后攻击者通过首先通过 Uniswap 和 WETH 将攻击获得的其他 token 全部换成 ETH,然后通过多次使用 Tornado.Cash 将 ETH 洗白。
21Shares携手法国兴业银行,强化德国及东欧市场加密ETP流动性
eToro新增29种加密资产,含17种高风险实验性代币
Broadridge推出数字资产解决方案:助力金融机构合规扩展加密市场
CME集团6月30日推出现货报价期货:比特币、以太坊及标普500等长达5年持有
eToro推出加密资产转账与兑现功能:比特币、以太坊可无缝投资多资产
nxtAssets GmbH携手SIX推出比特币与以太坊ETP:加密资产交易总数达398
Ben Bilski 2025年新动作:打造颠覆性加密货币交易平台
Clearstream携手Crypto Finance推出加密资产托管服务,德意志交易所集团引领机构投资者新风潮
西班牙BBVA获批推出比特币和以太坊交易服务,零售客户可通过手机银行直接管理加密资产
Bybit遭遇重大黑客攻击,15亿美元数字货币被盗
Bybit遭遇安全漏洞,1.4亿美元以太坊代币被盗,市场反应激烈
Bybit交易所遭黑客攻击,14亿美元ETH被盗,资金安全隐患引发行业关注
LandX Finance与Dominion Blockchain Solutions合作推动DeFi创新
以太坊 ETF 在美国上市首日交易量达 10 亿美元,占比特币交易量的 23%
现货以太 ETF 将于今日在 SEC 最终批准后开始交易
芝商所推出以微欧元计价的比特币和以太坊期货
DeFi 孵化器 AQRU 進行進一步的運營審查
Visa (NYSE:V) 設想在以太坊網絡上進行自動支付
MetaMask“Portfolio Dapp”新增跨链桥服务
0xScope:Alameda以太坊上627个地址总资产约1亿美元



