天天看點

ERC20 Short Address Attack

ERC20 Short Address Attack

  • 什麼是ERC20
  • Application Binary Interface(ABI)
  • 開始攻擊
  • 怎麼利用?
  • 攻擊防範

代币大家應該都很熟悉了,代币也叫 token, 他不是像比特币,以太坊等虛拟币這樣建立在大量技術人員的辛苦工作基礎之上,用于維持公鍊運作的虛拟貨币。代币一般是依賴于以太坊平台,就是一個以太坊平台上面的一個智能合約裡面記錄的數字。

是以說token沒有任何價值,每個人都可以在1分鐘之類建立出無限的token。那麼為什麼現在有這麼多的token價格這麼高,還有這麼多人去交易呢? 一般來說,發行token的人會将整個token綁定一個很有前途的項目,什麼納米科技,衛星技術,新型動力,怎麼牛逼怎麼往上面靠,然後找幾個币圈有名氣的人站台,然後通過各種管道釋出這個token很了不起的輿論,接下來就是上交易所收割韭菜了,至于他們當時标榜的跨時代的項目就不知道是不是能夠真的完成了,畢竟錢已經收到口袋裡面了。

不可否認,有些token确實很有良心,也真的按照設想的去做了事情。這裡不深究發行token的對與錯,這裡我們講下ERC20。

大家都來發token,那麼這麼多的token沒有一個統一的标準,不好在以太坊平台進行通用的轉讓呀,為了便于token的流通,于是出現了一個token的标準叫做ERC20,簡單點說,ERC20規定了token智能合約必須要實作的9個方法和2個事件。

具體的方法和事件名請看 ERC-20标準說明

這篇文章其實是講ERC20攻擊的,要想攻擊ERC20,我們首先要知道怎麼去跟以太坊虛拟機進行互動。在以太坊Ethereum 生态系統中, 應用二進制接口Application Binary Interface(ABI) 是從區塊鍊外部與合約進行互動以及合約與合約間進行互動的一種标準方式。 資料會根據其類型按照約定的方法進行編碼。

簡單點說,一個ABI包括一個函數選擇器和函數的參數。

函數選擇器是一個函數調用資料的前 4 位元組,指定了要調用的函數。這4個位元組是函數簽名通過 Keccak(SHA-3)哈希之後的前 4 位元組。

函數的參數會被編碼成32位元組,不足32位元組的将會補全。ERC20 Short Address Attack 就是在這個函數補全上面出現的。

我們先看一個簡單的token合約,用來轉賬。

pragma solidity ^0.4.11;
 
contract MyToken {
mapping (address => uint) balances;
 
event Transfer(address indexed _from, address indexed _to, uint256 _value);
 
function MyToken() {
balances[tx.origin] = 10000;
}
 
function sendCoin(address to, uint amount) returns(bool sufficient) {
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount;
balances[to] += amount;
Transfer(msg.sender, to, amount);
return true;
}
 
function getBalance(address addr) constant returns(uint) {
return balances[addr];
}
}

           

很簡單的一個智能合約, 這裡我們看下sendCoin的方法,他接收2個參數,一個是接收token的位址,一個是數目。

如果想進行調用,那麼生成的ABI可能是這樣的:

0x90b98a11
00000000000000000000000062bec9abe373123b9aabbcce608f94eb8644163e
0000000000000000000000000000000000000000000000000000000000000004
           

其中:

0x90b98a11 是Keccak (SHA-3) 運算之後的方法标記 sha3(sendCoin(address,uint))。

00000000000000000000000062bec9abe373123b9aabbcce608f94eb8644163e 是 20位元組的目标位址,被補全到32位元組。

0000000000000000000000000000000000000000000000000000000000000004 是 1個位元組的4被補全到32位元組。

如果攻擊者不按常理出牌,并不将參數補全到32位元組,那麼總的ABI長度會比預料的要小,那麼會出現什麼問題呢?

還是上面的例子,我們要發送4個token到62bec9abe373123b9aabbcce608f94eb8644163e。

但是我們把位址的最後1個位元組“3e”去掉,那麼ABI如下所示:

0x90b98a11
00000000000000000000000062bec9abe373123b9aabbcce608f94eb8644163e00
00000000000000000000000000000000000000000000000000000000000004
                                                              ^^ 
           

我們可以看到,最後的04後面其實是少了一個位元組的。

那麼如果我們把這個ABI傳給以太坊虛拟機,會出現什麼問題呢?

以太坊虛拟機會将ABI補全,就是在04後面加一個位元組00。

參數如下:

_from: 0x58bad47711113aea5bc5de02bce6dd332211aabb
_to: 0x62bec9abe373123b9aabbcce608f94eb8644163e00
_value: 2048

           

我們看到兩個變化,第一目标位址變了,第二轉移的數值變了:4<<8 = 2048。

這個就是ERC20 Short Address Attack。

  1. 攻擊者建構一個最後一個位元組為0的位址A。
  2. 攻擊者找到一個發行token的智能合約,在這個智能合約裡面,向A存入500token。
  3. 攻擊者從智能合約轉出500token到位址A,但是将位址A最後一個位元組的0去掉。
  4. 位址A最後1個位元組會被補全,而轉賬的500token就變成了500<<8。

防範起來其實很簡單,做好必要的參數校驗即可。

下面的代碼加了一個modifier onlyPayloadSize 。 在這個modifier裡面,我們做了參數長度的校驗。

pragma solidity ^0.4.11;
 
contract MyToken {
mapping (address => uint) balances;
 
event Transfer(address indexed _from, address indexed _to, uint256 _value);
 
function MyToken() {
balances[tx.origin] = 10000;
}

    modifier onlyPayloadSize(uint size) {
     assert(msg.data.length == size + 4);
     _;
   }

 
function sendCoin(address to, uint amount) onlyPayloadSize(2 * 32) returns(bool sufficient) {
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount;
balances[to] += amount;
Transfer(msg.sender, to, amount);
return true;
}
 
function getBalance(address addr) constant returns(uint) {
return balances[addr];
}
}

           

更多教程請參考flydean的部落格

繼續閱讀