遺忘的亞特蘭蒂斯:以太坊短地址攻擊詳解
0 概述
說到智慧合約漏洞,第一時間映入腦海的可能都是演算法溢位,call()函式濫用,假充值等漏洞,畢竟這是在很多智慧合約上都有例項,並且危害等級也是比較高的,但是有一個漏洞也許很多人都見過、聽過卻不是很多人都關心的漏洞,它就像是人類世界的亞特蘭蒂斯,很多人知道,卻很少有研究報告,Google 上能找到的,也不過只是說原理而沒有復現實戰,有點紙上談兵的感覺。大家都知道的是這是 EVM 層面的缺陷,而不是智慧合約層面的問題,並且在我們預設的思維裡面,這是一個已經被修復的漏洞。前段時間嘗試翻查短地址攻擊的官方修復方案,但是經過我的搜尋,並沒有找到相關的修復方案,Github 上也扒了一遍,也看不到歷史 release 有相關的修復,於是,我猜,真的是我猜,EVM 層面可能並沒有修復。
1 短地址攻擊基礎知識
短地址攻擊其實就是每個 ERC20 的合約裡面都有一個以下的函式
當對合約的這個函式進行呼叫的時候,其實在 EVM 層面來說是傳入一串位元組碼,就像下圖這樣,可以在 Etherscan 裡面檢視每筆交易的 inputdata 裡面的整個函式的呼叫資料
傳入的位元組碼一共有 136 位元組(正常來說),裡面包含方法的簽名(前 4 位元組),和傳入的引數值(資料部分,包括傳入的地址資料和金額資料,分別都為 32 位元組,高位補零)。上面這個圖是不正常的,只有 134 位元組,這是我經過特殊處理過的,下面我會詳細說。
從上面的資訊我們可以知道,EVM 是根據位元組碼識別傳入的引數的,位元組碼是多少就是多少,也不會去驗證。巧了,就是這樣,漏洞就產生了,有人就想用不合法的地址,比如地址故意寫少後幾位,看看 EVM 會怎麼處理,但是又巧了,EVM 不僅不會報錯,還會“貼心”的幫你對地址進行補全操作,怎麼補全的呢?就是將地址後面不足的部分,用金額資料部分的位數來補全,比方說你的地址本來是

結果由於你心機叵測,故意寫少兩個 0,變成下面這樣
那麼“貼心”的 EVM 就會從位元組碼中的金額資料部分取兩位對地址進行補全,而金額資料部分的前兩位又恰好是 0,那麼就是說你的地址還是原來的地址,但是資料部分就少 2 位了,怎麼辦呢?這就不符合 ABI 的位元組數啦,沒關係,“貼心”的 EVM 會將你的金額資料部分從末位開始補 0,補到為正常的 136 位元組為止,那麼有同學就要問了,如果我的地址有 6 個 0,那麼我地址故意寫少 6 個 0,是不是資料部分就多了 6 個 0?那不是發財了?
答案就是:你是對的
也就是說,本來填的資料是 1,變成位元組碼後是 0×1,如果地址少了 6 個位元組,那麼你的 data 就自動變成 0×1000000 啦!驚不驚喜,意不意外!
2 第一次出師
第一次我嘗試使用 remix 進行復現,但結果是能預料到失敗的,如下圖,為什麼呢?因為這是一個在 2017 年就被爆出的漏洞,修復方案滿天飛,怎麼可能那麼簡單的就讓你成功了呢,所以前端肯定會對你的輸入的地址進行過濾和檢查,想成功,是 naive 的。
但是作為一個搞事情的人,怎麼可以輕言放棄,既然沒有找到官方修復方案,底層肯定就還有問題的,於是乎,我只能從最底層開始進行操作,我想到了使用 web3 進行漏洞復現
3 環境準備
1. 作業系統:macOS
2. node : v8.11.0
3. web3:1.0.0-beta.36
4. rpc:infura
5. 測試合約的 abi
6. 示例合約:

7. 使用 remix 在測試網部署示例合約,獲取合約的地址,下面會用到
:warning:獲取 abi 的方法:

4 第二次出師
注:(:warning:本次所有操作均在命令列中執行)
1. 鍵入 node 進入命令列提示符
2. 在專案中引入 web3 並設定 provider(怎麼下載 web3 這裡不展開說明,有興趣的可以自己查詢,一個很簡單的操作)
3. 建立合約例項:
4. 構造方法 abi,也就是構造我們上面說的交易裡面的 inputdata,由於我們是要構造短地址攻擊,所以我們的地址是要比正常的地址的位數要少的,為的就是要讓 EVM 用零自動補全缺失的地址,但是正常的構造是會失敗的,例如下圖這樣

但是,再次宣告一下,作為一個搞事情的人,不能輕言放棄!於是我們需要一點特別的方法,一開始的時候我到了這裡就以為會有檢測就不行了,太天真,其實是 web3 的檢測,我們需要一點特別的方法。這個方法分為兩步
第一步,先構造正常的 abi,這次使用的地址是 ’0xdfca6234eb09125632f8f3c71bf8733073b7cd00′

如圖,現在的 abi,也就是 inputdata,是 136 位元組的。

第二步:使用一個小技巧,將 abi 裡面地址後面的兩個零偷偷抹掉,本來是 136 位元組的,現在只有 134 位元組了,也就是我上面說到的不正常的 inputdata,就是在這個時候構造出來的

以上就是把零抹掉之後的 abi
ok,一切都準備好之後就可以到最激動人心的時刻了
5. 在專案中引入 ethereumjs-tx(怎麼下載的這裡也不詳細展開)
6. 匯入你的私鑰並做一些處理:

7. 構造原始交易資料,這是一筆十分原生的以太坊交易資料,每一次的合約呼叫,其實都會構造下面這一個資料。有關這方面的知識也不詳細展開,但是,除了 nonce 我們是不怎麼了解之外,其他都是我們在 remix 上呼叫合約的時候會接觸到的,有關於 nonce 的說明,其實就是帳號所傳送的交易數量,比方說你的帳號曾經處理過 5 筆交易,那麼 nonce 就等於4(nonce 從 0 開始),可以在 ehterscan 上檢視到你的帳號的最後一筆交易的 nonce,以下是具體的交易資料:

8. 對交易進行簽名和對交易做一點處理

9. 傳送交易

5 奇蹟再現
通過預先設定的 event 事件我們可以看到,EVM 成功補零,輸入本來是 123,但是 EVM 提取的結果卻是 31488,本來的 16 進位制 123 是 0x7b,現在是 0x7b00!刺激!
6 後記
“貼心”的我在測試網上也布了合約,而且我還驗證了,可以在鏈上查詢記錄,眼見為實。
https://kovan.etherscan.io/address/0x6e2f32497a7da13907831544093d3aa335ecbf33#code
這次的操作,不禁使我想起了飛躍瘋人院裡面的那句話–But I tried, didn’t I? Goddamnit, at least I did that.
7 參考資料
web3js 1.0 介面方法(中文手冊)
http://cw.hubwiz.com/card/c/web3.js-1.0/1/2/21/
短地址攻擊詳解
https://zhuanlan.zhihu.com/p/34470071
有關 ABI 的詳細資訊在這裡
https://solidity-cn.readthedocs.io/zh/develop/abi-spec.html
發文時比特幣價格 ¥43493.00