作者:隱形人真忙
作者博客:http://blog.csdn.net/u011721501/article/details/79476587

0x00 基礎知識

EVM虛擬機在解析合約的字節碼時,依賴的是ABI的定義,從而去識別各個字段位于字節碼的什么地方。關于ABI,可以閱讀這個文檔: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI

一般ERC-20 TOKEN標準的代幣都會實現transfer方法,這個方法在ERC-20標簽中的定義為: function transfer(address to, uint tokens) public returns (bool success);

第一參數是發送代幣的目的地址,第二個參數是發送token的數量。

當我們調用transfer函數向某個地址發送N個ERC-20代幣的時候,交易的input數據分為3個部分:

4 字節,是方法名的哈希:a9059cbb

32字節,放以太坊地址,目前以太坊地址是20個字節,高危補0
000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca

32字節,是需要傳輸的代幣數量,這里是1*10^18 GNT
0000000000000000000000000000000000000000000000000de0b6b3a7640000

所有這些加在一起就是交易數據:

a9059cbb000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca0000000000000000000000000000000000000000000000000de0b6b3a7640000

0x01 以太坊短地址

當調用transfer方法提幣時,如果允許用戶輸入了一個短地址,這里通常是交易所這里沒有做處理,比如沒有校驗用戶輸入的地址長度是否合法。

如果一個以太坊地址如下,注意到結尾為0:

0x1234567890123456789012345678901234567800

當我們將后面的00省略時,EVM會從下一個參數的高位拿到00來補充,這就會導致一些問題了。

這時,token數量參數其實就會少了1個字節,即token數量左移了一個字節,使得合約多發送很多代幣出來。我們看個例子:

這里調用sendCoin方法時,傳入的參數如下:

0x90b98a11
00000000000000000000000062bec9abe373123b9b635b75608f94eb8644163e
0000000000000000000000000000000000000000000000000000000000000002

這里的0x90b98a11是method的hash值,第二個是地址,第三個是amount參數。

如果我們調用sendCoin方法的時候,傳入地址0x62bec9abe373123b9b635b75608f94eb8644163e,把這個地址的“3e”丟掉,即扔掉末尾的一個字節,參數就變成了:

0x90b98a11
00000000000000000000000062bec9abe373123b9b635b75608f94eb86441600
00000000000000000000000000000000000000000000000000000000000002  
                                                                                                                            ^^
                                                             缺失1個字節

這里EVM把amount的高位的一個字節的0填充到了address部分,這樣使得amount向左移位了1個字節,即向左移位8。

這樣,amount就成了2 << 8 = 512。

0x02 構造短地址攻擊

(1)首先生成一個ETH的靚號,這個賬號末尾為2個0

使用一些跑號工具就可以做到,比如MyLinkToken工具,可以很輕易跑出末尾兩個0的。

(2)找一個交易所錢包,該錢包里token數量為256000

(3)往這個錢包發送1000個幣

(4)然后再從這個錢包中提出1000個幣,當然這時候寫地址的時候把最后兩個0去掉

如果交易所并沒有校驗用戶填入的以太坊地址,則EVM會把所有函數的參數一起打包,會把amount參數的高位1個字節吃掉。

(5)這三個參數會被傳入到msg.data中,然后調用合約的transfer方法,此時,amount由于高位的1個字節被吃掉了,因此amount = amount << 8,即擴大了256倍,這樣就把25600個幣全部提出來了。

0x03 總結

針對這個漏洞,說實話以太坊有不可推卸的責任,因為EVM并沒有嚴格校驗地址的位數,并且還擅自自動補充消失的位數。此外,交易所在提幣的時候,需要嚴格校驗用戶輸入的地址,這樣可以盡早在前端就禁止掉惡意的短地址。

Reference


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/545/