0×00 引言
很多不了解 html 、 js 编码的童鞋遇到 xss 时,都是一顿乱插,姿势对了,就能获得快感,姿势不对,就么反应。另外在 freebuf 里,有很多文章介绍过跨站编码,有兴趣的,可以搜索下。
本文介绍常见的编码方法,能力不足,如有其他意见,请指正。
0×01 常用编码
URL 编码:一个百分号和该字符的 ASCII 编码所对应的 2 位十六进制数字,例如“ / ”的 URL 编码为 %2F( 一般大写,但不强求 )
HTML 实体编码:
命名实体: 以 & 开头,分号结尾的,例如“ < ”的编码是“ < ”
字符编码: 十进制、十六进制 ASCII 码或 unicode 字符编码,样式为“ &# 数值; ” , 例如“ < ”可以编码为“ < ”和“ < ”
JS 编码: js 提供了四种字符编码的策略,
| 1、三个八进制数字,如果不够个数,前面补0,例如“e”编码为“\145” 2、两个十六进制数字,如果不够个数,前面补0,例如“e”编码为“\x65” 3、四个十六进制数字,如果不够个数,前面补0,例如“e”编码为“\u0065” 4、对于一些控制字符,使用特殊的C类型的转义风格(例如\n和\r) |
CSS 编码: 用一个反斜线 (\) 后面跟 1~6 位的十六进制数字,例如 e 可以编码为“ \65 ”或“ 65 ”或“ 00065 ”
复合编码:
所谓复合编码,也就是说输出的内容输出在多个环境中,例如
|
<span
class="tag"><<span
class="title">td</span>
<span
class="attribute">onclick</span>=<span
class="value">”openUrl(add.do?userName=’<%=value%</span>></span>’);”>11<span
class="tag"></<span
class="title">td</span>></span>
|
value 的内容首先出现在一个 URL 中,这个 URL 在一段 javascript 总,而javascript 代码又是 html 的一部分。所以解码的顺序就是 HTML 解码 –>js 解码 –>url 解码,那么正确的编码顺序就应该是 url 编码 –>js 编码 –>html 编码。
0×02 基本概念
HTML 解析器能识别在文本节点和参数值里的实体编码,并在内存里创建文档树的表现形式时,透明的对这些编码进行解码。
例如以下两种写法的功能是一样的(忽略里面的空格。。鄙视下这个编辑器):
| <span class="tag"><<span class="title">img</span> <span class="attribute">src</span>=<span class="value">"http://www.example.com"</span>></span> <span class="tag"><<span class="title">img</span> <span class="attribute">src</span>=<span class="value">"ht&# x74;p://www.example.com"</span>></span> |
而下面的两个例子,实际上却没法真的加载图片,因为这种编码干扰了标签本身的结构。
|
<span
class="tag"><<span
class="title">img</span>
<span
class="attribute">src</span>&#
<span class="attribute">x3d</span>;"<span class="attribute">http:</span>//<span class="attribute">www.example.com</span>"></span>
<span
class="tag"><<span
class="title">img</span>
<span
class="attribute">s</span>&#
<span class="attribute">x72</span>;<span class="attribute">c</span>=<span class="value">"http://www.example.com"</span>></span>
|
下面了解下浏览器解析 HTML 的步骤:浏览器收到从服务器发送来的 HTML 内容,会从头解析,当遇到 <script></script> 时,会调用 javascript 脚本解析器解析javascript ,并执行脚本,然后继续解析其他的 HTML 内容,对于一些需要触发才能执行的事件,当触发事件发送时,脚本解析器才会解析其中的脚本,在事件触发之前,它是 HTML 的一部分。
0×03 实例剖析
3.1 HTML–>js 编码
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <span class="preprocessor"><?php</span> <span class="function"><span class="keyword">function</span> <span class="title">htmlencode</span><span class="params">(<span class="variable">$str</span>)</span>{</span> <span class="keyword">if</span>(<span class="keyword">empty</span>(<span class="variable">$str</span>)) <span class="keyword">return</span>; <span class="keyword">if</span>(<span class="variable">$str</span> == <span class="string">""</span>) <span class="keyword">return</span>; <span class="variable">$str</span> = str_ireplace(<span class="string">"<"</span>,<span class="string">""</span>,<span class="variable">$str</span>); <span class="variable">$str</span> = str_ireplace(<span class="string">">"</span>,<span class="string">""</span>,<span class="variable">$str</span>); <span class="variable">$str</span> = str_ireplace(<span class="string">"script"</span>,<span class="string">""</span>,<span class="variable">$str</span>); <span class="variable">$str</span> = str_ireplace(<span class="string">"img"</span>,<span class="string">""</span>,<span class="variable">$str</span>); <span class="variable">$str</span> = str_ireplace(<span class="string">":"</span>,<span class="string">""</span>,<span class="variable">$str</span>); <span class="variable">$str</span> = str_ireplace(<span class="string">"javascript"</span>,<span class="string">""</span>,<span class="variable">$str</span>); <span class="keyword">return</span> <span class="variable">$str</span>; } <span class="keyword">if</span>(!array_key_exists (<span class="string">"name"</span>,<span class="variable">$_GET</span>) || <span class="variable">$_GET</span>[<span class="string">'name'</span>] == <span class="keyword">NULL</span> || <span class="variable">$_GET</span>[<span class="string">'name'</span>] == <span class="string">''</span>){ <span class="variable">$isempty</span> = <span class="keyword">true</span>; } <span class="keyword">else</span> { <span class="variable">$html</span> .= <span class="string">'<pre>'</span>; <span class="variable">$html</span> .= <span class="string">'<a οnclick=" '</span> .htmlencode(<span class="variable">$_GET</span>[<span class="string">'name'</span>]).<span class="string">'">click this url</a>'</span>; <span class="variable">$html</span> .= <span class="string">'</pre>'</span>; } <span class="preprocessor">?></span> <html> <script> </script> </html> |
可以看到,过滤了尖角号, script 等标签,当输入 javascript:alert(/xss/) 时,系统返回的为:
我们现在分析一下 $name 的环境, $name 先在 html 环境中,然后在javascript 环境 (onclick 事件 ) 中,浏览器解析的顺序是 html 解码 –>js 解码,所以我们将 javascript:alert(/xss/) 进行 html 编码,从而可以绕过限制 ( 当然此处代码还有其他好几种绕过的方式,此处只是简单说明编码问题 ) ,因为在 javascript 解码时, $name 已经被 html 解码了,那么处在 javascript 中的 $name 变量就是正常的 js 代码。
源代码为:
3.2 js–>html 编码
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<span
class="preprocessor"><?php</span>
<span
class="keyword">if</span>(!array_key_exists
(<span
class="string">"name"</span>,<span
class="variable">$_GET</span>)
||
<span
class="variable">$_GET</span>[<span
class="string">'name'</span>]
==
<span
class="keyword">NULL</span>
||
<span
class="variable">$_GET</span>[<span
class="string">'name'</span>]
==
<span
class="string">''</span>){
<span
class="variable">$isempty</span>
=
<span
class="keyword">true</span>;
}
<span
class="keyword">else</span>
{
<span
class="variable">$value</span>
=
<span
class="variable">$_GET</span>[<span
class="string">'name'</span>];
<span
class="variable">$html</span>
.=
<span
class="string">'<pre>'</span>;
<span
class="variable">$html</span>
.=
<span
class="string">"Your
Name is :
<div id='a'></div>
<script>
document.getElementById('a').innerHTML= "</span>.<span
class="string">"'"</span>.htmlspecialchars(<span
class="variable">$value</span>).<span
class="string">"'"</span>.<span
class="string">";
</script>
"</span>;
<span
class="variable">$html</span>
.=
<span
class="string">'</pre>'</span>;
}
<span
class="preprocessor">?></span>
|
可以看到, $value 使用了 htmlspecialchars 进行了编码, htmlspecialchars 会对 & 、 ’ 、 ” 、 < 、 > 进行 html 编码,当输入 <img src=1 οnerrοr=alert(/xss/)>时,系统不会弹框,因为特殊符号被 html 编码了。
我们现在分析下 $value 的环境, $value 先在 javascript 中,然后在 html 环境中 ( 通过 innerHTML 操作 html) ,所以浏览器解码顺序为 js 解码 –>html 解码,所以我们可以对 $value 进行 js 编码,绕过 htmlspecialchars 限制,因为 $value 在html 解码时,已经是正常的 html 代码了。
3.3 URL 编码
此外,如果 & 等符号被过滤的话,可以对其进行 URL 编码,然后测试。
0×04 Tips :
1、DOM: 只有使用合规的完整闭合的 HTML 区块对每个 innerHTML 节点进行赋值,因为这样才不会改变被重写段落之外的文档层级结构。如果格式不对,在重写发生之前输入的数据会先按照规定的语法进行强制转换。
即通过 DOM 操作 HTML 时,可以使用 <script>alert(/xss/) 来代替<script>alert(/xss/)</script> ,因为 DOM 会自动补全。
2、innerHTML 只能使用 <img src=1οnerrοr=alert(1)> 这种方式来触发 JS 。而不能以 <script>alert(1)</script> 来触发,因为这种压根不会执行 <script>..</script> 之间的内容。