前言
其实看这篇文章就差不多懂了:
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('8u0027);">Button</button>
//9.HTML字符实体编码 alert(9);
<script>alert(9);</script>
//10.Unicode 编码 alert
<script>u0061u006cu0065u0072u0074(10);</script>
//11.Unicode 编码 alert(11)
<script>u0061u006cu0065u0072u0074u0028u0031u0031u0029</script>
//12.Unicode 编码 alert 和 12
<script>u0061u006cu0065u0072u0074(u0031u0032)</script>
//13.Unicode 编码 " ' " (单引号)
<script>alert('13u0027)</script>
//14.Unicode 编码换行符(0x0A)
<script>alert('14u000a')</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>u0061u006cu0065u0072u0074(10);</script>
这里unicode出现在alert这个函数中,允许被解析并执行
控制字符中
当用Unicode转义序列来表示一个控制字符时,例如单引号、双引号、圆括号等等,它们将不会被解释成控制字符,而仅仅被解码并解析为标识符名称或者字符串常量。如果你去看ECMAScript的语法,就会发现没有一处会用Unicode转义序列来当作控制字符。例如,如果解析器正在解析一个函数调用语句,圆括号部分必须为“(”和“)”,而不能是u0028和u0029。
说的已经比较详细了,还是看个例子:
例11
//11.Unicode 编码 alert(11)
<script>u0061u006cu0065u0072u0074u0028u0031u0031u0029</script>
这里本来是alert(11),但是unicode并不能用来表示一个控制字符,如其中的(),所以不会弹窗
例13也是同样的不能用来表示单引号
//13.Unicode 编码 " ' " (单引号)
<script>alert('13u0027)</script>
而例8中有onclick这个属性,所以也会调用js解析,不过也不会用来表示单引号
//8.Unicode编码 " ' " (单引号)
<button onclick="confirm('8u0027);">Button</button>
例12看上去没有违背这个规则为什么没有弹窗
//12.Unicode 编码 alert 和 12
<script>u0061u006cu0065u0072u0074(u0031u0032)</script>
因为unicode解码之后是ASCII型的数字,需要使用单引号闭合
<script>u0061u006cu0065u0072u0074('u0031u0032')</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('8u0027);">Button</button>
//不会弹窗,属于控制符中的unicode,不能表示控制字符
//9.HTML字符实体编码 alert(9);
<script>alert(9);</script>
//不会弹窗,script块中的字符实体编码不会被解析
//10.Unicode 编码 alert
<script>u0061u006cu0065u0072u0074(10);</script>
//会弹窗,属于标识符名称中的unicode,用来表示alert
//11.Unicode 编码 alert(11)
<script>u0061u006cu0065u0072u0074u0028u0031u0031u0029</script>
//不会弹窗,同8
//12.Unicode 编码 alert 和 12
<script>u0061u006cu0065u0072u0074(u0031u0032)</script>
//不会弹窗,unicode解码后为ASCII类型,需要加引号
//13.Unicode 编码 " ' " (单引号)
<script>alert('13u0027)</script>
//不会弹窗,同8
//14.Unicode 编码换行符(0x0A)
<script>alert('14u000a')</script>
//会弹窗,因为用引号闭合