分析 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() 。