2023年8月3日,Arbitrum上的一个MEV Bot遭到攻击,导致80万美元的损失。此次攻击的根本原因是用户输入验证不足。
考虑到MEV Bot与其合约之间复杂的交互未经验证,这表明非开源并不保证安全,尤其对于DeFi协议而言。
背景
MEV Bot
MEV Bot(最大可提取价值机器人)旨在识别并执行区块链上的盈利机会。 它通过分析待处理的交易(即内存池中的交易)或链上状态来产生套利利润。
与典型的抢先交易和三明治攻击MEV Bot不同,此次攻击的目标MEV Bot专注于执行套利和债务清算等策略。这些Bot本身有助于稳定AMM的价格,并协助借贷协议进行清算以确保顺畅运行,构成了DeFi生态系统健康运行的重要组成部分。
Flashloan
Flashloan代表了DeFi生态系统中的一项独特创新——一种无抵押借贷形式。通过Flashloan,您可以借入高达十亿美元的资金,而无需任何抵押品,前提是贷款必须在同一笔交易中偿还。如果在同一笔交易中未偿还贷款,则该交易将被撤销,就好像交易从未发生过一样。
这种机制通常用于套利或利用其他DeFi策略来剥削临时的市场低效率。
漏洞
简述
对用户输入参数验证不足,使得攻击者能够引入一个FakeFlashloanProvider。Vault合约利用这个Provider发起了一笔闪电贷。随后,可能是为了结算闪电贷,Vault合约向FakeFlashloanProvider批准了代币,导致资产未经授权地从Vault中转出。
详细版本
被利用的合约是:
Vault: 受害者合约0xd614927acfb9744441180c2525faf4cedb70207f作为“Vault”,提供储备金并促进AAVE和Balancer等其他协议的闪电贷。Arbitrage Bot: 易受攻击的合约0x8db0efee6a7622cd9f46a2cf1aedc8505341a1a7,在“Vault”合约中充当“套利机器人”的角色,持有借款人身份。
“套利机器人”中的函数 0x0582f20f() 是启动套利的主要入口点。
它首先调用“Vault”中的 borrow() 来获取原始本金,然后通过 delegatecall 调用calldata中指定的外部合约来执行套利逻辑,并且没有进行任何验证。
function 0x0582f20f(...) {
...
v67, /* uint256 */ v68 = address(0xd614927acfb9744441180c2525faf4cedb70207f).borrow(address(v39), address(v9[0]), v29).gas(msg.gas);
...
// 0x4da91757 = swap(address,address,address,uint256,uint256,uint256,address)
MEM[MEM[64] + 32] = uint224(address(MEM[0 + v4[v69]])) | 0x4da9175700000000000000000000000000000000000000000000000000000000;
v82 = address(v76 >> 96).delegatecall(MEM[(MEM[64]) len 228], MEM[(MEM[64]) len 0]).gas(msg.gas);
...
v189 = v170.refund(0x410085df, address(v9[0]), address(v39), v68, address(v9[0]), v29, v186, 4 + MEM[64] + (varg2.length << 5) - (4 + MEM[64]) + 192).gas(msg.gas);
...
}
随后,它调用“Vault”中的 0x512b7351(),向攻击者的 FakeFlashloanProvider 合约发起闪电贷。
函数 0x512b7351() 要求 msg.sender 在允许列表中,但它被先前的 delegatecall 成功绕过,从而规避了检查。 这是一个非常关键的步骤
function 0x512b7351(...) public nonPayable {
...
if (_borrow[msg.sender] >= 1) {
v0 = !_refund;
}
require(v0, Error('BBVault: FORBIDDEN'));
...
v38 = v23.length;
v39 = v23.data;
_refund = keccak256(v23);
...
<FakeFalshloanProvider>.flashloan(...);
...
}
在闪电贷回调期间,“Vault”中的 executeOperation() 首先将借入的资产转移给“套利机器人”MEVBot 0x8db0ef,然后调用其 0x7fe3ba8b()。
function executeOperation(...) {
...
require(_refund == keccak256(v3.data), Error('BBVault: STATUS'));
Token.transfer(ArbitrageBot, amountBorrowed);
<ArbitrageBot>.call(0x7fe3ba8b...);
}
“套利机器人”信任此外部调用,将收到的资产转回 FakeFlashloanProvider。
然而,“Vault”未能识别这一点,仍在 executeOperation() 结束时授予 FakeFlashloanProvider 批准以偿还闪电贷。
攻击过程
攻击交易:0x864c8cfb8c54d3439613e6bd0d81a5ea2c5d0ad25c9af11afd190e5ea4dcfc1f
攻击者调用“套利机器人”的 0x0582f20f(),该函数继而对攻击者的合约执行 delegatecall。


然后 hack_contract_2 调用 victim 的函数 0x512b7351()。0x512b7351() 要求 msg.sender 在允许列表中,但它被先前的 delegatecall 成功绕过,从而规避了检查。

然后 victim 调用攻击者的 FakeFlashloanProvider 合约,将所有闪电贷资产转入 victim 并调用 victim 的 executeOperation()。

“套利机器人”的 0x7fe3ba8b() 再次对 攻击者合约 执行 delegatecall,这次将所有资产转回给攻击者。
此时,攻击者闪电贷提供商提供的资产已被偿还。

受害者(“Vault”)批准代币给 FakeFlashloanProvider,可能是为了偿还闪电贷。

攻击者利用此批准,通过 transferFrom 从受害者处提取资金,从而获利。
安全建议
非开源代码不保证安全
认为非开源和混淆的代码能保证安全是一种误解。此次MEV Bot事件表明,保密并不能防范攻击,反而可能给开发者带来虚假的安全感。
严格的输入验证
在处理闪电贷和交换回调等标准接口时,必须仔细验证所有合约交互和calldata。在合约设计和实现中,确保数据的完整性和安全性应是优先考虑的事项。
阅读本系列其他文章:
- 导语:2023年十大“惊人”安全事件
- 第一名:利用Flashbots Relay中的漏洞收割MEV Bot
- 第二名:Euler Finance事件:2023年最大的黑客攻击
- 第三名:KyberSwap事件:通过极其精妙的计算, masterfully利用了舍入误差
- 第四名:Curve事件:编译器错误导致无害源代码生成有缺陷的字节码
- 第五名:Platypus Finance:侥幸逃过三次攻击
- 第六名:Hundred Finance事件:催化了易受攻击的forked协议中与精度相关的漏洞浪潮
- 第七名:ParaSpace事件:一场与时间赛跑,阻止了行业迄今为止最关键的攻击
- 第八名:SushiSwap事件:笨拙的救援尝试导致了一系列模仿攻击
- 第十名:ThirdWeb事件:受信任模块之间的不兼容暴露了漏洞



