零时科技 || Equilibria 攻击事件分析

2025年8月23日,区块链安全公司零时科技监控到以太坊上DeFi协议Equilibria遭受攻击,造成约6.3万美元损失。攻击者利用协议经济模型的设计漏洞,通过重复转移质押代币并领取奖励的方式非法获利。

  • 攻击流程:攻击者首先创建攻击合约,转入0.1 ETH并兑换为PENDLE代币,通过deposit和harvest操作积累ePendle。随后通过闪电贷借入大量ePendle,并存入协议获取stake-ePendle代币。
  • 漏洞分析:问题核心在于updateReward函数计算用户奖励时,直接与用户持有的stake-ePendle余额挂钩,且该代币可自由转移。攻击者通过将stake-ePendle转移至新合约地址,并反复调用getReward函数,实现了奖励的重复领取。
  • 技术细节earned函数内奖励计算未考虑代币转移历史,仅依赖当前余额与全局奖励参数,导致转移后新地址可重复累积并提取奖励。
  • 安全建议:项目方应在经济模型设计阶段加强多方验证,并采用多家审计公司进行交叉审计,以避免类似逻辑漏洞。
总结

背景介绍

2025年8⽉23⽇,我们监控到 Ethereum 上针对Equilibria 的攻击事件

https://etherscan.io/tx/0x185a16017fb4d9b2fefdf5935435253d53d4758238275426b507fe54eb4fe97a

攻击共造成约63k USD 的损失。

攻击及事件分析

⾸先,攻击者攻击者创建了⼀个攻击合约,转入0.1 ETH后通过Deposit获取了0.1 WETH。

然后,攻击者使⽤了 0.1 WETH 兑换了 7.9 PENDLE ⽤于后续的攻击。

接着,攻击者通过 deposit 和 harvest 来将 PENDLE 存入 Equilibria 和获取收益再投资,⽬的是获取更多的 ePendle 。

接着,攻击者⼜利⽤ flashloan 借了 17029 ePendle ,便开始真正的攻击流程。

在攻击者真正的攻击流程中,⾸先通过 depositAll 来存入从 flashloan 借来的 ePendle 和之前使⽤ 0.1 ETH 兑换的 ePendle 获得了 stake-ePendle 。随后创建⼀个新的合约,将 stake-ePendle 转给新合约,利⽤ getReward 获取奖励。

我们⾸先看⼀下 depositAll 函数:

function depositAll() external returns (uint256) {

return deposit(ependle.balanceOf(msg.sender));

}

发现函数其实调⽤了 deposit ,那我们看⼀下 deposit 的具体实现:

function deposit(

uint256 _amount

)

public

nonReentrant

updateReward(msg.sender, userHarvest)

returns (uint256)

{

require(

_amount > 0,

"VaultEPendle deposit: amount must be greater than zero"

);

uint256 balanceBefore = balance();

ependle.safeTransferFrom(msg.sender, address(this), _amount);

uint256 shares = 0;

if (totalSupply() == 0) {

shares = _amount

} else {

shares = (_amount * totalSupply()) / balanceBefore;

}

_mint(msg.sender, shares);

ePendleRewardPool.stake(_amount);

emit Deposited(msg.sender, _amount);

return shares;

}

可以看出,这个 deposit 逻辑比较简单,通过 modifier 函数 updateReward 在 deposit 时更新⽤户的 reward ,最后再 stake ,接下来我们看⼀下 updateReward 的具体实现:

modifier updateReward(address _account, bool needHarvest) {

if (needHarvest) {

harvest();

}

for (uint256 i = 0; i < rewardTokens.length; i++) {

address rewardToken = rewardTokens[i];

UserReward storage userReward = userRewards[_account][rewardToken];

userReward.rewards = earned(_account, rewardToken);

userReward.userRewardPerTokenPaid = rewards[rewardToken]

.rewardPerTokenStored;

}

_;

}

function earned(

address _account,

address _rewardToken

) public view returns (uint256) {

Reward memory reward = rewards[_rewardToken];

UserReward memory userReward = userRewards[_account][_rewardToken];

return

((balanceOf(_account) *

(reward.rewardPerTokenStored -

userReward.userRewardPerTokenPaid)) / 1e18) +

userReward.rewards;

}

问题就出现在 earned 函数中,在该函数中,计算⽤户的 reward 时,参数包含了⽤户 stake-ePendle 的 balance 。所以,⽤户可以通过 flashloan 获取⼤量的 ePendle token 后,通过 stake 获取 stake-ePendle ,再 transfer 给新的地址来获取重复的 stake 收益。

攻击者通过反复进⾏ transfer stake-ePendle 到新地址,然后再 getReward 获取收益,最终获利 63k USD 。

总结

本次漏洞的成因是函数 updateReward 函数在计算 Reward 时, Reward 的值和⽤户的 stake-ePendle 的 token 余额有关,且stake-ePendle 可以通过 transfer 到另外⼀个地址。最终,导致攻击者通过反复进⾏ transfer stake-ePendle 到新的合约地址再 getReward 获取奖励,循环操作提取了项⽬所有资⾦。建议项⽬⽅在设计经济模型和代码运⾏逻辑时要多⽅验证,合约上线前审计时尽量选择多个审计公司交叉审计。

分享至:

作者:零时科技

本文为PANews入驻专栏作者的观点,不代表PANews立场,不承担法律责任。

文章及观点也不构成投资意见

图片来源:零时科技如有侵权,请联系作者删除。

关注PANews官方账号,一起穿越牛熊
推荐阅读
2025-12-07 06:24
2025-12-05 14:50
2025-12-05 14:15
2025-12-05 09:22
2025-12-03 13:00
2025-12-02 06:50

热门文章

行业要闻
市场热点
精选读物

精选专题

App内阅读