驚鴻一瞥:捕獵以太坊黑暗森林中的'那隻怪獸'

買賣虛擬貨幣
作為區塊鏈安全領域從業者,我對密碼學和安全一直都有著強烈的興趣,上週看了一篇非常不錯的文章: shorturl.at/cfhg9,在和作者聯絡取得同意後,翻譯成了中文,與感興趣的朋友一起分享一下。下邊就是原文: 上週,以太坊黑暗森林裡的一隻怪獸終於向我露出了他的獠牙。在與tux交流後,我得知了這隻怪獸的存在,於是投下了“我的誘餌”,16個小時之後它如期上鉤。在etherscan上看到我消失的eth,仍舊心有餘悸。 這隻怪獸一直在注視著以太坊網路,試圖尋找以太坊交易中那個不為人注意的錯誤:簽名中的k值複用。為了找到這隻怪獸,我引誘它出現。為了理解我是怎麼做到的,一些關於橢圓加密曲線簽名演算法(ecdsa)和數字簽名的知識需要先介紹一下。 ecdsa 橢圓曲線的數字簽名演算法(ecdsa)是支撐當今最大的兩個區塊鏈,bitcoin 和 ethereum的密碼學基礎。就如同在現實中,人們用自己的簽名來證明自己的權益,基於ecdsa的數字簽名可以證明這些區塊鏈上的數字資產的所有權。數字簽名可以提供下邊兩個方面的證明: 1. 對於私鑰的所有權。每把私鑰都有與之繫結的一把公鑰,這把公鑰同時也用來生成在區塊鏈上的一個“地址”。 2. 使用私鑰對於一個訊息的簽名。在區塊鏈的上下文中,這個訊息就是交易。 ecdsa之所以有效是因為,我們可以使用私鑰非常容易的得到公鑰,但是無法(或者說很難)從公鑰去推到出私鑰。但是如果得到了數字簽名,在一些條件下是可以推匯出私鑰的。下邊讓我們聊一點技術細節。 如果要完成對某個訊息的簽名,我們需要使用私鑰d,一個隨機值k,以及這個訊息的雜湊h。同時加上橢圓曲線中的g和n, 經過下邊的公式計算,我們可以得到這個訊息的數字簽名r和s。 ![1.png](https://appserversrc.8btc.cn/post/f7af390eac8daadb1777459e746876ee.png) r和s一起就是這訊息數字簽名。 nonce是什麼? 在橢圓加密演算法中,計算數字簽名的隨機k值是非常關鍵的。k值永遠不能暴露,並且一個k值永遠只能使用一次。這就是隨機k值被稱為nonce的原因。下文中也會用nonce來指代k值。如果駭客知道了某個簽名對應的k值,那麼它就可以透過以下的公式推算出這個簽名的私鑰。 ![2.png](https://appserversrc.8btc.cn/post/988d558d391dd1a0c7329557f4ec66e9.png) 同樣,如果nonce被在兩個不同的簽名中重複使用,這個nonce(k值)同樣也可以透過以下公式被推算出來。 ![3.png](https://appserversrc.8btc.cn/post/60cca9be57c84814c4d7125a20ea942a.png) 有了nonce就可以透過上邊的公式來推算出私鑰。 那在實際中我們如何判斷有沒有nonce重複使用呢? 我們回憶一下,在ecdsa 數字簽名中r的計算過程 r = k * g mod n. 這個公式中g和n都是確定的,唯一變數就是k,所以如果用同一個私鑰籤兩個不同訊息,在得到的簽名中r是一樣的,那麼就可以判斷出有nonce重複使用的問題。 在瞭解了這些後,我們來看看以太坊上的交易,如果一個地址下的兩筆交易的簽名有相同的r值但是s值不同,那麼可以判斷這個地址下有nonce重複使用的情況。這就是“那隻怪獸”一直的關注點。也是我下邊所用的實驗方法。 note:對於普通的使用者來說,只要選擇靠譜的錢包,你不要關心nonce洩露和重複使用的問題。但是對於區塊鏈開發者來說,這些是需要了解和注意的。 下餌,引蛇出洞 為了引蛇出洞,找到這隻怪獸,我構造了兩筆交易,這兩筆交易的簽名使用了相同的nonce,所以他們簽名會有相同的r值。我在這個地址上留了點eth,所以如果有任何人盯著這個地址,那麼它就可以透過簽名推算出私鑰來轉走這個地址上的eth。 為了製作這些誘餌,我必須構造出兩筆nonce重複使用的交易,當然你沒法使用metamask來達到這個目的。於是我使用ether.js來達到這個目的。來看下邊的示例程式碼。 var drbg = new hmacdrbg({ hash: this.hash, entropy: bkey, nonce: 1, // changed from "nonce" pers: options.pers, persenc: options.persenc || 'utf8', }); (程式碼僅供參考示例,別在自己的錢包上試) 在這裡我把nonce設為了1!然後我重新生成了一把私鑰,並在地址上充了0.04eth,然後我使用下邊的指令碼,生成了兩筆交易。 const ethers = require("ethers") async function main(){ const privatekey = "not-leaking-it-this-way" let wallet = new ethers.wallet(privatekey) console.log("using wallet:", wallet.address) const provider = new ethers.providers.jsonrpcprovider("rpc-endpoint") let signer = wallet.connect(provider) const tx = await signer.sendtransaction({ to: wallet.address, value: ethers.utils.parseether("0").tostring(), gaslimit: 45000, }) console.log(tx) } main() 由於我把nonce設為了1, 所以這兩個交易的簽名是有相同的r,和不同的s值。寫了一個簡單的指令碼驗證了一下: robert@mbp nonce-reuse-bait % node get_r_s.js transaction 1 r: 0x340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94 s: 0x3e5b92d8c5a2a033c9e5eb53ff2946681b28ab82fa5b17d0a9c48d139e78fe2c transaction 2 r: 0x340709f674d030dda4aa8794bffb578030870bdfe583e7f41aea136a7ca1ed94 s: 0x2c17cf960d1af7602f875afc56d01eddcc67bb03e962877444ed8d4c1287ab7a 隨著這兩筆交易的上鍊,簽名私鑰其實已經暴露了。我已經把餌下好,現在就靜靜地等待那隻怪獸的上鉤。 驚鴻一瞥,怪獸出沒 當傳送完這兩筆交易後,我就不斷的重新整理區塊瀏覽器來觀察地址上的eth是否還依舊存在。但是奇怪的事,一切如常。又過了幾個小時,依舊一切如常。我都開始懷疑是不是自己搞錯了什麼,但是天色已晚,我就先去休息了。 第二天早上一早,我就檢視我的賬戶,它出現了!我地址上的eth被取的乾乾淨淨,我故意留在地址上的0.04個eth被取走了! ![11.png](https://appserversrc.8btc.cn/post/a83bdb152257c75c71f4e62e11b11e58.png) 那隻怪獸終於露出了獠牙,他注視著以太坊上的交易, 看是不是有交易的簽名有相同的r值。當他發現以後就去推算他們的私鑰,取空他們賬戶上的所有資產。我有點疑惑的是,為什麼過了這麼久它才出動?有一種可能性是它一直在默默等待,看這個賬戶上會不會有更多的資產轉進來,它好一次性的都轉走。 讓我們近距離的看看這隻怪獸,我不僅僅是唯一的受害者,它同樣也取幹了別人的資產! ![22.png](https://appserversrc.8btc.cn/post/d8af4a2e8fb7351e19aadf78ce6054a6.jpg) 這個地址一直有從不同地址下轉入的eth,截止目前這個賬戶上有價值$3,700的資產。為了驗證這些被盜地址是否也有nonce重複使用的問題,我寫了一個指令碼來檢查他們歷史上交易檢查是否也有相同的r值。我檢查這隻怪獸(駭客)盜取的第二地址,發現它之前傳送的交易也存在r值重複使用的問題。 robert@mbp nonce-reuse-bait % node get-tx-history.js same r found! r: 0xf0d7b10f398357f7d140ff2be1bea9165d32238360ad0f82911235868be7c6e1 hash: 0xb5d2454d7380bfa7ac75ec76f15eecb56e60941429153081fe799fb53a7ff901 r: 0xf0d7b10f398357f7d140ff2be1bea9165d32238360ad0f82911235868be7c6e1 hash: 0x9e459be7fa9950835a3c2594d3440c684fed05fa8e12e8088cc7776c4afb364c same r found! r: 0x41d43fd626c24e449ac54257eeff271edb438bbabbc9bee3d60a5bd78dc39d6d hash: 0x670f66ff71882ae35436cd399adf57805745177b465fdb44a60b31b7c32e4d16 r: 0x41d43fd626c24e449ac54257eeff271edb438bbabbc9bee3d60a5bd78dc39d6d hash: 0x374180005946ef3b1906ee1677f85fa62eb5a834aa0241b4c9c74174bca26a07 這進一步證明了我的猜測,這隻怪獸緊緊盯著以太坊上的交易,時刻關注著是否有nonce複用的情況。一旦發現了nonce複用,它會慢慢的,悄悄地把這些地址上的資產都取光。 在我分析了這隻怪獸攻擊的其他地址後,我驚奇的發現有些地址並沒有nonce重複使用的問題,事實上在它攻擊的20個地址裡,有9個存在nonce重複使用的問題,而其他並沒有。 那麼其他11個地址是什麼因為什麼遭到了攻擊呢?老實來說我並不清楚,一種可能性是這隻怪獸同時還有其他策略去嘗試推算私鑰,例如用常見的一些單詞,數字來推算私鑰。同時還有一些其他的方法來利用nonce生成中的漏洞。但是我並沒有從這隻怪獸的行為中找到證明。 以太坊黑暗森林裡的這隻怪獸終於被我找到,但是會不會有或者誰是它的下一個受害者?這依舊是個謎題。 亞倫簡評: 私鑰生成不隨機,nonce生成不隨機,nonce重複使用這些問題都會有導致私鑰有洩漏的可能性。 對於普通使用者來說,選擇靠譜的錢包是避免這個問題的最好方法,尤其推薦使用有真隨機數生成器的硬體錢包。 對於開發者來說,重視隨機數的產生,選擇靠譜的cryptographic library。同時使用確定性ecdsa簽名演算法也是避免這個問題的另一種方式。 **文章來源:亞倫碎語 譯者:陳東**

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;