前言
其實看這篇文章就差不多懂了:
https://www.cnblogs.com/b1gstar/p/5996549.html
這里只是記錄一下方便理解
//1.URL 編碼 "javascript:alert(1)"
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">123</a>
//2.HTML字符實體編碼 "javascript" 和 URL 編碼 "alert(2)"
<a href="javascript:%61%6c%65%72%74%28%32%29">123</a>
//3.URL 編碼 ":"
<a href="javascript%3aalert(3)">123</a>
//4.HTML字符實體編碼 < 和 >
<div><img src=x onerror=alert(4)></div>
//5.HTML字符實體編碼 < 和 >
<textarea><script>alert(5)</script></textarea>
//6.textarea中直接alert
<textarea><script>alert(6)</script></textarea>
//7.HTML字符實體編碼 " ' " (單引號)
<button onclick="confirm('7');">Button</button>
//8.Unicode編碼 " ' " (單引號)
<button onclick="confirm('8\u0027);">Button</button>
//9.HTML字符實體編碼 alert(9);
<script>alert(9);</script>
//10.Unicode 編碼 alert
<script>\u0061\u006c\u0065\u0072\u0074(10);</script>
//11.Unicode 編碼 alert(11)
<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>
//12.Unicode 編碼 alert 和 12
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
//13.Unicode 編碼 " ' " (單引號)
<script>alert('13\u0027)</script>
//14.Unicode 編碼換行符(0x0A)
<script>alert('14\u000a')</script>
以上是一些xss編碼解析的例子
首先需要知道html在解析相應的文檔時可能會使用:html解析
、url解析
、javascript解析
html解析:
一個HTML解析器作為一個狀態機,它從輸入流中獲取字符並按照轉換規則轉換到另一種狀>態。在解析過程中,任何時候它只要遇到一個'<'符號(后面沒有跟'/'符號)就會進入“標簽開始狀態(Tag open state)”。然后轉變到“標簽名狀態(Tag name state)”,“前屬性名狀態(before attribute name state)”......最后進入“數據狀態(Data state)”並釋放當前標簽的token。>當解析器處於“數據狀態(Data state)”時,它會繼續解析,每當發現一個完整的標簽,就會釋放出一個token。
例如:
<a href="127.0.0.1">123</a>
首先html匹配到<
,進入標簽開始狀態,然后進入標簽名狀態,並匹配到a
,然后進入前屬性名狀態,進行一系列屬性名、屬性值匹配,匹配完href="127.0.0.1"后,進入數據狀態,匹配完數據123,結束標簽,結束匹配
這大概就是html解析的簡略過程,然后呢下面說一下幾個概念
字符實體、引用
其實也就是html字符實體編碼,例如:<,>
(<,>),html中也有一些預留的字符實體,稱之為html字符實體,如:<
這兩個都是為了轉義用戶輸入的<或>而用的編碼形式,使其不會成為一個新標簽的開始或結束,也就是
解析器在解析這個字符引用后不會轉換到"標簽開始狀態",只會將其當作"數據"處理
這也是為什么例4不會彈窗的原因
字符引用包括"字符值引用"和"字符實體引用",在上述例子中,'<'對應的字符值引用為\<
,對應的字符實體引用為\<
。字符實體引用也被叫做“實體引用”或“實體”。
下面有三種情況可以容納字符實體:
"數據狀態中的字符引用"
"RCDATA狀態中的字符引用"
"屬性值狀態中的字符引用"
"數據狀態中的字符引用"
第一種也就是在數據內容中可以用字符實體,如:
<a href="127.0.0.1"><</a>
其中<會被解析成<
"RCDATA狀態中的字符引用"
要了解這個,首先需要知道在HTML中有五類元素:
- 空元素(Void elements),如<area>,<br>,<base>等等
- 原始文本元素(Raw text elements),有<script>和<style>
- RCDATA元素(RCDATA elements),有<textarea>和<title>
- 外部元素(Foreign elements),例如MathML命名空間或者SVG命名空間的元素
- 基本元素(Normal elements),即除了以上4種元素以外的元素
五類元素的區別如下:
- 空元素,不能容納任何內容(因為它們沒有閉合標簽,沒有內容能夠放在開始標簽和閉合標簽中間)
- 原始文本元素,可以容納文本
- RCDATA元素,可以容納文本和字符引用
- 外部元素,可以容納文本、字符引用、CDATA段、其他元素和注釋
- 基本元素,可以容納文本、字符引用、其他元素和注釋
可以看到所謂RCDATA也就是一種html元素,而這種元素的標簽為<textarea>或<title>
這意味着在<textarea>和<title>標簽中的字符引用會被HTML解析器解碼
在瀏覽器解析RCDATA元素的過程中,解析器會進入“RCDATA狀態”。在這個狀態中,如果遇到“<”字符,它會轉換到“RCDATA小於號狀態”。如果“<”字符后沒有緊跟着“/”和對應的標簽名,解析器會轉換回“RCDATA狀態”。這意味着在RCDATA元素標簽的內容中(例如<textarea>或<title>的內容中),唯一能夠被解析器認做是標簽的就是它本身,即</textarea>或者</title>。因此,在<textarea>和<title>的內容中不會創建標簽,就不會有腳本能夠執行。所以例5,例6都不能執行,但他還是能解析實體編碼:
<textarea><script>alert(5)</script></textarea>
至於外部元素中的,CDATA:
我們來迅速看一下CDATA元素。任何在CDATA元素中的內容將不會觸發解析器創建開始標簽。閉合CDATA元素的標志是“]]>”序列。因此如果用戶想逃出CDATA元素,就要用未經任何編碼的“]]>”序列,不然是不會逃出CDATA元素的。
"屬性值狀態中的字符引用"
這部分將放到url解析中講述
url解析
這個都比較清楚,着重看一下例1:
//1.URL 編碼 "javascript:alert(1)"
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">123</a>
這里是:不能對協議類型進行任何的編碼操作,不然URL解析器會認為它無類型
例1中對js偽協議(:
也是其中一部分)進行了編碼,導致url解析其為無類型,於是沒有彈窗
再來看一下例2:
//2.HTML字符實體編碼 "javascript" 和 URL 編碼 "alert(2)"
<a href="javascript:%61%6c%65%72%74%28%32%29">123</a>
這里就用到了上面沒說的:屬性值狀態中的字符引用
由於屬性值狀態中的字符實體是能被解析的,所以上面的代碼首先經過html解析會變成:
<a href="javascript:%61%6c%65%72%74%28%32%29">123</a>
然后進行url解析,變成
<a href="javascript:alert(2)">123</a>
由此即可正常彈窗
例7也是一樣:
//7.HTML字符實體編碼 " ' " (單引號)
<button onclick="confirm('7');">Button</button>
這里'作為屬性值中的字符實體,先被解析成'
,然后與前面的單引號閉合彈窗
javascript解析
HTML五類元素中的第二類:原始文本元素中的<script>
就與js解析有關,而script塊有個有趣的屬性:在塊中的字符引用並不會被解析和解碼
看到例9
//9.HTML字符實體編碼 alert(9);
<script>alert(9);</script>
由於在script塊中的字符實體編碼不會被解析,所以不會彈窗
不過,js還支持unicode解析。根據文章,js是否會解析unicode編碼並執行需要:"視情況而定"
具體要看被編碼的序列到底是哪部分,unicode可以放在3個部分:字符串中,標識符名稱中和控制字符中
字符串中
當Unicode存在於字符串中時,它只會被解釋為正規字符,而不是單引號,雙引號或者換行符這些能夠打破字符串上下文的字符。因此,Unicode轉義序列將永遠不會破環字符串上下文,因為它們只能被解釋成字符串常量。
標識符名稱中
當Unicode轉義序列出現在標識符名稱中時,它會被解碼並解釋為標識符名稱的一部分,例如函數名,屬性名等等。
看例10:
//10.Unicode 編碼 alert
<script>\u0061\u006c\u0065\u0072\u0074(10);</script>
這里unicode出現在alert這個函數中,允許被解析並執行
控制字符中
當用Unicode轉義序列來表示一個控制字符時,例如單引號、雙引號、圓括號等等,它們將不會被解釋成控制字符,而僅僅被解碼並解析為標識符名稱或者字符串常量。如果你去看ECMAScript的語法,就會發現沒有一處會用Unicode轉義序列來當作控制字符。例如,如果解析器正在解析一個函數調用語句,圓括號部分必須為“(”和“)”,而不能是\u0028和\u0029。
說的已經比較詳細了,還是看個例子:
例11
//11.Unicode 編碼 alert(11)
<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>
這里本來是alert(11),但是unicode並不能用來表示一個控制字符,如其中的(),所以不會彈窗
例13也是同樣的不能用來表示單引號
//13.Unicode 編碼 " ' " (單引號)
<script>alert('13\u0027)</script>
而例8中有onclick這個屬性,所以也會調用js解析,不過也不會用來表示單引號
//8.Unicode編碼 " ' " (單引號)
<button onclick="confirm('8\u0027);">Button</button>
例12看上去沒有違背這個規則為什么沒有彈窗
//12.Unicode 編碼 alert 和 12
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
因為unicode解碼之后是ASCII型的數字,需要使用單引號閉合
<script>\u0061\u006c\u0065\u0072\u0074('\u0031\u0032')</script>
以上就是html、url、js解析器的簡略內容
最后完善一下上面的例子:
//1.URL 編碼 "javascript:alert(1)"
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">123</a>
//不會彈窗,js偽協議被url編碼會被url解析器當作無類型
//2.HTML字符實體編碼 "javascript" 和 URL 編碼 "alert(2)"
<a href="javascript:%61%6c%65%72%74%28%32%29">123</a>
//會彈窗,經過html解析實體編碼后得到javascript:,得到一個js偽協議
//3.URL 編碼 ":"
<a href="javascript%3aalert(3)">123</a>
//不會彈窗,同1
//4.HTML字符實體編碼 < 和 >
<div><img src=x onerror=alert(4)></div>
//不會彈窗,html實體不能當作新標簽開始
//5.HTML字符實體編碼 < 和 >
<textarea><script>alert(5)</script></textarea>
//不會彈窗,由於是RCDATA 狀態中的字符引用
//6.textarea中直接alert
<textarea><script>alert(6)</script></textarea>
//不會彈窗,同6
//7.HTML字符實體編碼 " ' " (單引號)
<button onclick="confirm('7');">Button</button>
//會彈窗,屬於屬性值狀態的字符引用
//8.Unicode編碼 " ' " (單引號)
<button onclick="confirm('8\u0027);">Button</button>
//不會彈窗,屬於控制符中的unicode,不能表示控制字符
//9.HTML字符實體編碼 alert(9);
<script>alert(9);</script>
//不會彈窗,script塊中的字符實體編碼不會被解析
//10.Unicode 編碼 alert
<script>\u0061\u006c\u0065\u0072\u0074(10);</script>
//會彈窗,屬於標識符名稱中的unicode,用來表示alert
//11.Unicode 編碼 alert(11)
<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>
//不會彈窗,同8
//12.Unicode 編碼 alert 和 12
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
//不會彈窗,unicode解碼后為ASCII類型,需要加引號
//13.Unicode 編碼 " ' " (單引號)
<script>alert('13\u0027)</script>
//不會彈窗,同8
//14.Unicode 編碼換行符(0x0A)
<script>alert('14\u000a')</script>
//會彈窗,因為用引號閉合