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

This article is not available in the current language yet. Showing the original version.
我们监控到 Ethereum 上针对 Equilibria 的攻击事件,本次攻击共造成约 63k USD 的损失。

背景介绍

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 获取奖励,循环操作提取了项⽬所有资⾦。建议项⽬⽅在设计经济模型和代码运⾏逻辑时要多⽅验证,合约上线前审计时尽量选择多个审计公司交叉审计。

Share to:

Author: 零时科技

Opinions belong to the column author and do not represent PANews.

This content is not investment advice.

Image source: 零时科技. If there is any infringement, please contact the author for removal.

Follow PANews official accounts, navigate bull and bear markets together
PANews APP
Grayscale has staked 102,400 ETH in the past 10 hours.
PANews Newsflash