ret2got ●未分類 ●2018年1月19日 ●約4分鐘可讀完

當我們針對如何利用Tavis Ormandy發掘的在本地主機上的未驗證JSON-RPC服務開展討論時,我所想到的第一件事就是把這手法套在以太坊的客戶端(Geth,Mist與Parity)。

大多數以太坊客戶端都在本機上的8545埠上運行JSON-RPC服務,但由於它位於本機上,所以基於SOP的緣故,我們無法直接從用戶的瀏覽器訪問它。在電子錢包中的 這個文章利用了CORS標頭來通過本地主機上的JSON-RPC破解並控制用戶的電子錢包。

這讓Geth的JSON-RPC看起來非常安全,因為它沒有返還任何的CORS標頭,但是後來cpacia則在回文中下了個有關這個以太坊電子錢包的評論,而這正引發了我的新想法。以下是他當時的回文

只是禁用CORS仍然容易受到DNS重新綁定的攻擊。它應該需要被驗證。 〜cpacia

我曾聽說過DNS重新綁定,但從來沒有試圖深入研究它。由於Geth的JSON-RPC也未經過身份驗證,它也很可能容易受到DNS重新綁定攻擊是嗎?

還著實作的想法,我後來在這裡找到了些DNS重新綁定的基本定義。

我嘗試開始研究DNS的重新綁定,但遇到個主要問題是所有的文章都是上古世紀的遺物。所以我在Bug Bounty的論壇上詢問了這個問題,幸運的,Luke Young就將他2017年關於現今可行的DNS重新開發利用之DEFCON神級演講的連結發來給我。它還包括了一個自動化工具,可以在大多數現存瀏覽器上實現DNS重新綁定。

(嘿,起司,我知道你可以,但針對”現存”,請你躺回去。)

了解愈多讓我愈好奇,但我並不想使用任何預製工具。所以我開始編寫我自己的DNS伺服器。Python有一個非常好的函式庫──dnslib。它可以為我處理大部分的東西。我還註冊了一個網域,並設置了一些glue records指向我的伺服器,並將它們用作nameservers

因為我想看看不同瀏覽器在遇到極低的TTL中的行為表現,所以我讓我的DNS服務器返還TTLs < 5。而有趣的是,Chrome、Firefox和Safari都接受了這個等待60秒的DNS回應,儘管他標明TTL小於5。

60秒並不是很長時間,應該還算在可接受範圍,而我想我可以讓用戶留在我的網頁上至少60秒。現在唯一剩下的就是實際嘗試。

我用--rpc旗幟(當然是在testnet上)

geth -rpc -testnet

現在是時候加點Javascript的料了,而這正是最難的部分。因為我不是一個好的web開發人員,每次使用JavaScript的處理某些行為時,我都很難將自己的思緒兜起來以JS實現。所以我跌跌撞撞的在3個小時內寫出了一個非常糟糕但有效果的JavaScript。而最初的結果是成功的。

現在為了使它與geth一起成功運行,因為SOP,我必須在8545埠上運行我的web伺服器和網域。但是,如果我到時要受害者自主將Web連結封包發送給任何埠號為8545的人,但這方法感覺起來實在有點草率且不精緻。

所以我想到的解決方案是使用iframe。我讓apache監聽8545和80,並為這兩個埠各設置一個虛擬主機。現在我可以建立iframe將對80的請求轉到8545埠並在其中隱藏的iframe運行所有的JavaScript。

另一個問題是關於多個用戶,如果多個用戶同時訪問我的網域會怎麼樣?DNS伺服器肯定會因為我這種使用基於計數器的系統而感到困惑,並且因此無法區分各個用戶的請求。這問題讓我整個計畫撞牆了一段時間,直到我記起有subdomains這個東西。

每當用戶訪問主域名時,我都可以用iframe為其添加一個隨機子域名用作標識。我知道我可能沒法很好地解釋它,但接下來我舉個例子好了。

讓我們假設我的網域名稱為attacker.com而我的伺服器的IP是87.87.87.87這樣,那麼流程是:

  • 受害者在瀏覽器中打開attacker.com
  • 首先,DNS請求attacker.com被發送到我的伺服器,並回應以真實IP 87.87.87.87
  • 接下來,attacker.com加載到用戶的瀏覽器,然後創建一個隱藏的iframe與一個隨機子域randomrsub.attacker.com:8545,並將其附加到<body>
  • 現在,DNS請求被發送到我的伺服器的子網域randomrsub.attacker.com,並且DNS伺服器再次回應真實IP 87.87.87.87。但是這一次,由於目的位於8545埠上,所以apache會使用不同的虛擬主機進行回應,進而開始DNS重新綁定攻擊。
  • randomrsub.attacker.com:8545中的Javascript會等待60秒,然後發送一個XmlHttpRequestrandomrr.attacker.com:8545/test
  • 由於DNS緩存已過期,瀏覽器會再次解析DNS。這一次,我的伺服器會回應IP 127.0.0.1
  • 現在請求實際上被發送給127.0.0.1:8545/test而不是我的伺服器,並且由於它來源仍被認知為randomrr.attacker.com:8545,所以我們仍能夠讀取回應。
  • 由於我們每次都會生成一個隨機的子網域,因此我們現在甚至可以容納多個用戶,因為子網域可以執行其身份標識。

為此,我還不得不優化JavaScript,以確保它在95%的時間內運作正常。我在真實的DNS查詢之前添加了一些虛假的DNS查詢,以便它在錯誤的時間不會用錯誤的IP進行響應。

這基本上也可以用存儲型XSS來破解利用。只需將Script的src指向目標添加iframeTADA即可!

所以現在我們可以閱讀JSON-RPC服務的回應,這意味著我們可以讀取他們的以太幣地址,他們的存款數,並且如果他們的賬戶未鎖,他們可能可以竊取他們的以太幣。JSON-RPC的API有一個相當不錯的方法──eth_sendTransaction,它基本上可以用來從用戶帳戶發送ethereum

我在http://rebinddns.ml (注意點擊) 上設置了概念驗證。如果你持續超過60秒,並且使用JSON-RPC運行Geth(或任何其他ethereum客戶端),您將看到一個alert(),其中將包含您的ethereum地址及其餘額。

PoC中使用的所有文件都可以在我的Github上找到。

  • min.js - 在8545埠上生成子網域及隱藏iframe的Js文件
  • main.js - 執行DNS重新綁定的Js文件
  • server.py - 用python編寫的DNS伺服器

本篇主要概念是以注入的惡意轉址搭配DNS重綁定來攻擊以太坊的未驗證JSON-RPC服務,來取得攻擊成果。

我已經證實Geth以太幣的C++客戶端和python客戶端都很脆弱。PoC已經在Firefox,Chrome和Safari上進行了測試。


PS:這已經被報告給了ethereum基金會,但他們並不認為它是一個有效的漏洞。

有任何問題,你都可以到我的twitter@ret2got上私訊我。