??翻譯文章, 原文:Deep dive into browser parsing and XSS payload encoding[1]

這篇博客文章將深入探討HTML,URL和JavaScript的規范和解析器,以及它們之間的交互如何在跨站點腳本轉義中有所作為。對于您而言,這可能很難或容易地完成,具體取決于您對HTML規范和瀏覽器解析器的了解程度。而且,我保證您這是一篇很長的文章,所以準備花一兩個小時從中受益。因此,在繼續討論主題之前,請看一下以下HTML并問自己:腳本是否將要執行?
基礎
1.
URl編碼:javascript:alert(1)
2.
字符實體編碼為"javascript",URL編碼為"alert(2)"
3.
URL編碼:":"
4.
字符實體編碼 < 和 >
5.
字符實體編碼 < 和 >
6.
高級
7.Button
字符實體編碼 '
8.Button
Unicode轉義序列編碼 '
9.
字符實體編碼 alert(0);
10.
Unicode轉義序列編碼 alert
11.
Unicode轉義序列編碼 alert(11)
12.
Unicode轉義序列編碼 alert 和 12
13.
Unicode轉義序列編碼 '
14.
Unicode轉義序列編碼的換行
更高級
15.
這個可以分成幾步轉化:
2
2
這些問題的答案在這里[2],測試頁面在這里[3]。如果您正確回答了大多數問題,并且在嘗試查找解決方案時沒有將黑客弄亂,請在此處停止,您無需閱讀此博客。否則,博客中有關瀏覽器解析過程的其余部分將有所幫助。
解析HTML文檔涉及三個主要過程:HTML解析器,URL解析器和JavaScript解析器。每個解析器負責解碼和解析其在文檔中的份額,以及他們應該如何做到這一點在其相應的解析器規范中明確規定。
HTML解析
從XSS的角度來看,我們最感興趣的是如何對文檔進行標記化,因為我們不希望將用戶提供的數據最終解析為能夠執行腳本的標記。HTML標記化的規范在這里。這是一個冗長的文檔,我不打算涵蓋所有內容。在本文中,我將只介紹我對如何完成文檔解碼以及何時創建新標記感興趣的部分。
HTML解析器用作狀態機,在其中使用輸入流中的字符并根據其轉換規則轉換到不同的狀態。解析期間,只要遇到“
有三個狀態可以包含字符實體,“數據狀態中的字符引用”,“ RCDATA狀態中的字符引用”和“屬性值狀態中的字符引用”。在某些狀態下,HTML實體將從其“...;”解碼形式并將相應的字符插入到數據緩沖區中。例如,在 問題4 中,“”字符在輸入流中被編碼為“”,并且當解析器處于“數據狀態”時將對其進行解析。當解析器遇到'&'字符時,它切換到“處于數據狀態的字符引用”,消耗字符引用并發出相應的字符標記。在這種情況下,它是“”。您可能在想:這是否意味著''標記將被視為open和close標記,然后它們中的腳本將被執行?這個問題的答案是否定的。其背后的原因是,解析器在使用了字符引用后將不會轉換為“標簽打開狀態”,因此,不會創建新標簽。這種確切的行為是我們如何利用字符實體編碼來逃避不受信任的用戶輸入,并確保僅將它們解析為數據。
現在您可能已經了解了為什么我們需要轉義'','''和'"'字符,但是為什么我們仍然需要轉義'&'字符呢?在您看來,“&”是無辜的,并且之后的任何內容僅會被解釋為字符引用,不會觸發并且創建新的“標簽打開狀態”或“結束標簽打開狀態”開始。事實是'&'不會破壞HTML級別的轉義,但會中斷其他級別發生的轉義。稍后我們將在涉及JavaScript解析時再進行討論。
我之前沒有提到過一個概念:什么是RCDATA?要回答這個問題,我們需要了解另一個主題。在HTML中,有五種元素:
Void elements
area, base, br, col, embed, hr, img, input, keygen, link, menuitem, meta, param, source, track, wbr
Raw text elements
script, style
RCDATA elements
textarea, title
Foreign elements
Elements from the MathML namespace and the SVG namespace
Normal elements
所有其他允許的HTML元素都是普通元素
它們之間的區別如下:
Void elements 不能有任何內容(由于沒有結束標簽,因此不能在開始標簽和結束標簽之間放置任何內容)
Raw text elements 可以有文字
RCDATA elements 可以有文字和字符引用
Foreign elements 可以具有文本,字符引用,CDATA部分,其他元素和注釋。
Normal elements 可以具有文本,字符引用,其他元素和注釋。
如果回到HTML解析器規范,可以使用字符引用的狀態之一是“處于RCDATA狀態的字符引用”。這意味著HTML解析器將解碼“textarea”和“title”內部的字符引用。同樣,在解碼那些字符引用時,沒有輸入“標簽打開狀態”,這就是為什么 問題5 中沒有腳本執行的原因。此外,“RCDATA”還有另外一件特別的事情。當瀏覽器解析RCDATA元素時,它將進入“RCDATA狀態”。在這種狀態下,如果遇到“”。這意味著在RCDATA元素(例如,)的標簽內,它唯一識別并認為是標簽的打開標簽字符是“ ”或“”,取決于打開的標簽。因此,將不會在“textarea”或“title”內部創建其他標簽,因此無法在其中執行腳本。這解釋了為什么腳本無法在 問題6 中執行。
關于另一個元素“CDATA”元素需要注意。 CDATA中包含的任何內容都不會導致解析器創建新的打開標簽,并且它以“]]>”序列結尾。因此,如果用戶輸入要轉出CDATA上下文,則必須使用不帶任何編碼的確切“]]>”序列,否則它將不會脫離上下文。(就是只能使用]]>結尾)
URL解析
URL解析器也被稱為建模為狀態機,其中輸入流中的字符可以將其定向到不同的狀態。解析器規范在這里。從安全性或XSS轉義角度來看,有些事情很有趣。
首先, URL方案必須使用ASCII字母字符。(U+0041-U+005A || U+0061-U+007A),否則狀態將轉換為“無方案”狀態。例如,您不能使用任何編碼對協議方案進行編碼,否則URL解析器會將其識別為“無方案”。這就是為什么 問題1 中的腳本不執行的原因,因為URL解析器未將URL編碼的“javascript”解碼并識別為協議。相同的理論也適用于':'字符,如果編碼,該字符將不會被識別。這就是為什么 問題3 中的腳本無法執行的原因。但是,您可能會想:為什么當使用字符實體對方案(javascript)進行編碼時,問題2 的腳本為什么會執行?如果您還記得我們在HTML解析部分中討論的內容,則有一個狀態稱為“處于屬性值狀態的字符引用”,其中字符引用將被解碼并替換為其解碼版本。稍后我們將討論解析順序,但是發生的是HTML解析器解析文檔,創建標記令牌并解碼href屬性中的字符實體。然后,當HTML解析器完成后,URL解析器將啟動以解析href值中的鏈接,這時“ javascript”方案已被解碼,并且被URL解析器識別為該方案。然后,URL解析器繼續對鏈接的后續部分進行URL解碼。因為該方案是“ javascript”,所以JavaScript解析器會進入并執行它,這就是為什么執行 問題2 中的腳本的原因。(注意解析的順序問題)
其次,URL編碼過程使用UTF-8編碼方案對每個字符進行編碼。如果您嘗試使用其他方案對鏈接進行URL編碼而URL解析器無法識別它,則這可能變得很重要。
Javascript 解析
JavaScript解析與HTML解析的不同之處在于,該語言本身是上下文無關的語言,并且存在描述上下文的語法。基本上可以遵循上下文無關的語法來弄清楚如何解析JavaScript。ECMAScript-262的規范位于此處[4],單獨的語法文件位于此處[5]。
關于安全性,字符如何解碼以及在某些情況下轉義是否有效,有幾件特別有趣的事情。
首先,讓我們回到HTML解析的“Raw Text”元素。我有意將其留在本節中,因為它與JavaScript解析有關。所有“script”塊都屬于Raw Text元素,并且“script”塊具有一個有趣的屬性:字符引用不會在其中解析和解碼。這意味著什么?這意味著問題9中的腳本將不會執行。因此,如果您是攻擊者,并嘗試使用字符引用對輸入進行編碼并將其放入腳本塊中,它將無法執行。
那么甥塘塘之類的字符(例如 , )呢,這些字符中編碼的JavaScript是否可以解析并執行?好吧,簡短的答案是:有附加條件。較長的答案取決于編碼序列的放置位置。首先,甥塘塘之類的字符稱為Unicode轉義序列。根據上下文的不同,可以在三個位置放置這些轉義序列:字符串,標識符名稱或控制字符。
字符串:當字符串中存在Unicode轉義序列時,它們將僅被解釋為常規字符,但不能解釋為' "或可以終止字符串上下文的行終止符。ECMAScript規范(在下面列出)中明確指出了這一點。因此,Unicode轉義序列將永遠不會超出JavaScript中的字符串上下文,因為它們始終會被解釋為字符串文字:
ECMA-262 edition5.1 Rev 6, Clause 6"ECMAScript differs from the Java programming language in the behaviour of Unicode escape sequences. In a Java program, if the Unicode escape sequence
, for example, occurs within a single-line comment, it is interpreted as a line terminator (Unicode character 000A is line feed) and therefore the next Unicode character is not part of the comment. Similarly, if the Unicode escape sequence occurs within a string literal in a Java program, it is likewise interpreted as a line terminator, which is not allowed within a string literal—one must write instead ofto cause a line feed to be part of the string value of a string literal. In an ECMAScript program, a Unicode escape sequence occurring within a comment is never interpreted and therefore cannot contribute to termination of the comment. Similarly, a Unicode escape sequence occurring within a string literal in an ECMAScript program always contributes a Unicode character to the literal and is never interpreted as a line terminator or as a quote mark that might terminate the string literal."
標識符名稱:當標識符名稱中存在Unicode轉義序列時,它們將被解碼并解釋為標識符的名稱,例如函數名稱,屬性名稱等。這就是為什么問題10中的腳本是可執行的。如果我們深入研究該規范,則還應明確說明如下。
"Unicode escape sequences are also permitted in an IdentifierName, where they contribute a single character to the IdentifierName, as computed by the CV of the UnicodeEscapeSequence (see 7.8.4). The preceding the UnicodeEscapeSequence does not contribute a character to the IdentifierName. A UnicodeEscapeSequence cannot be used to put a character into an IdentifierName that would otherwise be illegal."
控制字符:當Unicode轉義序列表示控制字符時,例如單引號,雙引號,括號等,它們將不會被解釋為控制字符,只會被解碼和解析為標識符名稱或字符串文字。如果您查看ECMAScript的語法,則沒有Unicode轉義序列可以充當任何控制字符。例如,如果解析是在解析函數調用語句,則括號必須為'('和')',而不是(和)之類的字符。
通常,只有在標識符名稱的上下文中,Unicode轉義序列才會被解釋為不是字符串,并且它們是唯一可以注入這些編碼字符并對其進行解釋的位置。如果我們看問題11,這是行不通的,因為'(11)'無法正確解釋,并且因為'alert(11)'不是有效的標識符名稱。問題12 無效,因為'12'不會被解釋為字符串文字,因為它們必須以單引號或雙引號開頭,或者為ASCII數字。問題13 無效,因為“'”僅被解釋為單引號文字,并且字符串現在不完整。問題14 之所以有效,是因為' '被解釋為換行文字,而沒有引入會破壞JavaScript語法的新換行。
解析流
在討論了HTML,URL和JavaScript解析之后,您應該對要解碼的內容,在何種上下文中以及如何進行有很好的了解。現在,另一個重要的概念是所有這些如何一起發揮作用?網頁上有很多地方需要多個解析器來計算。因此,在這里,我們將簡要討論一下瀏覽器在解碼和轉義方面通常如何解析文檔。
當瀏覽器從網絡堆棧獲取內容時,HTML解析器被觸發并開始標記文檔。這是完成字符引用解碼的步驟。完成標記化之后,將構建DOM樹,并啟動JavaScript解析器解析內聯和在腳本塊中的腳本。這是解碼Unicode轉義序列和十六進制轉義序列的步驟。同時,如果瀏覽器遇到期望使用URL的上下文,則URL解析器也會啟動以解碼URL內容。這是完成URL解碼的步驟。根據URL的位置,URL解析器可能會在JavaScript解析器出現之前或之后解析內容。請考慮以下兩個示例。
例子A:例子B:
例子A中,HTML解析器將首先啟動并執行用戶輸入的字符引用解碼。然后,URL解析器開始對href中的值進行URL解碼。最后,如果URL方案是javascript,則JavaScript解析器將出現并執行Unicode轉義序列和十六進制轉義序列解碼。之后,腳本被執行。因此,按照HTML,URL和JavaScript的順序,總共進行了三輪解碼。
例子B中,HTML解析器也會首先啟動。但是,此后,JavaScript解析器開始解析onclick處理程序中的值,因為在onclick處理程序中需要腳本。解析并執行JavaScript后,它看到它正在執行“ window.open()”,其中參數應為URL。此時,URL解析器開始對用戶輸入進行URL解碼,并將結果傳遞回JavaScript引擎。因此,按照HTML,JavaScript和URL的順序,總共進行了三輪解碼。
是否可能會進行超過三輪的解碼?考慮下面的例子?
例子C:
示例C與A類似,但在JavaScript中有一個“ window.open”調用的意義上也有所不同。因此,將在UserInput上進行其他URL解碼。通常,按照HTML,URL,JavaScript,URL的順序進行四個解碼。
附錄(前面問題的答案)
Basics
1.
URL encoded "javascript:alert(1)"
Answer: The javascript will NOT execute.
2.
Character entity encoded "javascript" and URL encoded "alert(2)"
Answer: The javascript will execute.
3.
URL encoded ":"
Answer: The javascript will NOT execute.
4.
Character entity encoded < and >
Answer: The javascript will NOT execute.
5.
Character entity encoded < and >
Answer: The javascript will NOT execute AND the character entities will NOT be decoded either
6.
Answer: The javascript will NOT execute.
Advanced
7.Button
Character entity encoded '
Answer: The javascript will execute.
8.Button
Unicode escape sequence encoded '
Answer: The javascript will NOT execute.
9.
Character entity encoded alert(9);
Answer: The javascript will NOT execute.
10.
Unicode Escape sequence encoded alert
Answer: The javascript will execute.
11.
Unicode Escape sequence encoded alert(11)
Answer: The javascript will NOT execute.
12.
Unicode Escape sequence encoded alert and 12
Answer: The javascript will NOT execute.
13.
Unicode escape sequence encoded '
Answer: The javascript will NOT execute.
14.
Unicode escape sequence encoded line feed.
Answer: The javascript will execute.
Bonus
15.
Answer: The javascript will execute.
References
[1] Deep dive into browser parsing and XSS payload encoding: https://www.attacker-domain.com/2013/04/deep-dive-into-browser-parsing-and-xss.html
[2] 這里: http://test.attacker-domain.com/browserparsing/answers.txt
[3] 這里: http://test.attacker-domain.com/browserparsing/tests.html
[4] 此處: http://www.ecma-international.org/publications/standards/Ecma-262.htm
[5] 此處: http://www.antlr3.org/grammar/1206736738015/JavaScript.g