本次,我們來講一講運用delegatecall函數時更復雜的合約漏洞案例。
目標合約

漏洞分析
這次的攻擊目標依然是獲得HackMe 合約中的?owner?權限,我們可以看到兩個合約中除了 HackMe 合約中的構造函數可以修改合約的?owner?其他地方并沒有修改?owner?的函數,但是卻可以修改位置slot0的值,而HackMe 合約中插槽slot0表示的便是Lib的地址,那么我們就先修改Lib的地址為我們的地址,再次調用HackMe 合約時就會運行我們合約中的邏輯,那么想改哪個位置插槽的值不就都由我們控制了嗎?
攻擊合約
下面是我們本次的攻擊合約:

接下來我們來看看攻擊的整個邏輯:
1. Attack.attack() 函數先將自己的地址轉換為 uint256 類型(這一步是為了兼容目標合約中的數據類型)第一次調用 HackMe.doSomething() 函數;
2. HackMe.doSomething() 函數使用 delegatecall 函數帶著傳入的 Attack 合約的地址調用了 Lib.doSomething() 函數;
3. 可以看到 Lib.doSomething() 函數將合約中存儲位置為 slot0 的參數改為傳入的值,這樣當 HackMe 合約使用 delegatecall 調用 Lib.doSomething() 函數時也將改變自己在 slot0 位置存儲的變量的值,也就是將 lib 參數(這里存儲的是 Lib 合約的地址)改為我們傳入的 Attack 合約的地址。此時之前在 HackMe.lib 參數中存儲的 Lib 合約的地址就被修改成我們傳入的 Attack 合約的地址了;
4. Attack.attack() 函數再次調用 HackMe.doSomething() 函數,由于在上一步我們已經將 HackMe.lib 變量修改為 Attack 合約的地址了,這時 HackMe.doSomething() 函數將不再調用之前的 Lib 合約而是用 delegatecall 去調用 Attack.doSomething() 函數。此時我們再來觀察 Attack 合約的寫法,發(fā)現其變量的存儲位置故意和 HackMe 合約保持一致,并且不難發(fā)現 Attack.doSomething() 函數的內容也被攻擊者寫為 owner = msg.sender,這個操作修改了合約中存儲位置為 slot1 的變量。所以 HackMe 合約使用 delegatecall 調用 Attack.doSomething() 函數就會將合約中存儲位置為 slot1 的變量 owner 修改為 msg.sender 也就是攻擊者的地址,至此攻擊者完成了他的攻擊。
修復建議
我們在合約的開發(fā)中使用delegatecall要時刻注意其被調用的合約地址要始終在我們設計的邏輯內運行,不能讓其有可能超出我們設計時的適用范圍,一旦出現了超出我們預期設計的情況,那么合約就有可能被不法之徒利用。
如果想了解更多的智能合約和區(qū)塊鏈知識,歡迎到區(qū)塊鏈交流社區(qū)CHAINPIP社區(qū),一起交流學習~
社區(qū)地址:https://www.chainpip.com/