原文:http://sirdarckcat.blogspot.jp/2016/12/how-to-bypass-csp-nonces-with-dom-xss.html
原作者:EDUARDO VELA
譯:Holic (知道創宇404安全實驗室)
TL;DR
CSP nonces 在對抗 DOM XSS 方面似乎并沒有所謂的奇效。你可以通過幾種方法繞過它們。我不知道如何修復,也許不應該修復。
感謝閱讀。這篇博文介紹了繞過 CSP nonce 的方法。從上下文入手,然后介紹了在幾種情況下繞過 CSP nonce ,結尾是一些評論。一如既往,這篇博文是我對這些問題的個人意見,而且很想聽到你的觀點。
我與 CSP 的關系,“錯綜復雜”
我曾很喜歡 Content-Security-Policy。打印在 2009 年,我為此興奮無比。我興致高漲,甚至花了一段時間在我的 ACS 項目 上實現 CSP (據我所知,這是第一個運行的 CSP 實現/原型)。它支持 hash 和白名單,我當時確信這可太棒了!我的摘要都是以“我是如何解決 XSS”作為開頭的。
但是有一天,我的朋友 elhacker.net(WHK) 指出 ACS(以及 CSP 的擴展)通過 JSONP 可以很簡單地繞過。他指出,如果你將包含 JSON 接口的域名列入白名單,那么你的防護就被破解了,而且確實有那么多問題,我還沒有發現一個簡單的方法來解決這個問題。我的心都碎了。?
快進至 2015 年,當時 Mario Heiderich 舉辦了一個很酷的 XSS 挑戰賽,叫做“sh*t, it's CSP”,其中有個挑戰題是逃避一個看起來安全的 CSP 防護以及盡可能給出最短的 payload。不出所料,JSONP 出現了(但 Angular 和 Flash 也可以)。多說無益。
然后終于在 2016 年,一篇頗受歡迎的文章 “CSP Is Dead,Long Live CSP!” 總結了 WHK 和 Mario 對互聯網整體 CSP 部署進行調查之后所強調的問題,由 Miki, Lukas, Sebastian 以及 Artur展示。這篇文章的結論是,CSP 白名單破碎不堪毫無益處。至少給 CSP 辦場葬禮吧,我想。
CSP nonces 首先推出的時候,我對它們的關注點在于,它的傳播似乎真的很困難。為了解決問題,2012 年的 dominatrixss-csp 致力于解決此問題,以便所有動態生成的腳本節點通過使用 dynamic resource filter 傳播腳本隨機數。這直接簡化了隨機數(nonce)的傳播。因此,準確的方法在其文章中提出,并命名為 "strict-dynamic",而不是 "dominatrixss-csp" 的運行時腳本。這便有了很大的改善。我們自己獲得了原生 dominatrixss!
這種新風格的 CSP,建議完全忽略白名單,并完全依賴隨機數(nonces)。雖然 CSP nonces 的部署比白名單更困難(因為它需要服務器端更改每個單獨的頁面與策略),盡管如此它似乎提出了真正的安全上的益處,這顯然缺乏基于白名單的方法。所以,今年秋天,我再一次為此感到相當樂觀。也行有一種方法,使大多數 XSS 實際上 *真的* 無法利用。CSP 畢竟不是蓋的。
但是在這個圣誕節,就像是圣誕老人帶來一顆煤球,Sebastian Lekies 指出一點,在我看來似乎是對 CSP nonce 的重大打擊,幾乎使 CSP 對 2016 年的很多 XSS 漏洞無效。
A CSS+CSP+DOM XSS three-way
盡管 CSP nonce 看起來確實能緩解 15 歲的 XSS 漏洞,它對 DOM XSS 來說似乎并不是很有效。為了解釋原因,我需要告訴你當代 Web 應用程序是怎么寫的,這與2002年有何不同。
以前,大多數應用程序的邏輯存在于服務端,但是過去十年中它已經越來越多地向客戶端發展。現如今,開發 Web 應用程序最有效的方法是在在 HTML JavaScript 中變現大多數 UI 代碼。這允許 web 程序可離線使用,而且終端提供強大的 web API 訪問。
現在,新開發的應用程序仍存在 XSS,區別在于,由于很多代碼是用 JavaScript 寫的,所以現在它們存在 DOM XSS 漏洞。這些正是 CSP nonces 不能統一防御(至少按照目前的實現)的錯誤類型。
我給你三個 DOM XSS 的例子(當然,列表不完全概括),CSP nonces 通常不能單獨防御:
- 持久型 DOM XSS,當攻擊者可以強制將頁面跳轉至易受攻擊的頁面,并且 payload 不包括在緩存的響應中(需要提取)。
- 包含第三方 HTML 代碼的 DOM XSS 漏洞(例如,fetch(location.pathName).then(r=>r.text()).then(t=>body.innerHTML=t);)
- XSS payload 存在于
location.hash中的 DOM XSS 漏洞(例如https://victim/xss#!foo?payload=)
個中緣由需要回溯到 2008 年(woooosh!)。早在 2008 年, Gareth Heyes, David Lindsay(http://twitter.com/thornmaker) 和我在微軟 Bluehat 做了一個小型演示,叫做 CSS - The Sexy Assassin。其中,我們演示了一種技術,純粹用 CSS3 選擇器讀取 HTML 屬性(被 WiSec 偶然重新發現,并在幾個月后由 kuza55 在他們的 25c3 訪談 Attacking Rich Internet Applications 中呈現)。
這個攻擊方式的結論是,創建一個 CSS 程序從 HTML 屬性中逐字節提取數據是可行的,只需通過在每次 CSS 選擇器匹配時生成 HTTP 請求,并持續重復。如果你沒有看懂它是怎么回事,看看這里。它的工作方式非常簡單,它只是創建了一個表單的 CSS 屬性選擇器:
*[attribute^="a"]{background:url("record?match=a")}
*[attribute^="b"]{background:url("record?match=b")}
*[attribute^="c"]{background:url("record?match=c")}
[...]
然后,一旦我們得到匹配結果,重復:
*[attribute^="aa"]{background:url("record?match=aa")}
*[attribute^="ab"]{background:url("record?match=ab")}
*[attribute^="ac"]{background:url("record?match=ac")}
[...]
直到它提取出完整的屬性。
script 標簽的攻擊非常簡單。我們需要做完全相同的攻擊,只需要確保 script 標簽設置為 display:block; 。
因此,我們現在可以使用 CSS 提取 CSP nonce,我們唯一需要這么做的就是在同一頁面注入多次。上面我給你的 DOM XSS 的三個例子正是如此。一種在同一文檔多次注入 XSS payload 的方法。三個完美方案。
Proof of Concept
Alright! Let's do this =)
首先,要有持久型 DOM XSS。這一點是普遍問題,因為如果在“新世界”中,開發者應該使用 JavaScript 編寫 UI,那么動聽的內容需要異步來自服務器。
我的意思是,如果你使用 HTML + JavaScript 編寫的 UI 代碼,那么用戶數據必須來自服務器。雖然此設計模式允許你控制應用程序加載的流程,但是它也使得加載同一文檔兩次可以返回不同的數據。
當然,現在的問題是:如果強制網頁文檔加載兩次!當然是用 HTTP 緩存了!這就是 Sebastian 在圣誕節給我們展示的。
Sebastian 解釋了 CSP nonce 與大多數緩存機制不見人的情況,并提供了一個簡單的 PoC 來證明。經過 Twitter 的一系列討論,結論相當清楚。
向你展示一個例子吧,我們從 APPEngine 入門指南中獲取默認的 Guestbook 示例,并添加一些改動:添加 AJAX 支持和 CSP nonces。應用程序很簡單,有明顯的 XSS 漏洞,但它被 CSP nonces 防護了,真的嗎?
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.jmbmsq.com/166/