xss之理解浏览器解码过程

前言

xss漏洞挖掘的时候有时候是碰运气,姿势对了就能挖到,姿势不对就没有反应。说到底还是对xss理解不够深刻,特别是浏览器对一些payload的解码过程,如果能够理解,对绕waf也有一些帮助。下面记录学习浏览器解码过程的一些收获。

先从问题出发

看一下哪些payload能够触发,哪些不能呢?为什么呢?

  1. <a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">123</a>

  2. <a href="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:%61%6c%65%72%74%28%32%29">123</a>

  3. <a href="javascript%3aalert(3)">123</a>

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

  5. <textarea>&#60;script&#62;alert(5)&#60;/script&#62;</textarea>

  6. <textarea><script>alert(6)</script></textarea>

  7. <button onclick="confirm('7&#39;);">Button</button>

  8. <button onclick="confirm('8\u0027);">Button</button>

  9. <script>&#97;&#108;&#101;&#114;&#116&#40;&#57;&#41;&#59</script>

  10. <script>\u0061\u006c\u0065\u0072\u0074(10);</script>

  11. <script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>

  12. <script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>

  13. <script>alert('13\u0027)</script>

  14. <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;">123</a>

编码与解码

xss涉及的编/解码大体分为三种(三种解析器)

  • html实体编码/解码
  • jsunicode编码/解码
  • url编解码
    其中第三种url编码我们都不陌生了。主要介绍前两种。

html实体编码

先说明html字符实体是什么。在HTML中,某些字符是预留的。例如在HTML中不能使用<>,这是因为浏览器可能误认为它们是标签的开始或结束。如果希望正确地显示预留字符,就需要在HTML中使用对应的字符实体。比如<的实体名称为&lt,其实体编号为&#x3c;。要注意的是有的字符没有实体名称,但实体编号是存在的。这样字符与字符实体之间的对应关系就形成了,那么字符到实体的映射就是html实体编码,实体到字符的映射就是对应解码。alert(1)的html实体编码为&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x31;&#x29;

jsunicode编解码

js提供了四种字符编码的策略,

  1. 三个八进制数字,如果不够个数,前面补0,例如“e”编码为“\145”
  2. 两个十六进制数字,如果不够个数,前面补0,例如“e”编码为“\x65”
  3. 四个十六进制数字,如果不够个数,前面补0,例如“e”编码为“\u0065”
  4. 对于一些控制字符,使用特殊的C类型的转义风格(例如\n和\r)

协同工作

前面编码的细节没有讲太多,主要关注这三种解析器如何工作。首先明确的一点是

浏览器从服务器获得文档后先对文档进行html解码,构建起dom树。

这里也就解释了为什么使用html编码能够避免xss。因为在html解码过程中那些html实体会先通过既定的规则映射为对应字符被当作数据显示出来,html解析器作为一种状态机此时不会识别已经映射的字符为标签开始,因此状态不会转换,标签内的代码也就不会执行。

语法解析完成后dom树也就构建好了。

JavaScript解析器就会介入来解析内联脚本

在这个过程中jsunicode被解码。同时如果浏览器遇到需要url的上下文

url解析器就会进行解析。

根据实际情况的不同,url解析器会在JavaScript解析器解析前后进行解析。也就是url解码和jsunicode解码先后顺序不固定。

下面还要具体补充几点

补充1

HTML解析过程中有三种情况可以容纳字符实体,数据状态中的字符引用RCDATA状态中的字符引用属性值状态中的字符引用。在这些状态中HTML字符实体将会从“&#…”形式解码,对应的解码字符会被放入数据缓冲区中。其中RCDATA状态中的字符引用需要注意。RCDATA为html元素五类中的一类,包括<textarea>,<title>标签。在浏览器解析RCDATA元素的过程中,解析器会进入“RCDATA状态”。在这个状态中,如果遇到<字符,它会转换到RCDATA小于号状态。如果<字符后没有紧跟着/和对应的标签名,解析器会转换回“RCDATA状态”。这意味着在RCDATA元素标签的内容中(例如<textarea><title>的内容中),唯一能够被解析器认做是标签的就是</textarea>或者</title>。当然,这要看开始标签是哪一个。因此,在<textarea><title>的内容中不会创建标签,就不会有脚本能够执行。

补充2

url解析器解析过程中url资源类型必须为ascii字符,否则就会进入无类型状态,也就是我们不能对任何协议类型进行任何编码操作造成无法解析。同样:也不可以进行编码。

补充3

JavaScript解析器就会介入来解析内联脚本过程中还有一些问题需要注意。

  1. 首先<script>元素输入原始文本元素,其块中的字符引用并不会被解析和解码,这和html解析器的状态转换规则有关,因此问题9内的代码虽然进行看似可以执行,但其并不会被进行html实体映射操作。
  2. 还有另一个问题就是形如\u00XX\u00XX的字符js解析器如何解码的问题。总体来说一个\u00XX\u00XX字符被解码,那么他所在的位置大体分为三类
    • 字符串中
      当Unicode转义序列存在于字符串中时,它只会被解释为正规字符,而不是单引号,双引号或者换行符这些能够打破字符串上下文的字符。
    • 标识符名称中
      当Unicode转义序列出现在标识符名称中时,它会被解码并解释为标识符名称的一部分,例如函数名,属性名等等。但不能这样来写\u00AA00BB00CC
    • 控制字符
      当用Unicode转义序列来表示一个控制字符时,例如单引号、双引号、圆括号等等,它们将不会被解释成控制字符,而仅仅被解码并解析为标识符名称或者字符串常量。

总体来说,Unicode转义序列只有在标识符名称里不被当作字符串,也只有在标识符名称里的编码字符能够被正常的解析。

参考

Deep dive into browser parsing and XSS payload encoding

©️2020 CSDN 皮肤主题: 岁月 设计师:pinMode 返回首页