分析 Hedgey Finance 的合約漏洞
前幾天看到這篇推文:
Hi @hedgeyfinance, you may want to a look (w/ $1.3m) pic.twitter.com/0ujOtyyLYI
— PeckShield Inc. (@peckshield) April 19, 2024
所以就追了一下發生什麼事,這個 Hedgey Finance 是一個 DeFi 項目,看起來主要是幫忙團隊分發 token 的服務,推文裡的 transaction 是 0x2606d459a50ca4920722a111745c2eeced1d8a01ff25ee762e22d5d4b1595739 ,從 Tenderly Explorer 可以看到:
- Attacker address 也就是
msg.sender
是0xded2b1a426e1b7d415a40bcad44e98f47181dda2
- Exploit contract ,也就是這個 tx 的 receiver 是
0xc793113f1548b97e37c409f39244ee44241bf2b3
- Victim contract ,也就是 Hedgey 的 ClaimCampaigns 是
0xbc452fdc8f851d7c5b72e1fe74dfb63bb793d511
- 整個 tx 其實很簡單,就是先用
USDC.balanceOf(ClaimCampaigns)
拿到 ClaimCampaigns 裡面有多少 USDC 之後,再用USDC.transferFrom(ClaimCampaigns, attacker, balance)
把 USDC 全部轉走
所以問題就變成:為什麼 ClaimCampaigns
會給 Attacker 這麼多 USDC allowance ?我們可以看 exploit contract 在 exploit 的前一個 tx 0xa17fdb804728f226fcd10e78eae5247abd984e0f03301312315b89cae25aa517 發生了什麼事:
- Exploit contract 用
BalancerVault.flashLoan()
借了一堆 USDC - 呼叫
USDC.approve(ClaimCampaigns)
把 USDC 全部 approve 給 ClaimCampaigns - 呼叫
ClaimCampaigns.createLockedCampaign()
開了一個 campaign ,這時候會呼叫USDC.transferFrom(ExploitContract, ClaimCampaigns)
- 呼叫
ClaimCampaigns.cancelCampaign()
取消 campaign ,這時候ClaimCampaigns
會呼叫USDC.transfer(ExploitContract)
把 USDC 全部轉回 exploit contract - 最後 exploit contract 會再呼叫
USDC.transfer(BalancerVault)
把 USDC 還回去
問題是出在 ClaimCampaigns.createLockedCampaign()
裡,會先 SafeERC20.safeIncreaseAllowance
(這行) ,但是在 ClaimCampaigns.cancelCampaign()
的時候,用的是 transfer
而不是 transferFrom
,所以在這個 tx 執行完之後, ClaimCampaigns
對於 exploit contract 的 allowance 並沒有被清掉,所以下一個 tx 才能直接把 USDC 全部轉走。
其實在 Hedgey 的 Audits 頁面 可以看到有 Consensys Audit , audit 的目標是 commit hash 6a5ff58c2e83015b83c8de15f1cc61e9ac58f2c7
,其實也是有 同樣的問題 。
最後 Hedgey 團隊的 修正 ,是把 safeIncreaseAllowance()
從 createLockedCampaign()
移到 claimTokens()
。