Gas(礦工費)濫用漏洞的最新披露
Gas是什麼?
在英文中gas有燃氣的意思,於是以太坊很形象地引用了這個詞,在以太坊中可以把gas理解為“交易手續費”也可以理解為“礦工費”等等,凡是要上鍊的每一筆交易都必須支付手續費,以太坊網路就像是一個世界計算機,使用這個計算機的資源是必須付出相應的費用的。
基於這種經濟模型,以太坊還衍生出來兩個概念:gas limit和gas price。顧名思義,這兩個概念可以理解為“燃氣”上限和“燃氣”單價,就跟開車一樣,如果“燃氣”上限給低了,就必然無法到達目的地,在以太坊中體現為交易失敗且還白支付了礦工費,為了避免這種情況,現在的一些熱錢包在進行交易之前都會先計算該筆交易需要消耗的gas,然後以此預估一個值設定為gas limit,問題就出在這裡,後面再講。
以太坊網路是不講究先來後到的,礦工往往唯利是圖,gas price就是願意為每個gas支付的金額,比如普通使用者直接的轉賬一般是消耗21000 gas,如果你gas price設為0.01 eth的話那消耗的手續費是gas * gas price=21000* 0.01=210 ETH(只是舉例,沒人會付這麼高的手續費),所以gas price給得越高,礦工會更快確認你的交易。
中心化交易所提幣功能
使用過中心化交易所的朋友都知道,在使用者向交易所發起提幣請求之後,使用者的錢包會收到一筆來自陌生地址的轉賬,這個地址是交易所的熱錢包地址。
向交易所發起提幣請求時,一般情況下交易所會收取你一定量的幣作為轉賬的礦工費,比如0.009 ETH,但是交易所在向我們轉賬的時候所需要使用的礦工費一定會低於這個固定值嗎?
不一定。
我們之前提到過:現在的一些熱錢包在進行交易之前都會先計算該筆交易需要消耗的gas,然後以此預估一個值設定為gas limit。所以在特殊情況下,交易所轉賬需要使用的礦工費可能會大於那個固定值。
以太坊智慧合約fallback函式
fallback函式是沒有函式名的,在以太坊智慧合約中可以用以下方式宣告fallback函式
function () [payable] { //todo }
中括號內的為可選項。根據官方文件,這個函式的主要作用是:當有人呼叫該合約不存在的函式或者該合約收到ETH轉賬時被該函式就會被觸發。
利用fallback函式濫用交易所gas
在交易所沒在熱錢包轉賬時設定最高gas limit的情況下,該交易所便存在gas濫用隱患。
首先我們在鏈上部署這麼一個合約
contract Example { function () payable { for(uint256 i;i<100000;i++){ keccak256(i); } address(你錢包的地址).transfer(msg.value); } }
然後使用交易所提幣到該合約地址,交易所向這個合約地址轉賬時,將消耗5821768個gas,當前網路的標準確認速度的gas price是4 Gwei,那麼最終交易所將會消耗的ETH為5821768 * 4Gwei = 0.023287072 ETH,遠超過之前看到的0.009 ETH的提幣手續費,這樣反覆提幣會使交易所造成大量的經濟損失。
利用Storage gas refund機制來獲利
目前以太坊主網上的賬本大小已經超過1T了,執行全節點對節點的儲存容量有很高要求,於是導致了執行全節點的數量急劇減少,以太坊開發者為了緩解這種情況新增了一個storage gas refund機制。
storage gas refund機制中其中有一項緩解措施就是:當用戶執行selfdestruct銷燬合約時,將獲得一部分gas退款,這些退回來的gas可以在整個交易中被用來抵消。
利用這種機制,攻擊者可以變相地把交易所消耗的gas“存”起來。目前利用這種機制來“存”gas已經有了一個成型的專案,名為GasToken( ofollow,noindex" target="_blank">https://gastoken.io/)。
我們部署以下合約就可以使用交易所消耗的gas為我們鑄造50個GasToken
contract Gastoken { function mint(uint256 value) public; function free(uint256 value) public returns (bool success); function freeUpTo(uint256 value) public returns (uint256 freed); function freeFrom(address from, uint256 value) public returns (bool success); function freeFromUpTo(address from, uint256 value) public returns (uint256 freed); } contract Example { function () payable { Gastoken(GasToken的合約地址).mint(50); address(你錢包的地址).transfer(msg.value); } }
這樣攻擊者就可以獲得交易所的一部分經濟損失,以此獲利。
以太坊中其他可能受影響的地方
ERC233、ERC721、ERC777、ERC677等等這些會呼叫不可控的外部函式的合約都可能存在這種隱患。
不僅如此,甚至是使用者個人在向對方轉賬時,若未關注預估的gas limit,都有可能被對方濫用你的gas。
其他公鏈
在以太經典(ETC)或者其他轉賬時需要支付礦工費的公鏈中,都將同樣存在這種隱患。
如何防範
-
普通使用者提取Ether,檢測地址是否為普通地址而非合約地址,且設定gas limit,一般取21000。
-
若允許向合約地址轉賬,務必計算向該合約轉賬所需要的gas,超出預期的部分由使用者承擔。
-
熱錢包在發起交易之前計算gas limit,若該值過高則需要提醒使用者。
參考
https://medium.com/level-k/public-disclosure-malicious-gastoken-minting-236b2f8ace38
https://drive.google.com/file/d/1mULop1LxHJJy_uzVBdc_xFItN9ck04Jj/view