浅谈XSS的小技巧与浏览器编码

最近系统学了一下以前一直模糊的浏览器编码概念,顺便总结了一下XSS的原理,在此做一个小笔记
首先先介绍一下浏览器的解析概念

HTML五大元素

  1. 空元素(Void elements),如<area>,<br>,<base>等等

  2. 原始文本元素(Raw text elements),有<script><style>

  3. RCDATA元素(RCDATA elements),有<textarea><title>

  4. 外部元素(Foreign elements),例如MathML命名空间或者SVG命名空间的元素

  5. 基本元素(Normal elements),即除了以上4种元素以外的元素

五类元素的区别如下:

  1. 空元素,不能容纳任何内容(因为它们没有闭合标签,没有内容能够放在开始标签和闭合标签中间)。

  2. 原始文本元素,可以容纳文本。

  3. RCDATA元素,可以容纳文本和字符引用。

  4. 外部元素,可以容纳文本、字符引用、CDATA段、其他元素和注释

  5. 基本元素,可以容纳文本、字符引用、其他元素和注释

HTML解析器

一个HTML解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。在解析过程中,任何时候它只要遇到一个’<’符号(后面没有跟’/‘符号)就会进入“标签开始状态(Tag open state)”。然后转变到“标签名状态(Tag name state)”,“前属性名状态(before attribute name state)”……最后进入“数据状态(Data state)”并释放当前标签的token。当解析器处于“数据状态(Data state)”时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。
协议是不能进行编码的

看到这里我相信大部分的读者都是懵逼的,所以这里我来举个例子

  1. 在解析<这个符号以前,状态是Data State

  2. 然后解析到<的时候,解析状态变为Tag open state,然后开始搜寻标签名,(在搜寻标签名的时候,我们要思考一个问题,<和标签名img并不是同一个token,他们显然是分别进行解析的,那么有没有可能忽略掉<和img之间的空格或者换行什么的?这个问题,我相信很好找到答案。)

  3. 找到标签名,状态变为Tag name state,这个状态就表示已经识别了标签名,

  4. 然后知道读取到最近的一个>时,结束tag name state的状态,重新进入Data State。

有了上面的概念,我们再来理解一下什么时候我们的HTML字符实体在哪些情况下能被浏览器解析

一般来说,HTML编码要在Data state(标签外部和标签的text段),RCDATA状态或者标签内的属性值的位置才能解析,然而对于一些特殊情况,例如RCDATA有个特殊的情况。在浏览器解析RCDATA元素的过程中,解析器会进入“RCDATA状态”。在这个状态中,如果遇到“<”字符,它会转换到“RCDATA小于号状态”。如果“<”字符后没有紧跟着“/”和对应的标签名,解析器会转换回“RCDATA状态”。这意味着在RCDATA元素标签的内容中(例如<textarea><title>的内容中),唯一能够被解析器认做是标签的就是“</textarea>”或者“</title>”。当然,这要看开始标签是哪一个。因此,在“<textarea>”和“<title>”的内容中不会创建标签,就不会有脚本能够执行。

所以就可以合理解释下面的payload能不能弹出payload

再试一下浏览器的解码顺序问题

浏览器一般的解码顺序是先进行html解码,在进行javascript解码,最后再进行url解码,这样一步一步下来我们就可以实现
这样我们就可以理解为什么下面无法弹出payload

1
<script>alert&#40;'1')</script>

因为script标签内无法解析HTML实体编码,因为其不是在data state数据段内,然而下面的payload又是可以弹的

1
<svg><script>alert&#40;'1')</script>

有印象的同学应该记得我们在上面说过<svg>属于外部标签,那么我们百度一下SVG到底是一种上面样的标签

这里我们可以发现SVG属于支持XML解析,所以那么我们就很好理解了,因为下xml支持在标签内解析HTML实体字符,所以在在XML中&#40;会被解析成

XSS的小技巧

说了这么多理论的东西,估计同学们都烦了,下面来说一下XSS的一般思路吧,

确定我们的输入点在哪里

打入常规的payload如

1
<script>alert('1')</script>

查看源代码查找我们的输出点在哪里,以及是否有过滤之类的

  1. 如果是单纯的字符串替换,fuzz一下常用的双写,大小写等方法
  2. 如果是过滤了script等标签,我们可以尝试用一些别的标签代替,比如常用的
    1
    2
    img
    iframe

标签等

  1. 如果是过滤了(),我们可以使用反引号`代替(),或者使用

    1
    <iframe srcdoc="<script>parent.alert&#40;1&#41;</script>"
  2. 还可以使用data协议绕过

    1
    <object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgiMSIpOzwvc2NyaXB0Pg=="></object>
  3. 如果是过滤了<>,那么看看输出点是否在标签内一般都是通过构造事件onload,或者onclick等事件进行弹窗。

  4. 如果是过滤了alert,那么我们也有常用的
    1
    2
    3
    confirm
    throw
    propmt

  1. 当然一般CTF高分的XSS都是利用一些chrome或者firefox爆出的最新的漏洞,比如上次国赛就是使用chrome的CVE-2017-**漏洞