作者:Sissel@知道創宇404區塊鏈安全研究團隊
時間:2018年8月20日
英文版:https://paper.seebug.org/687/
0x00 前言
2018年08月01日,知道創宇404區塊鏈安全研究團隊發布《金錢難寐,大盜獨行——以太坊 JSON-RPC 接口多種盜幣手法大揭秘》,針對 偷渡漏洞
和 后偷渡時代的盜幣方式
進行了介紹,披露了 后偷渡時代
的三種盜幣方式:離線攻擊、重放攻擊和爆破攻擊。
在進一步的研究中,我們又發現了針對這些攻擊方式的補充:拾荒攻擊。攻擊者或求助於礦工,或本身擁有一定算力以獲得將交易打包進區塊的權利。在偷渡漏洞中,攻擊者在被攻擊節點構造gasPrice
為 0
的交易,等待用戶解鎖賬戶簽名廣播。攻擊者同時設置一個惡意節點,用於接收這筆交易。攻擊者將符合條件的交易打包,就可以實現 0
手續費完成轉賬。通過這種攻擊,攻擊者可以獲取到餘額不足以支付轉賬手續費或勉強足夠支付手續費節點上的所有以太幣,並在一定程度上可以防止其他攻擊者的競爭,可謂是 薅羊毛
的典範。
除此之外,在薅夠以太幣殘羹之後,攻擊者又盯上了這些以太幣已被盜光,但賬戶中殘留的代幣。直到現在,針對許多智能合約發行的代幣,一些被攻擊賬戶中的token,仍在小額地被攻擊者以拾荒攻擊盜走。
本文將從一筆零手續費交易談起,模擬復現盜幣的實際流程,對拾荒攻擊成功的關鍵點進行分析。
0x01 從一筆零手續費交易談起
在區塊鏈系統中,每一筆交易都應該附帶一部分gas以及相應的gasPrice作為手續費,當該交易被打包進區塊,這筆手續費將用來獎勵完成打包的礦工。
在《金錢難寐,大盜獨行——以太坊 JSON-RPC 接口多種盜幣手法大揭秘》中,我們提到了一個利用以太坊JSON-RPC接口的攻擊者賬號0x957cD4Ff9b3894FC78b5134A8DC72b032fFbC464。該攻擊者在公網中掃描開放的RPC端口,構造高手續費的交易請求,一旦用戶解鎖賬戶,便會將用戶餘額轉至攻擊者的賬戶或攻擊者創建的合約賬戶。
在分析該賬戶交易信息的時候,我們發現了一筆不符合常識的交易,先從這筆交易開始談起。
交易地址:0xb1050b324f02e9a0112e0ec052b57013c16156301fa7c894ebf2f80ac351ac22
Function: transfer(address _to, uint256 _value) MethodID: 0xa9059cbb [0]: 000000000000000000000000957cd4ff9b3894fc78b5134a8dc72b032ffbc464 [1]: 000000000000000000000000000000000000000000000000000000000abe7d00
從0x00a329c0648769a73afac7f9381e08fb43dbea72向合約MinereumToken(攻擊者的合約)的交易,雖然用戶餘額很少,但這筆交易使用了該賬戶所有餘額作為value與合約交互,這筆交易使用了正常數量的gas,但它的gasPrice被設定為0。
前文提到,攻擊者會使用較高的手續費來保證自己的交易成功,礦工會按照本節點的txpool中各交易的gasPrice倒序排列,優先將高gasPrice交易打包進之後的區塊。在這個世界上每時每刻都在發生着無數筆交易,在最近七日,成交一筆交易的最低gasPrice是3Gwei。這筆零手續費交易究竟是如何發生,又是如何打包進區塊的呢。
0x02 思路分析
在區塊鏈系統中,任何人都可以加入區塊鏈網絡,成為其中一個節點,參與記賬、挖礦等操作。保證區塊鏈的可信性和去中心化的核心便是共識機制。
共識機制
在以太坊中,礦工將上一區塊的哈希值、txpool中手續費較高的交易、時間戳等數據打包,不斷計算nonce來挖礦,最先得出符合條件的nonce值的礦工將擁有記賬權,得到手續費和挖礦獎勵。礦工將廣播得到的區塊,其他節點會校驗這一區塊,若無錯誤,則認為新的區塊產生,區塊鏈高度增加。這就是各節點生成新區塊保持共識的過程。
將0 gasPrice交易完成需要確認兩個問題
- 礦工是否會接受這個交易,並將其打包
- 其餘節點接收到含此交易的區塊,是否會達成共識
下面我們來對0 gasPrice交易相關的操作進行測試。了解零手續費的交易如何產生,如何被txpool接受,打包了零手續費交易的區塊能否被認可,確認上述問題的答案。
0x03 零手續費交易測試
a. 單節點測試
首先,我們來確認此交易是否可以進入節點的txpool中,啟用一個測試鏈。默認rpc端口是8545,使用python的web3包發起一筆0 gasPrice轉賬。
geth --networkid 233 --nodiscover --verbosity 6 --ipcdisable --datadir data0 --rpc --rpcaddr 0.0.0.0 console
節點一發起轉賬的腳本,轉帳前要解鎖賬戶
from web3 import Web3, HTTPProvider web3 = Web3(HTTPProvider("http://localhost:8545/")) print(web3.eth.accounts) # 轉帳前要解鎖賬戶 web3.eth.sendTransaction({ "from":web3.eth.accounts[0], "to":web3.eth.accounts[1], "value": 10, "gas":21000, "gasPrice":0, })
交互結果
> txpool.content { pending: {}, queued: {} } > eth.getBalance(eth.accounts[0]) 800000000 > personal.unlockAccount(eth.accounts[0],'sissel') true > INFO [08-14|11:20:14.972] Submitted transaction fullhash=0x72e81751d2517807cabad24102d3cc2f0f4f2e8b92f1f106f1ee0bf6be734fe4 recipient=0x92636b228148e2824cB8d472Ef2F4e76f2F5059C > txpool.content { pending: { 0x092fda221a114FA702e2f59C217C92cfEB63f5AC: { 3: { blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0x092fda221a114fa702e2f59c217c92cfeb63f5ac", gas: "0x5208", gasPrice: "0x0", hash: "0x72e81751d2517807cabad24102d3cc2f0f4f2e8b92f1f106f1ee0bf6be734fe4", input: "0x", nonce: "0x3", r: "0x1eca20e3f371ed387b35ca7d3220789399a3f64c449a825e0fa7423b96ce235c", s: "0x35a58e5cb5027c7903c1f1cc061ae846fb5150186ebbabb2b0766e4cbfc4aee6", to: "0x92636b228148e2824cb8d472ef2f4e76f2f5059c", transactionIndex: "0x0", v: "0x42", value: "0xa" } } }, queued: {} } > miner.start(1) INFO [08-14|11:20:35.715] Updated mining threads threads=1 INFO [08-14|11:20:35.716] Transaction pool price threshold updated price=18000000000 null INFO [08-14|11:20:35.717] Starting mining operation > INFO [08-14|11:20:35.719] Commit new mining work number=115 txs=1 uncles=0 elapsed=223µs > mINFO [08-14|11:20:36.883] Successfully sealed new block number=115 hash=ce2f34…210039 INFO [08-14|11:20:36.885] ? block reached canonical chain number=110 hash=2b9417…850c25 INFO [08-14|11:20:36.886] ? mined potential block number=115 hash=ce2f34…210039 INFO [08-14|11:20:36.885] Commit new mining work number=116 txs=0 uncles=0 elapsed=202µs > miner.stop() true > eth.getBalance(eth.accounts[0]) 799999990
節點一發起的零手續費交易成功,並且挖礦后成功將該交易打包進區塊中。
b. 多節點共識測試
現在加入另一個節點
geth --datadir "./" --networkid 233 --rpc --rpcaddr "localhost" --port 30304 --rpcport "8546" --rpcapi "db,eth,net,web3" --verbosity 6 --nodiscover console 使用這些方法添加節點 > admin.nodeInfo > admin.addPeer() > admin.peers
節點一仍使用剛才的腳本發起零手續費交易,節點一的txpool中成功添加,但節點二因為gasPrice非法拒絕了此交易。
TRACE[08-15|10:09:24.682] Discarding invalid transaction hash=3902af…49da03 err="transaction underpriced" > txpool.content []
在geth的配置中發現了與此相關的參數
--txpool.pricelimit value Minimum gas price limit to enforce for acceptance into the pool (default: 1)
將其啟動時改為0,但節點二的txpool中仍未出現這筆交易。
转载请注明:IAMCOOL » 以太坊 “后偷渡時代” 盜幣之 “拾荒攻擊”