您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何通過欺騙性的React元素實現XSS,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
HackerOne 是一個安全響應和漏洞賞金的平臺,口號是「從頭開始 (構建),并把安全作為頭等大事」。創建 HackerOne 的團隊包括了幾名安全工程師和滲透測試人員,因此,能夠在他們的網站上發現漏洞,我感到非常興奮。
React 是一個流行的 JavaScript 庫,用于構建用戶界面。
我是 Trello(一個團隊協作應用)的一名開發人員,并且負責運營 Trello 在 HackerOne 上的漏洞賞金項目。偶爾,我也會嘗試在我使用的應用中發現安全問題。
這個問題是由我以前針對 HackerOne 的另外一個 XSS 漏洞,一個暫定還未公開披露的報告以及一個CSP繞過漏洞而引起的。
當我在尋找安全問題時,我花費了大量的時間嘗試創建「意外」的情況。一個網站要求我填寫名字,我給它發送一些 HTML。他們要求我填寫 URL,我給他們一個 URL,其中包含了一堆的引號,換行符,空字符等等。你明白我的意思了吧。
瀏覽 HackerOne 網站時,我查看了一些發送的 AJAX 請求,注意到其中的一些請求將復雜的 JSON 對象作為請求體來發送,例如:
{ state: "open", substate: "triaged", report_ids: [ 49652, 46916 ], reply_action: "change-state", reference: "http://danlec.com" }
我嘗試發送類似的請求,并將某些字段設置為我認為無效的類型。例如,在期望的參數類型是數字的地方,發送一個字符串類型的值 danlec。或者在通常應該發送字符串類型的地方,發送一個數組。例如:
{ state: { "foo": "danlec", "bar": 42 }, substate: 3.2, report_ids: [ "xyzzy", 46916 ], reply_action: [2, "change-state"], reference: { "a": 1, b: ["2"] } }
不出意外地,這些值幾乎普遍被拒絕了,或者被轉換為適當類型的值(例如,{ 'foo': 'bar' } 可能會轉換為字符串類型'{ "foo" => "bar" }')。
然而,在有些地方,錯誤類型的值并沒有直接被拒絕,而是從服務器返回。也就是說,似乎錯誤類型的值被存儲了,并在后續的 API 調用的響應中被返回了。
我發現了兩個例子,一個是名為 reference 的字段,被用來對報告進行分類。另一個是名為 data 的字段,該字段與觸發器條件關聯。
不幸的是,盡管這絕對是奇怪的,但實際上這些錯誤類型的值被安全地渲染到頁面上了。我無法立即想到利用此行為的方法。我將其添加到有潛力進行深度挖掘的 bug 列表中。
在接下來的一周中,我不斷重試這個 bug。我嘗試了幾種不同的值,雖然我無法做任何的壞事,但我確實注意到有些值被奇怪地渲染了。
通常,像"foo"這樣的字符串值將被渲染為類似于
<span>foo</span>
我注意到當該值改為一個數組時,比如 ["foo", "bar"],它會被渲染為
<span><span>foo</span><span>bar</span></span>
甚至更令人興奮的是,當使用 { foo: "bar" } 之類的對象時,在渲染時,foo 被包含在了 span 元素的 react-id 屬性中,例如
<span react-id="…1.2.3.foo.…">bar</span>
我肯定能對最終渲染出來的 DOM 元素產生一些影響,但是負責渲染的代碼在內容凈化(對非法字符進行轉義)方面做得很好。我仍然無法利用它做任何不好的事情。
我已經準備要把這個 bug 提交了,并定義為「它是個奇怪的東西,很可能是一個 bug,但沒有任何安全隱患」。但是我決定最后一次嘗試,看看是否可以通過單步調試負責渲染元素的客戶端代碼來找到任何東西。
在錯誤類型的值被渲染的地方設置了斷點之后,我開始逐步調試所有代碼,來看看是否有什么有趣的事情可以為我所用。大多數字符都被壓縮了(minified JS 文件中,變量名通常會被替換成隨機單個字符),所以并不總是很清楚到底發生了什么,但是當我看到錯誤類型的值被傳遞給這個函數時:
l.isValidElement = function(e) { var t = !(!e || !e._isReactElement); return t }
我開始變得興奮。(這是安全性研究中真正有趣的部分,你知道自己發現了一些不好的東西,現在你只需要弄清楚它到底能有多糟糕)
我已向自己證明了,我可以使 e 變成任何我想要輸入的 JSON 值。所以,不需要任何技巧,便可以創建一個對象并且將其_isReactElement 的值設置為 true,這似乎可以告訴客戶端我創建的這個對象是一個「有效的元素」。
一旦我添加_isReactElement,和其他幾個鍵(_store,type,props),我創建的對象會使用一種完全不同的渲染方式,這種方式最終會檢查 dangerouslySetInnerHTML 屬性。現在有了一個看起來很有趣的屬性可被操作。
顯然,該屬性名稱正試圖警告我,包含原始 HTML 是很危險的。但是,當然,我對于將 dangerouslySetInnerHTML 屬性添加到偽造的 React 元素中沒有任何的不安。一旦該屬性設置好之后,render 方法會渲染出我傳入的 HTML 片段,允許任意 HTML 注入,XSS 等。
漏洞利用已實現!
當我使 XSS 正常工作后,就會回退一步來弄清楚實際發生了什么。HackerOne 使用 React,結果證明 React 的 createElement 方法有一個參數,期望接受的值是一個 React 節點,可以是字符串(對于簡單的文本內容)或者是數組,或者是 React 元素。
還記得我是怎么注意到有時我的輸入會被渲染成多個 span 嗎?
我嘗試從控制臺運行一些 React 方法,結果很清楚實際發生了什么:
> React.renderToString(React.createElement("span", null, "abc")) "<span data-reactid=".7" data-react-checksum="-876606633">abc</span>" > React.renderToString(React.createElement("span", null, ["abc"])) "<span data-reactid=".8" data-react-checksum="-171174425"> <span data-reactid=".8.0">abc</span></span>" > React.renderToString(React.createElement("span", null, { foo: "bar" })) "<span data-reactid=".a" data-react-checksum="1979389930"> <span data-reactid=".a.$foo:0">bar</span></span>"
HackerOne 的客戶端代碼中,假定它傳遞的值始終是一個字符串,但是我能夠讓它傳遞我特意構造的 JSON 對象,并且通過設置正確的屬性,使 React 認為它正在渲染一個元素。
dangerouslySetHTML 是一個特殊的非 DOM 屬性,用于提供插入原始 HTML 到元素中的這種功能,這正是像我這樣的 XSSer 所尋找的。
我使用的是完整的 JSON 對象,而不是 HackerOne 期望的字符串,類似于
{ _isReactElement: true, _store: {}, type: "body", props: { dangerouslySetInnerHTML: { __html: "<h2>Arbitrary HTML</h2> <script>alert('No CSP Support :(')</script> <a href='http://danlec.com'>link</a>" } } }
在控制臺上渲染這個 JSON 對象:
> React.renderToString(React.createElement("span", null, { _isReactElement: true, …})) "<span data-reactid=".9" data-react-checksum="-1151650166"> <body data-reactid=".9.0"><h2>Arbitrary HTML</h2> <script>alert('No CSP Support :(')</script> <a href='http://danlec.com'>link</a></body></span>"
關于如何通過欺騙性的React元素實現XSS就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。