漏洞信息
漏洞报告
- https://code4rena.com/audits/2025-08-morpheus/submissions/S-198
漏洞背景
- https://docs.lido.fi/guides/lido-tokens-integration-guide#steth-internals-share-mechanics
StETH 是一种通过将 ETH 质押获取的 rebasing token,在用户持有的过程中余额会随着奖励的累加而自动增加。用户只需要持有 StETH 就可以获得 ETH 奖励,持有的 StETH 数量与可赎回的 ETH 数量是几乎相等的。
而实现这个 rebasing 功能依赖的是内部的 share 机制,share 的计算公式是
$$
shares = ETHAmount * TotalPooledETH / TotalShares
$$
ETH → StETH 的代码实现是这样的
而 StETH 的余额也是通过 shares 来计算得到的。
精度丢失问题
在进行转账时,会经过 StETH amount → shares → transfer shares → update StETH balance 的过程
而由于 StETH amount → shares 的环节是向下取整的,所以可能会存在在进行 transfer 时,实际收到的 StETH 数量比传入的参数要小的情况。
举例说明:
假设此时 StETH 中 shares : ETH = 1000 : 1500,UserA 向 UserB 进行转账
- UserA 持有 1500 StETH,对应 1000 shares
- Transfer 1300 StETH → shares = 1300 * 1000 / 1500 = 866.66… = 866
- UserB 收到 866 shares,对应 StETH = 866 * 1500 / 1000 = 1299
这就导致了 UserA 在调用 transfer 函数时传入的 amount 为 1300,而 UserB 实际上收到的代币数量为 1299。
漏洞案例
当合约对 AAVE 进行 supply 操作时,如果 token 为 StETH,可能会出现实际 supply 的 StETH 数量小于 amount_
的值。而在后续的 deposited
和 lastUnderlyingBalance
变量计算中则是直接累加上了 amount_
的值,使得它们的值比实际值要偏大。
而在后续的 distributeRewards() 函数中,首先会计算获得的 aToken 数量(等于所提供的 StETH 数量),随后用来减去 lastUnderlyingBalance
。如果此时 StETH 的转账发生了精度丢失,且 aToken 的奖励还没开始累计,这个减法操作将会发生下溢出,导致操作回滚。
修复建议
其实 Morpheus 在 User 往 Morpheus 协议转账这个层面有考虑到这个问题,所以采用的是转账前后账户余额差值(实际到账金额)作为 amount_ 的值。而在 Morpheus 协议向 AAVE 转账这个环节没有采用这种实践。
补充材料
- https://docs.lido.fi/guides/lido-tokens-integration-guide#wsteth
由于 StETH 的 rebasing 特性,在 DeFi 协议中使用起来可能会难以处理。所以为了解决这个问题,官方提供了 warp StETH 实现,也就是 WstETH。WstETH 的余额不会发生 rebasing,只能在转账、铸造和销毁时更改。
从代码实现来看,其实 WstETH 对应的就是 StETH 里面的 shares