1. 项目概述:DeFi收益聚合器漏洞分析
这个案例剖析了一个典型的DeFi收益聚合器合约漏洞攻击事件。攻击者利用合约中iToken代币配置错误和价格计算逻辑缺陷,通过精心设计的套利路径,最终从yTUSD协议中窃取了大量资金。整个过程涉及闪电贷、代币兑换、价格操纵等多个DeFi常见操作手法。
作为区块链安全研究员,我见过太多类似的案例。这类漏洞往往源于开发者在代币集成时的疏忽,以及对价格计算机制考虑不周。本文将详细拆解攻击流程,分析漏洞原理,并给出防范建议。对于DeFi开发者和安全审计人员来说,这个案例具有很高的参考价值。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 攻击流程与技术细节解析
2.1 初始状态与漏洞背景
在攻击发生前,yTUSD合约的状态如下:
- 合约持有41,500 TUSD
- 在AAVE协议中存有204,388 TUSD
- 其他DeFi协议(Compound、dYdX、Fulcrum)持有的TUSD数量为0
- 当前投资的协议为AAVE
关键漏洞点在于:
- Fulcrum协议的iToken配置错误:本应是iTUSD(TUSD的生息代币),却被错误设置为iSUSD(sUSD的生息代币)
- 价格计算函数_calcPoolValueInToken()存在设计缺陷,容易被操纵
注意:在DeFi开发中,代币地址配置错误是最常见但也最危险的错误之一。审计时必须对所有外部代币地址进行双重验证。
2.2 攻击步骤拆解
攻击者执行了以下精密的操作序列:
-
闪电贷准备:
- 通过Uniswap V3闪电贷借出165,305 sUSDe
- 将sUSDe兑换为215,192 sUSD
- 将sUSD兑换为213,848 iSUSD(Fulcrum协议的生息代币)
-
初始存款操作:
- 存入1,169,030 TUSD,获得732,294 yTUSD
- 此时_calcPoolValueInToken()返回值从245,889增至1,414,919
-
代币转移与价值操纵:
- 将213,848 iSUSD转入yTUSD合约
- _calcPoolValueInToken()值跃升至1,630,112
- 每个yTUSD对应的资产价值被人为提高
-
超额赎回:
- 销毁769,318 yTUSD,赎回1,414,919 TUSD
- 这个数量超过了合约实际持有的TUSD,触发从AAVE赎回
- 清空了合约和AAVE中的所有TUSD
-
rebalance操作:
- 调用rebalance()函数销毁iSUSD,赎回215,192 sUSD
- 由于AAVE已被清空,仅从iSUSD处赎回sUSD
- 这些sUSD被"锁定"在合约中无法取回
-
最终价格操纵:
- 向合约转入0.00000001 TUSD
- 此时_calcPoolValueInToken()被重置为0
- 存入1,000 TUSD,获得117,004,400,475,278,030 yTUSD
- 利用极小的实际资产和巨大的totalSupply进行价格操纵
2.3 关键函数漏洞分析
_calcPoolValueInToken()函数
这个函数的问题在于:
- 错误地将iSUSD计入TUSD价值
- 没有对代币价值进行实时验证
- 允许外部代币转入直接影响合约价值计算
solidity复制function _calcPoolValueInToken() internal returns (uint) {
return _balanceCompoundInToken()
.add(_balanceFulcrumInToken()) // 这里错误地计算了iSUSD价值
.add(_balanceDydxInToken())
.add(_balanceAaveInToken())
.add(_balance());
}
_balanceFulcrumInToken()函数
solidity复制function _balanceFulcrumInToken() internal view returns (uint) {
uint b = _balanceFulcrum();
if (b > 0) {
b = b.mul(IFulcrum(fulcrum).tokenPrice()).div(1e18);
}
return b;
}
问题在于:
- 使用的fulcrum地址指向iSUSD而非iTUSD
- tokenPrice()返回的是sUSD价格而非TUSD价格
3. 漏洞原理与攻击手法深度解析
3.1 价格操纵机制
攻击者利用了以下几个关键点:
-
代币价值计算缺陷:
- 转入iSUSD被错误计入TUSD总价值
- 人为抬高了_calcPoolValueInToken()返回值
-
超额赎回漏洞:
- 在价值被抬高后,可以赎回超过实际持有的TUSD
- 触发_withdrawSome()从AAVE赎回全部资金
-
rebalance后的价值销毁:
- 销毁iSUSD赎回的sUSD不被计入合约价值
- 相当于凭空销毁了之前计入的"虚假价值"
-
极小存款放大效应:
- 在pool value接近0时存入极小量TUSD
- 由于totalSupply巨大,计算出极高的shares
3.2 闪电贷的作用
攻击者使用闪电贷实现了:
- 无需自有大量资金即可发起攻击
- 在单笔交易中完成所有操作
- 规避市场价格波动风险
闪电贷模式:
- 借入sUSDe
- 执行攻击套利
- 归还贷款+利息
- 保留利润
4. 防御方案与最佳实践
4.1 代码层面的修复建议
- 代币地址验证:
solidity复制function setFulcrum(address newFulcrum) external onlyOwner {
require(IFulcrum(newFulcrum).supplyToken() == TUSD, "Wrong token");
fulcrum = newFulcrum;
}
- 价值计算加固:
solidity复制function _calcPoolValueInToken() internal returns (uint) {
uint verifiedBalance = _balance();
uint aaveBalance = _balanceAaveInToken();
// 对其他协议余额也进行类似验证
require(aaveBalance <= IERC20(TUSD).balanceOf(aavePool), "Invalid balance");
return verifiedBalance.add(aaveBalance).add(...);
}
- 存款/取款限制:
solidity复制function withdraw(uint shares) external {
uint amount = shares.mul(_calcPoolValueInToken()).div(totalSupply);
require(amount <= _availableBalance(), "Insufficient liquidity");
// ...
}
4.2 系统设计建议
-
引入时间加权平均价格(TWAP):
- 使用预言机获取TWAP价格
- 防止单笔交易内的价格操纵
-
实施存款/取款延迟:
- 大额操作需要等待期
- 给监控系统反应时间
-
多签名管理关键参数:
- 代币地址变更需要多签确认
- 防止单点失误
经验分享:在审计DeFi合约时,我通常会特别检查:
- 所有外部代币地址是否可验证
- 价格计算是否有防操纵机制
- 关键操作是否有速率限制
- 是否考虑了闪电贷攻击场景
5. 事件总结与行业启示
这个案例典型地展示了DeFi领域"组合性风险"——当多个协议交互时,可能产生开发者未预料到的漏洞。攻击者通过精心设计的交易路径,将多个看似无害的漏洞串联起来,最终实现了巨额套利。
对于项目方,这个案例的教训是:
- 必须对所有集成的外部协议进行完整验证
- 价格计算机制需要考虑极端情况
- 需要进行全面的攻击模拟测试
对于安全研究人员,这个案例的价值在于:
- 展示了复杂的多步骤攻击模式
- 凸显了代币集成时验证的重要性
- 提供了检测类似漏洞的思路框架
我在实际审计工作中发现,这类漏洞往往出现在:
- 项目急于上线,省略了完整测试
- 开发团队对某些协议的理解不够深入
- 安全审计没有覆盖所有边界情况
最后给开发者的建议是:在实现涉及多协议交互的复杂DeFi逻辑时,务必采用"防御性编程"思维,假设所有外部调用都可能被恶意利用,并据此设计防护机制。
