某次培训,讲到XSS中适用什么编码,大家纷纷表示比较模糊混乱。现实里也经常见XSS测试时各种编码一顿乱插,甚至很多研究文章也是只做统计,什么样可以,什么样不行,只基于测试结果做死记硬背。而下面我们就来讨论一下XSS中,到底可以适用什么编码?
首先,我们来初步了解下编译原理中词法分析和语义分析。如下
def x(y):
print y
x(5)
这是一段python代码,我们写出来的内容只是一堆字符串,python解释器会去解释这段代码。首先解释器发现了def关键字,它知道这是要定义一个函数,而后面的字符直到左括号(的字符串,都是函数名,之后是其他的参数、函数体等。这里就体现了在语义分析的核心,就是对一堆文本的数据进行解释。而同理,浏览器的作用就是分析我们定义好的HTML文本内容并渲染生成相应的样式,所以从这个角度上来说,浏览器就是一个编译器--至少它是个解释器。
我们知道,在HTML中的代码类似如下:
<img src="a.jpg" onerror="alert(1)" />
<input value="test" style="" />
<button onclick="alert(1)">Click Me!</button>
<textarea>xxxxxxxxxxxxxxxx</textarea>
<script>\x97lert(1);</script>
我们不妨换一个角度去理解,我们把每一个元素看成一个对象,即IE在遇到<符号即知道随后跟的一个单词是要定义对象(元素)的名字,直到遇到右尖括号>或者成对的元素匹配才即终止这个对象的定义。比如1我们可以看作有一个image类型的对象,它的属性(成员变量)src的值为a.jpg,同理2中定义了一个input类型的对象,它有一个属性value值为test,而3和4则分别是一个button和一个textarea对象,5是一个script类型的对象。这正是IE内部的处理。
有调试过IE的人都知道,在IE中,HTML本身的渲染是由mshtml.dll
来解释生成对象并展示出来的,而JS的代码会交给Jscript9.dll去解释执行。大家可以参考MVC结构,模型就是HTML中定义得那些元素,View就是我们看到得输入框、超链接的样子(可理解为style),这些都是在mshtml.dll中在匹配、分析、生成的,而Control就由Jscript9.DLL
来完成,所以JS引擎不过就是把生成的模型和View按照树形组织并管理起来,例如控制着body节点(对象)下有一个成员为input对象:
IE的工作流程就是,拿到HTML文本代码,然后扫描寻找左尖括号<,找到即开始按照后面的字符串来生成相应的对象,然后把之后属性的值“赋值”给对象相应的属性,这里说明一下,这个属性其实在内存里并不是一个直接的值,而还是一个对象。这里思考一下,在给属性赋值的过程中,拿到的文本字符串其实就只是串连续的字符文本,下面我们讨论一个场景,假设我想在input框的value里,有一个>符号,我应该怎么写:
<input value=te>st style="" />
而这显然是错误的,浏览器解析时发现到>已经把之后的字符串“抛弃",并不作为input对象的一部分定义。即需要定制另外一种表达标记字符的字符,为此就引入了另一只字符表示方式--实体编码。
我们来看一下日常中引起执行JS代码的场景:
1 <script>
2 on* (事件)
3 javascript: (javascript伪协议)
上面已经说过了流程,那么在执行时,mshtml.dll到底把值字符串到底以什么形式传给jscript9.dll去解释执行的呢?在IE中,所有Javascript代码编译均由jscript9!ScriptEngine::DefaultCompile函数编译,此函数断下时,esp+4的值即指向由mshtml传过来的JS代码字符串。例如HTML代码如下:
<!doctype html>
<html>
<script>
function test() {
alert(222222);
var btn = document.createElement("input");
btn.value = "xxxx";
btn.onclick = function(){x=prompt("aaa");window[x](77777);}
document.body.appendChild(btn);
alert('\x3a\/?\u0061%6a<a');
}
</script>
<body>
<a href="javascript:alert('\x3a\/?\u0061%6a<a');" >test</a>
<input value="tttttt" onclick="alert(111111111);alert('\x3a\/?\u0061%6a<a');" />
</body>
</html>
代码中使用了三种形式的触发场景,IE加载页面后,同意加载JS脚本,之后windbg附加进程下断刷新IE重新加载页面继续执行,第一次断下查看内存:
参数即以双字节(兼容汉字)的形式表示的原始javascript代码,而内容也表明是SCRIPT标签定义的js代码。很容易得出结论:对于script标签定义的JS,可以泛理解为IE会直接产生script对象,之后把innerHTML,也就是JS代码内容直接原封不动传给JS引擎去解释执行。
点击超链接test,第二次断下:
这里看到的是javascript伪协议形式触发的JS,这里看到字符串只是冒号后面的内容,这是因为这里断下的只是要被JS引擎所解释执行的JS代码。为什么会执行代码?而javascript伪协议时,javascript:这个字符串又可以怎么编码呢?据作者分析结果,在HTML生成对象并赋值href属性时,会拿到原始字符串:javascript:alert('\x3a\/?\u0061%6a& lt;a'); 由于这个属性是URL形式(IE支持的URL形式可不只是http:,还有https:、ftp:、file:),故mshtml要生成这个真正指向的链接,例如是绝对路径还是相对路径,在生成此链接地址时,会分析是否为javascript:开头的URL,即js伪协议形式,函数是mshtml!IsScriptExecutableUrl,对该函数下断:
可看到此字符串只有实体编码和url编码被成功解码,实体字符是HTML定义的(亲儿子),肯定认识,而这里是URL形式的,故在设计者角度的思想设计为解码URL。得出结论:在javascript伪协议形式,全部字符串可以是任意的URL编码和实体字符。
继续来看on事件触发JS的情形,断下后:
比较有意思的是,在事件类型中,JS代码并不是直接解释直接,而是被封装成了一个名为onclick的function,参数是event,函数体即用户代码。查看字符串,可看到只有实体字符被还原解码,其他均无处理。可得出结论:在事件触发的情况下,只有实体字符是亲儿子,只认识解码实体字符。(这里插两句题外话,作者发现字符串被拼装成函数后,就想到了flash XSS中exception被拼装的情况,测试了onclick改为“};alert(1);function x(event){”
,发现这个测试毫无意义,效果不过就是都能执行,有谁想到场景可讨论一下。再另外说明一下,这种情形下,真正引起执行的是jscript9!ScriptEngine::ParseProcedureText
函数,而JS代码被解释编译后也是作为一个函数,作为windows消息循环机制中的事件callback来触发的。)
所以,最终结论就是:在IE解析HTML时,只对所有实体编码做出解码,在解析URL时,会解码URL编码,script标签时,原始文本均不做处理!
另外,浏览器对HTML解析时,元素标签和属性标签可以有哪些编码方式(如onclick)?以及JS引擎对javascript代码会解码哪些编码?我们将在下一篇文章通过chrome或者firefox源代码来进行分析。
下面摘自网络的一段XSS姿势。我们结合分析结论做出解释
1 | <a href="javascript:%61%6c%65%72%74%28%32%29"> |
浏览器在拿到URL后,全字符串做URL解码,会得到javascript:开头的字符串 | Y |
---|---|---|---|
2 | <a href="javascript%3aalert(3)"></a> |
同上 | N |
3 | <div><img src=x onerror=alert(4)></div> |
现浏览器只认<,这里根本没有在文本里扫描到<,故根本就不是对象,不产生IMG元素 | N |
4 | <textarea><script>alert(5)</script></textarea> |
只扫描到了textarea类型,产生了textarea,其余字符串只是作为值字符串,值里含有<字符。 | N |
5 | <textarea><script>alert(6)</script></textarea> |
探测到了textarea对象的定义,故成对匹配出来中间的内容作为了属性来看待。 | N |
6 | <button onclick="confirm('7');">Button</button> |
一切值原始文本均会做出实体解码。 | Y |
---|---|---|---|
7 | <button onclick="confirm('8\u0027);">Button</button> |
事件触发情形,只解码实体编码 | N |
8 | <script>alert(9);</script> |
在script标签里,原封不动交给js引擎。 | N |
9 | <script>\u0061\u006c\u0065\u0072\u0074(10);</script> |
在script标签里,原封不动交给js引擎。之后涉及JS引擎对字符串的解码。不做详细讨论。 | Y |
10 | <script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script> |
同上 | N |
11 | <script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script> |
同9 | N |
12 | <script>alert('13\u0027)</script> |
同9 | N |
13 | <script>alert('14\u000a')</script> |
同9,稍微提一下,JS引擎解析也是查找调用(函数)的过程。不做详细讨论 | Y |
结束语: 作者认为,在一切注入类的漏洞里,均是因为数据和指令混乱在一起导致的。缓冲区溢出、SQL注入、XSS、XXE、命令注入等,都隔离不清导致的。故缓冲区溢出引入了GS和DEP,SQL注入引入了预编译,故阻止注入类漏洞最根本的方法还是数据和指令隔离。