最近系统学了一下以前一直模糊的浏览器编码概念,顺便总结了一下XSS的原理,在此做一个小笔记
首先先介绍一下浏览器的解析概念
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段、其他元素和注释
基本元素,可以容纳文本、字符引用、其他元素和注释
HTML解析器
一个HTML解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。在解析过程中,任何时候它只要遇到一个’<’符号(后面没有跟’/‘符号)就会进入“标签开始状态(Tag open state)”。然后转变到“标签名状态(Tag name state)”,“前属性名状态(before attribute name state)”……最后进入“数据状态(Data state)”并释放当前标签的token。当解析器处于“数据状态(Data state)”时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。
协议是不能进行编码的
看到这里我相信大部分的读者都是懵逼的,所以这里我来举个例子
在解析<这个符号以前,状态是Data State
然后解析到<的时候,解析状态变为Tag open state,然后开始搜寻标签名,(在搜寻标签名的时候,我们要思考一个问题,<和标签名img并不是同一个token,他们显然是分别进行解析的,那么有没有可能忽略掉<和img之间的空格或者换行什么的?这个问题,我相信很好找到答案。)
找到标签名,状态变为Tag name state,这个状态就表示已经识别了标签名,
然后知道读取到最近的一个>时,结束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解码,这样一步一步下来我们就可以实现
这样我们就可以理解为什么下面无法弹出payload1
<script>alert('1')</script>
因为script标签内无法解析HTML实体编码,因为其不是在data state数据段内,然而下面的payload又是可以弹的1
<svg><script>alert('1')</script>
有印象的同学应该记得我们在上面说过<svg>
属于外部标签,那么我们百度一下SVG到底是一种上面样的标签
这里我们可以发现SVG属于支持XML解析,所以那么我们就很好理解了,因为下xml支持在标签内解析HTML实体字符,所以在在XML中(
会被解析成(
XSS的小技巧
说了这么多理论的东西,估计同学们都烦了,下面来说一下XSS的一般思路吧,
确定我们的输入点在哪里
打入常规的payload如
1 | <script>alert('1')</script> |
查看源代码查找我们的输出点在哪里,以及是否有过滤之类的
- 如果是单纯的字符串替换,fuzz一下常用的双写,大小写等方法
- 如果是过滤了script等标签,我们可以尝试用一些别的标签代替,比如常用的
1
2img
iframe
标签等
如果是过滤了(),我们可以使用反引号`代替(),或者使用
1
<iframe srcdoc="<script>parent.alert(1)</script>"
还可以使用data协议绕过
1
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgiMSIpOzwvc2NyaXB0Pg=="></object>
如果是过滤了<>,那么看看输出点是否在标签内一般都是通过构造事件onload,或者onclick等事件进行弹窗。
- 如果是过滤了alert,那么我们也有常用的
1
2
3confirm
throw
propmt
等
- 当然一般CTF高分的XSS都是利用一些chrome或者firefox爆出的最新的漏洞,比如上次国赛就是使用chrome的CVE-2017-**漏洞