Appearance
揭秘“天真接收者”:一场闪电贷的财富大挪移
在DeFi的世界里,安全与漏洞往往是一枚硬币的两面。今天,我们将深入探讨一个名为“Naive Receiver”的CTF挑战,它巧妙地揭示了闪电贷在缺乏严谨验证下的潜在风险。准备好了吗?一场关于“WETH”财富的追逐战即将打响!
挑战背景:当闪电贷遇上“天真”的接收者
想象一下,一个拥有1000 WETH(一种与以太坊锚定的代币)的闪电贷池,它就像一个慷慨的借贷平台,但有一个固定的1 WETH手续费。更重要的是,它整合了一个“BasicForwarder”,允许用户通过元交易(meta-transactions)来执行操作,这意味着用户无需直接支付Gas费,而是由Forwarder来处理。
与此同时,我们还有一个“用户”部署了自己的合约,里面有10 WETH。这个合约看起来似乎也能执行闪电贷,并且其设计存在一个关键的“天真”之处。
目标:拯救所有WETH,送往指定账户
我们的任务是:将用户合约和闪电贷池中所有的WETH安全地转移出来,并存入指定的“恢复账户”。 这是一场与时间的赛跑,也是对智能合约安全理解的终极考验。
核心漏洞:FlashLoanReceiver 的“信任危机”
理解这个挑战的关键在于分析FlashLoanReceiver.sol合约。它实现了IERC3156FlashBorrower接口,用于接收闪电贷。然而,仔细审视onFlashLoan函数,我们会发现一个令人担忧的细节:
solidity
assembly {
// gas savings
if iszero(eq(sload(pool.slot), caller())) {
mstore(0x00, 0x48f5c3ed)
revert(0x1c, 0x04)
}
}
这段汇编代码的意图是检查调用者caller()是否是合约部署时指定的pool地址。如果不是,就抛出错误。但是,这个检查存在一个严重的逻辑缺陷!
caller()在 Solidity 中指的是直接调用当前合约的地址。而当BasicForwarder合约通过execute方法调用FlashLoanReceiver.sol时,msg.sender(在NaiveReceiverPool.sol的_msgSender方法中被处理)会是BasicForwarder的地址,而不是真实的调用者(即我们的玩家账号)! 即使BasicForwarder的trustedForwarder()返回的是address(this),FlashLoanReceiver的onFlashLoan函数中的pool.slot存储的是NaiveReceiverPool的地址,并且caller()也指向BasicForwarder,这段汇编代码却错误地认为caller()就是pool,从而绕过了对真正发起闪电贷请求的源头的验证。
换句话说,FlashLoanReceiver“天真地”相信任何能够通过BasicForwarder与其交互的合约,都是合法的闪电贷接收者,而忽略了真正的发送者验证。
利用漏洞:多重闪电贷的连锁反应
一旦我们识别出这个漏洞,解决方案就变得清晰起来:
利用
NaiveReceiverPool的multicall功能: 由于NaiveReceiverPool合约继承了Multicall,我们可以连续多次调用flashLoan函数。每次闪电贷都会将一部分WETH从池子转移到FlashLoanReceiver,并收取1 WETH的费用。不断循环借贷: 我们可以通过
multicall循环调用flashLoan,每一次都借出尽可能多的WETH(受限于maxFlashLoan,即池子当前的WETH余额),然后将借到的WETH返回给FlashLoanReceiver。这样一来,FlashLoanReceiver的WETH余额会不断增长,因为它同时接收了闪电贷的本金和返回的本金+费用。执行“窃取”操作: 当
FlashLoanReceiver累积了巨量的WETH后,我们需要让NaiveReceiverPool将所有的WETH(包括池子原本的和FlashLoanReceiver中累积的)转移到指定的recovery账户。- 首先,我们要构建一个
withdraw函数调用,将NaiveReceiverPool中所有的WETH(池子初始的1000 WETH +FlashLoanReceiver中累积的WETH)提取出来,并指定recovery地址作为接收者。 - 然后,利用
BasicForwarder的元交易功能,由玩家签名,将这个withdraw请求发送给NaiveReceiverPool。因为BasicForwarder绕过了对FlashLoanReceiver的严格验证,我们可以用它来执行对NaiveReceiverPool的任意操作,包括最终的提款。
- 首先,我们要构建一个
总结:
“Naive Receiver”挑战巧妙地展示了,即使是看似独立的智能合约组件,如果设计中存在逻辑盲点,也可能被利用来造成巨大的损失。通过深度理解合约交互流程和利用BasicForwarder的元交易特性,我们可以“欺骗”FlashLoanReceiver,使其成为我们转移大量WETH的“临时仓库”,最终将所有资产安全地送达目的地。
这个挑战提醒我们,在DeFi开发中,每一个细节都至关重要,严谨的安全审计是必不可少的环节。