0x00 起

最近一直在研究XSS的攻防,特别是dom xss,问题慢慢的迁移到浏览器编码解码顺序上去。

今儿被人放鸽子,无奈在KFC看了两个小时的资料,突然有种豁然开朗的感觉。

参考资料先贴出来:

  1. http://www.freebuf.com/articles/web/43285.html

  2. http://www.freebuf.com/articles/web/10121.html

  3. http://www.wooyun.org/whitehats/%E5%BF%83%E4%BC%A4%E7%9A%84%E7%98%A6%E5%AD%90

  4. 道哥:白帽子讲Web安全

  5. 编码转换工具:http://app.baidu.com/app/enter?appid=280383

0x10 几种编码

通常在XSS防御中,输出在HTML标签中或HTML属性中的情况最为常见,浏览器也只会进行HTML解码,只需要对输出进行HTML编码就可以解决XSS问题。但如果涉及输出在脚本或者URL中的时候就要进行其他方式的编码。本文的重点就是研究在复杂环境中输出变量的编码问题。

HTML编码

在呈现HTML页面时,有时候需要显示一些特殊的字符,例如<&,因为它们是HTML专用字符,需要通过一定的方式来实现,HTML编码由此诞生。HTML编码只是一种函数体现,用于将字符转换成HTML实体。例如想要显示<script>,在代码中需要写成&lt;script&gt;。为了防止XSS,至少要转换以下字符:

字符 HTML实体
< &lt;
> &gt;
' &#039;
" &quot;
& &amp;

同时字符编码也是实现HTML编码的一种形式。十进制、十六进制ASCII码或unicode字符编码,样式为“&#数值;”。例如想要显示<script>,在代码中可以写成&#x003c;script&#x003e;或者&#60;script&#62;

Javascript编码

JavascriptEncode可以采用跟HtmlEncode不同的编码方式,即使用“\”对特殊字符进行转义。也可以转换成对应的字符编码。js提供了四种字符编码的策略:

  • 三个八进制数字,如果不够个数,前面补0,例如“e”编码为“\145”
  • 两个十六进制数字,如果不够个数,前面补0,例如“e”编码为“\x65”
  • 四个十六进制数字,如果不够个数,前面补0,例如“e”编码为“\u0065”
  • 对于一些控制字符,使用特殊的C类型的转义风格(例如\n和\r)

URL编码

Url参数字符串中使用key=value键值对这样的形式来传参,键值对之间以&符号分隔,如/s?q=abc&ie=utf-8。如果你的value字符串中包含了=或者&,那么势必会造成接收Url的服务器解析错误,因此必须将引起歧义的&和=符号进行转义,也就是对其进行编码。字符编码方式为:%号后面加上字符的十六进制来替换这些有冲突的字符,例如将空格替换为%20

0x20 浏览器解析原理

浏览器在接收到一个HTML文件时,会从头开始对文档进行解析。遇到javascript时,会调用javascript解析器进行解析。遇到类似Onclick等需要触发才会执行的代码会跳过,事件被触发时才会被解析。

case 1

1
<a href="#" onclick="{$value}">天气不错</a>

浏览器解析时,onclick中的内容被当做HTML来解析。在点击链接之后,调用javascript解析器来解析$value

所以解码的顺序:HTML解码->JavaScript解码。

所以正确的防御对策为:JavaScript编码->HTML编码。

case 2

1
2
3
4
<div id="bb"></div>
<script>
  document.getElementById('bb').innerHTML="{$value}";
</script>

浏览器解析时,$value位于JavaScript中,先被Javascript解码。后$value被赋值到HTML中,进行HTML解码。

所以解码顺序为:JavaScript解码->HTML解码

所以正确的防御对策为:HTML编码->JavaScript编码

case 3

1
<td onclick="openUrl(add.do?userName='{$value}');">11</td>

浏览器解析时,$value位于Javascript中,但由于是在onclick中,所以先以HTML的身份被解码。被点击之后,先被JavaScript解码。由于$value还是URL的一部分,所以还会被URL解码。

所以解码顺序为:HTML解码->JavaScript解码->URL解码

所以正确的防御对策为:URL编码->JavaScript编码->HTML编码

0x30 补充

思路来源于:http://escape.alf.nu/3/。

html解码的问题

通过appendChild添加的节点,不会被HTML解码,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>test</body>
<script type="text/javascript">
function escape(s) {
var url = 'javascript:console.log(' + JSON.stringify(s) + ')';
console.log(url);
var a = document.createElement('a');
a.href = url;
a.setAttribute("test", "&gt;2333")
document.body.appendChild(a);
}
s = "&gt;);alert(1)//";
escape(s);
</script>

只有通过innerHTML插入页面的代码才会被HTML解析。

1
2
3
4
<script type="text/javascript">
s = '<a href="&gt;">test</a>'
document.body.innerHTML = s;
</script>

解析后为:

URL解码的问题

URL解码发生在点击链接的时候。示例如下:

在触发点击事件的时候,先进行URL解码,发现javascript伪协议之后,对内容进行JS解析执行。

0x40 End

在编码不合理的情况下的绕过方法在参考资料里写的很清楚啦,这篇文章只是整理一下思路。

这几天尝试着去挖挖漏洞好啦~