以太坊虚拟机(EVM)及 Solidity 智慧合约问世后,重入攻击事件频传。本文 Amber Group 安全团队将分享并解析他们在 8月所发现币安智能链(BSC)项目 Dexfolio 的 LPFarming 合约漏洞案例。
(前情提要:DeFi 干货|Dinosaur Eggs 的流动性池漏洞解析 – Amber 安全团队)

(本文由 Amber Group 研究团队撰着、提供。)

在以太坊虚拟机(EVM)及 Solidity 智慧合约问世后,短短数年间内就出现了许多重入攻击事件。 2021 年 8 月 15日,我们发现币安智能链(BSC)项目 Dexfolio 的 LPFarming 合约存在一个可被重入攻击的漏洞,并透过漏洞悬赏平台 ImmuneFi通报了这个漏洞。

由于 Dexfolio 研发团队并未在过去的 120 天内公布事后剖析报告,因此我们将在本篇文章中提供相关细节。

0x00: Dexfolio

Dexfolio 的 LPFarming 合约可让用户以质押资产的方式来进行流动性挖矿。 对于用户所持有的每项资产,LPFarming合约提供了四个公用函数处理质押任务。

例如: LPFarming.stake() 函数可让使用者将币安币(BNB)转入合约,并将半数的 BNB 转换为 DEXF 代币,用以铸造 DEXF- WBNB LP 代币后,质押 LP 代币。

从上方的程式码片段可看到,在第 555 行 newBalance 由当前余额扣掉原始余额 initialBalance 计算出新铸造的 DEXF- BNB LP 代币数量。在第 560 行,新的质押记录被加入了 _stakes 阵列,作为计算挖矿奖励的依据。

0x01:漏洞

在这四个质押函数中, stakeToken() 是一个较为特殊的函数,它可以让使用者透过支付任意一种 ERC20 代币来交换 DEXF-BNB LP代币以作为质押资产。

然而,我们发现此处并未具有防止重入的 nonReentrant modifier。由于使用者可以传入任意的 fromTokenAddress 执行 stakeToken() ,因此很多的 ERC20 函数调用 (例如 transfer()、transferFrom() 及 approve() 等) 都可能被用于劫持控制流程或重入 stakeToken()

在上方的第 647 行程式码中可以看到,initialBalance 备份了后续代币交换前的余额以便计算第 651 行新铸造的 DEXF-BNB LP代币数量。然而, 649 行的 swapAndLiquifyFromToken() 调用在内部执行了 fromTokenAddress.approve(), 以利在 PancakeSwap 上交换代币。

这使得不法分子可于原始 stakeToken() 调用的主体内嵌入另一个质押操作,导致 651 行的 newBalance 变大。 简言之,攻击者可能会针对同一批 LP 代币进行双重质押。

例如,攻击者调用 stakeToken(10), 并透过另一个帐户及 fromTokenAddress.approve() 嵌入另一个 stakeToken(90) 。 最终,第一个帐户持有 10 + 90 = 100 个质押的 LP 代币,而第二个帐户则持有 90 个,后者的 90在此处经过了重复计算。

乍看下,因为第二个帐户必须执行某些程式码才能重入 stakeToken() ,故第 638 行的 isContract 检查可防止重入。 然而,在 LPFarming 里的 isContract 的实作无法涵盖所有情况,例如在 constructor 里就能实现绕过检查的恶意程式码。

0x02:漏洞利用

为利用重入漏洞,我们需要一个恶意 ERC20 合约 (Ftoken) 来劫持 approve() 调用。 一如下方的程式码片段所示, Ftoken 透过 _optIn 开关覆写了 OpenZeppelin ERC20 实作的 approve() 函数。

当开关开启时,创建 Exp 合约并在其 constructor 中嵌入上文提及的 stakeToken() 调用,以便避开有漏洞的 isContract 检查。

由于 LPFarming 的 isContract modifier 仅检查某地址对应的 extcodesize ,我们可透过执行 Exp合约中建构函数 (constructor) 里的 LPFarming.stakeLPToken() 来绕过保护机制,如下所示。

若真的很希望避免使用合约帐户,则须确保检查 tx.origin == msg.sender

此处遗漏了一个部分。由于 stakeToken() 在 PancakeSwap 将 fromTokenAddress 资产转换成 DEXF-BNBLP 代币,我们必须创造 Ftoken-BNB 对并增加其流动性。我们透过另一个 Lib 合约来实现此一目的。

下方的 Lib.trigger() 函数可使我们在 PancakeSwap 上创造 Ftoken-BNB 对,并将与 Ftoken 数量相同的 WBNB 放入流动池中。 此外,我们也加入了一个 Lib.sweep() 方便 owner 在完成攻击后搜刮流动池中所有剩余的 WBNB。

备妥这三份合约后,我们就可以进行实验以验证我们的理论。 如下方 eth-brownie 截图所示,我们先从 21 WBNB 开始,并部署了 FtokenLib 合约。

如前所述,我们使用 Exp 合约的 constructor 来重新质押部分 LP 代币并通过 LPFarming.getStakes() viewfunction 可以观察到 Exp 合约目前所持有的份额数量。

由于 Exp 合约需要 LP 代币用于重新质押,但直到 Ftoken.approve() 中 Exp 才会被创造,如此处所示范的 1,我们用 Ftoken 合约地址预先以 eth-util 计算出 Exp 地址,并且将 LP 代币转过去。

准备好 FtokenLib, 并以 Lib.trigger() 创造 Ftoken-BNB 对后,我们便可执行第一个 stakeToken() 发起重入攻击。

如上图所示,我们在 stakeToken() 调用前后均执行 Ftoken.optIn() ,以便启动及切换 Ftoken.approve() 中的劫持机制。

最后,我们用 LPFarming.emergencyWithdraw() 提取 LP 代币的数额并转换为 WBNB。 此外, 我们也执行 Lib.sweep() 以便获取 Ftoken-BNB 流动池中其余的 WBNB。

最终我们取得了 19.36 WBNB,并在 LPFarming 合约中留下 Exp 合约的质押记录。 由于 Exp 合约已部署于 Ftoken.approve() 调用中,我们无法重新初始化合约以及在 constructor 中再次执行 LPFarming.emergencyWithdraw() ,因此,攻击者似乎无法从中获利。 然而实际上,CREATE2 指令可使我们能够重新初始化 Exp 合约。

0x03: CREATE2

CREATE2 指令是以太坊 Constantinople 硬分叉后的产物,能使用户透过特定合约的位元组程式码及 salt 值预先计算合约地址。 伴随而来的结果是,如果合约以 SELFDESTRUCT 指令自毁,同样的位元组程式码及 salt 值可能会再次被部署在同一个地址上。

利用这个特性,我们可以让 Ftoken.approve() 部署 Exp 合约并执行重入后执行 SELFDESTRUCT。之后,我们可重新部署 Exp合约并执行 LPFarming.emergencyWithdraw() 以提取双重质押的 LP 代币。

上图显示了变更后的 Ftoken.approve() 。有四个参数传递至 CREATE2 调用。 第一个 0 代表创造合约时支付了 0 以太币,最后一个 0 是 salt 值,其应与重新建立合约时的值相同。 第二、第三个参数均与我们要部署的位元组程式码相关。

除了变更后的 Ftoken.approve() 外,我们还新增了 getAddress() view 函数以预先计算 Exp 合约地址。 我们在这里只须根据 EIP-1014,以 0xff、创建者地址及 32 位元组的 salt 值及合约程式码 bytecode。

除了上述的修改,我们也调整了 Exp 合约,根据 token 合约所保持的状态来质押或提取 LP 代币,并以 SELFDESTRUCT指令自毁的方式允许下次的重新部署操作。

在 CREATE2 的帮助下,我们的 Exp 合约便可赚取利润,如下方截图所示:

我们成功地在区块高度 10181384 从 LPFarming 合约中获取 1,558 个 LP 代币,并将其转换为 WBNB。

0x04:事件的时间轴与致谢

我们在 2021 年 8 月 15 日将此问题回报给 ImmuneFi 平台 2, 且 Dexfolio 团队已要求使用者于 2021 年 8 月 20日提取其资产 3。在我们通报之后,ImmuneFi 随即告知我们,此漏洞报告有可能是重复回报,但须与 Dexfolio 团队确认。

由于我们已逾 90 天未能从 ImmuneFi 或 Dexfolio 得到回应,因此我们选择以独立研究的方式揭露细节。 在我们表示有意揭露细节后,ImmuneFi 便为这份有效的报告向我们适当地揭露流程,于 2021 年 11 月 26 日赠予我们 $1000美元等值的以太币(ETH)以作为奖励。 此外,ImmuneFi 也协助我们联系了最初回报同样漏洞的「白帽」骇客 lucash-dev。

关于 Amber Group

成立于 2017 年,业务遍及亚洲及欧美各大主要城市,现为 1,000 多家知名大型机构客户提供加密金融服务,在 100 多个电子交易所中累计交易总额已超过 1 兆美元,资产管理规模超过 40 亿美元,帮助客户管理各种加密资产的风险,提供灵活化的投资、最大化的回报来优化长期价值

Amber Group官方网站: www.ambergroup.io

WhaleFin官方网站: www.whalefin.com

若有产品相关问题,请联系 Amber 客服团队: email protectedemail protected

📍相关报导📍

Amber Group估值「涨10倍达10亿镁」、AUM20亿镁,加密金融独角兽正野心扩展全球!

全解析 | 不可不知的 3种「Defi重入攻击」: 基本概念、背后细节、手法 — Amber资安负责人

干货 | Amber 安全专家吴博士:剖析 BSC 的闪电贷攻击手法,如何再引发 3 个分叉项目连环爆?

LINE 与 Messenger 不定期为大家服务

Leave a Reply

Required fields are marked *