在之前的內容中,學習到了storage中是使用插槽存儲數據的。而delegatecall函數有個有趣的特點當使用delegatecall 函數進行外部調用涉及到storage 變量的修改時是根據插槽位置來修改的而不是變量名

舉個例子:

合約A

智能合約安全——delegatecall (1)

合約B

智能合約安全——delegatecall (1)

當合約B調用testDelegatecall ()函數時,合約B的地址c的值會變為合約A的地址,而地址a則是不變。因為合約A的函數test()改變的是插槽slot 1的值,同樣的在合約B中運行時,改變的也是插槽slot 1的值,即地址c的值。

目標合約

智能合約安全——delegatecall (1)

漏洞分析

我們可以看到有兩個合約, Lib 合約中只有一個pwn 函數用來修改合約的owner,在HackMe 合約中存在fallback 函數,fallback 函數的內容是使用delegatecall 去調用Lib 合約中的函數。我們需要利用HackMe.fallback() 觸發delegatecall 函數去調用Lib.pwn() 將HackMe 合約中的owner 改成自己。

攻擊合約

智能合約安全——delegatecall (1)

現在我們來看看整個攻擊的邏輯:

1.攻擊者調用attack()發起攻擊,attack 函數首先去調用HackMe.pwn();

2.HackMe 合約中並沒有pwn 函數,此時觸發HackMe.fallback();

3.HackMe.fallback() 使用deldegatecall 調用Lib 合約中的函數,函數名取的是msg.data 也就是"pwn()",而Lib 合約中恰好有名為pwn 的函數,於是便在HackMe 合約中運行了pwn函數;

4.pwn函數修改了插槽slot0位置(即HackMe 合約的擁有者)的值為msg.sender(即攻擊者),最終導致了HackMe 合約的擁有者變成了攻擊者。

修復建議

1. 在使用delegatecall 時應注意被調用合約的地址不能是可控的;

2. 在較為複雜的合約環境下需要注意變量的聲明順序以及存儲位置。因為使用delegatecall 進行外部調時會根據被調用合約的數據結構來用修改本合約相應slot 中存儲的數據,在數據結構發生變化時這可能會造成非預期的變量覆蓋。

如果想了解更多的智能合約和區塊鏈知識,歡迎到區塊鏈交流社區CHAINPIP社區,一起交流學習~

社區地址: https://www.chainpip.com/