●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伺服器再次回應真實IP87.87.87.87
。但是這一次,由於目的位於8545埠上,所以apache
會使用不同的虛擬主機進行回應,進而開始DNS重新綁定攻擊。 - 在
randomrsub.attacker.com:8545
中的Javascript
會等待60秒,然後發送一個XmlHttpRequest
給randomrr.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指向目標添加iframe
和TADA
即可!
所以現在我們可以閱讀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上私訊我。