20250823 - Equilibria 攻擊事件分析:獎勵計算公式不要輕易采用 balanceOf 做參數(shù)呀!
背景信息
Equilibria Finance 是一個為 $PENDLE 代幣持有者提高收益的 DeFi 協(xié)議。其主要功能包括將 PENDLE 代幣轉(zhuǎn)換為 ePENDLE(PENDLE --lock--> vePENDLE --mint--> ePENDLE),將 ePENDLE 進(jìn)行 stake 后獲得 stk-ePENDLE 等,目的是提升流動性提供者的收益。除此以外,Equilibria 還為 stk-ePENDLE 的持有者提供了 xEQB 和 vlEQB 代幣激勵。
本次攻擊的原因是 stk-ePENDLE 相關(guān)的獎勵計算邏輯錯誤,在 stk-ePENDLE 代幣可以轉(zhuǎn)移的前提下,根據(jù)用戶持有的 stk-ePENDLE 代幣余額來進(jìn)行獎勵計算。使得攻擊者可以通過一筆 stk-ePENDLE 代幣在多個賬戶間重復(fù)利用來領(lǐng)取超額的獎勵。
- 攻擊交易:https://app.blocksec.com/explorer/tx/eth/0x185a16017fb4d9b2fefdf5935435253d53d4758238275426b507fe54eb4fe97a
- 漏洞合約:https://etherscan.io/address/0xd30d6fd662c0d92b49f3c3e478e125ba1d968059#code
- Alert:
Trace 分析

- 攻擊合約通過一系列的代幣轉(zhuǎn)換獲取價值 0.01 ETH 的 ePENDLE 代幣
- 調(diào)用
stk-ePendle.harvest()從 ePENDLE 獎勵池中收集獎勵代幣,將收集到的 WETH 和 PENDLE 兌換成 ePendle 代幣后進(jìn)行 stake。

此時可以看到 stk-ePendle 合約中存在 13.6 ETH

- 隨后通過閃電貸
Balancer.receiveFlashLoan()發(fā)起攻擊。
Balancer.receiveFlashLoan()
先通過閃電貸獲取了大量的 ePENDLE,然后 deposit 獲得 stk-PENDLE,所有準(zhǔn)備工作已經(jīng)完成。

執(zhí)行攻擊部分,
- 創(chuàng)建一個新的合約
- 將 stk-PENDLE 發(fā)送到該合約
- 調(diào)用
getReward()函數(shù)獲取 EQB,xEQB 和 ETH 等獎勵代幣 - 把 ETH 匯總到主攻擊合約
- 把 stk-PENDLE 發(fā)送到主攻擊合約
- 把 EQB,xEQB 返還給 stk-PENDLE 合約

隨后將這個攻擊流程重復(fù)進(jìn)行了 20 次,每次獲利約 0.664 ETH,總計獲利 13.27 ETH。
攻擊者通過漏洞獲取到獎勵代幣后,只要 ETH,把 EQB,xEQB 返還給 stk-PENDLE 合約,是為了重復(fù) 20 次的獎勵獲取能夠正常運行下去。因為合約中持有 EQB 的數(shù)量是 5538 個,而每次獲取 EQB 383 個,20 次就是 7660 個。所以需要每次都將獲得的 EQB 和 xEQB 返還給 stk-PENDLE 合約。

代碼分析
在 Trace 分析中可以了解到,攻擊者利用同一筆 stk-PENDLE 發(fā)送到不同的合約中進(jìn)行重復(fù)的獎勵領(lǐng)取,多半是計算獎勵的邏輯中采用了 balanceOf 的方法來計算獎勵權(quán)重。
stk-PENDLE 合約:https://etherscan.deth.net/address/0xd30d6fd662c0d92b49f3c3e478e125ba1d968059
在 getReward() 函數(shù)中利用 updateReward() 計算用戶獎勵。

而在 earned() 函數(shù)中,通過 balanceOf 獲取目標(biāo)賬戶 stk-PENDLE 的余額,余額越大,獎勵越多。所以攻擊者可以通過同一筆 stk-PENDLE 代幣重復(fù)獲取大量的獎勵。

后記
漏洞修復(fù)
對于這個漏洞修復(fù)方案,看到社區(qū)也存在一些討論,主要是圍繞著以下幾個方面
- 避免直接采用
balanceOf來計算收益。 - 如果需要采用
balanceOf來計算收益,需要限制 stk-PENDLE 代幣不允許transfer。 - stk-PENDLE 代幣可以
transfer的場景下,則需要在代幣轉(zhuǎn)移前后,更新sender和recipient的獎勵累計情況。
如果想要較為優(yōu)雅地解決這個問題,推薦是選擇第三個方法,在 _beforeTokenTransfer() 和 _afterTokenTransfer() 中設(shè)計好獎勵的更新邏輯。
stk-ePendle.harvest() 的作用
在攻擊之前,攻擊者專門調(diào)用了一次 stk-ePendle.harvest() ,其主要目的是為了通過 _queueNewRewards() 函數(shù)累加 rewardInfo.rewardPerTokenStored 的值,以謀求更多的收益。但是在實際執(zhí)行的 Trace 中,由于 harvestAmount = 0 ,所以 continue 跳過了 _queueNewRewards() 函數(shù)的執(zhí)行步驟。雖然從事后的角度來看這個操作并沒有生效,但是漏洞利用的設(shè)計與執(zhí)行上存在這個環(huán)節(jié)還是必要的。


浙公網(wǎng)安備 33010602011771號