事實上,如今,為 DeFi 作業付費是不可能的。
一群朋友發現瞭如何透過將多筆交易批量處理為一筆交易來稍微降低執行多筆交易的成本,因此他們開發了一個智能合約來實現這一點。
他們需要該合約能夠升級,以防程式碼包含錯誤,並且他們還希望防止組外的人使用它。為此,他們投票並分配了兩個在系統中具有特殊角色的人:管理員,有權更新智能合約的邏輯。所有者,控制允許使用合約的地址白名單。合約已部署,該組已列入白名單。每個人都為自己對抗邪惡礦工的成就而歡呼。
他們殊不知,他們的午餐錢正處於危險之中…
您需要劫持此錢包才能成為代理的管理員。
這題牽扯到 Proxy Contract,建議先看完 WTF Solidity极简入门: 46. 代理合约
這題可以看到代理合約跟邏輯合約的變數宣告不一致,storage slot 會產生衝突。所以我們只要透過 proposeNewAdmin()
(23 行)修改代理合約的 pendingAdmin
,就會因為 slot 衝突進而修改到 PuzzleWallet
的 owner
(都是第一個宣告的變數)。順利取得 PuzzleWallet
的 owner 權限。
接下來就是要奪取 PuzzleProxy
的 admin 權限。跟剛剛一樣,我們的目標是修改 PuzzleWallet
的 maxBalance
讓 admin 跟著一起改變。改變 maxBalance
的 function 在第 54 行,要先通過 modifier onlyWhitelisted()
,所以要把自己加入到白名單中,透過 59 行的 addToWhitelist()
。
接下來要通過 55 行的 require(address(this).balance == 0)
,必須將合約中的幣題光,合約中有 0.001 eth,雖然 execute()
可以提款但是只能提取自己存進去的 eth,所以現在要想辦法讓自己在 PuzzleWallet
中的錢顯示 0.002 但其實只有存入 0.001。
這裡要用到合約中最後的 function multicall(bytes[] calldata data)
(76 行),他會將參數 data[]
中存入的 function 逐一執行,所以我們只要 [deposit, [multicall, deposit]]
就可以實現只放入 0.001 但執行兩次 deposit()
的效果。
最後攻擊合約如下: