浏览器编码与xss

0x00 引言

这两天在做xss Challenges的时候对一些绕过方法感到迷惑,原因是不太清楚浏览器编码方式和细节。所以就学了一下,顺便记记笔记。

0x01 常用编码介绍

URL编码

URL参数字符串中使用key=value键值对这样的形式来传参,键值对之间以&符号分隔,如/s?q=abc& ie=utf-8。如果你的value字符串中包含了=或者&,那么就会造成接收Url的服务器解析错误,因此必须将引起歧义的&和= 符号进行转义,也就是对其进行编码。URL的编码格式采用的是ASCII码,用一个百分号和该字符的ASCII编码所对应的2位十六进制数字表示。如“/”的URL编码为%2F。

HTML实体编码

由于HTML的编写需要使用一些特殊的字符,例如”<”因此如果想让它能够以文本形式显示,而不是被解析为标签,就需要消除歧义,因此引入了HTML字符实体,就像转义文字的功能一般。

例如:

输出字符 描述 实体名称 实体编号
< 小于号 &lt; &#60;
> 大于号 &gt; &#62;
& 和号 &amp; &#38;
引号 &quot; &#34;

字符编码

十进制、十六进制ASCII码或unicode字符编码,样式为“&#数值;”,例如“<”可以编码为“&#060;”和“&#x3c;

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”

0x02 HTML解析器

HTML解析器的工作是将html标识解析为解析树。

输入

因为 HTML 语法是由 W3C 组织创建的规范中进行定义的,而且语法格式是由 DTD (Document Type Definition)定义的,该格式中定义了语言中允许的元素、属性和层次结构,适用一切的 SGML (Standard Gerneralized Markup Languge)族的语言。为了在发展的进程中向后兼容老版本的内容,DTD 存在两种模式,严格模式完全遵守 HTML 规范,其他模式支持老的浏览器使用的编辑。

解析算法

因为 HTML 的包容性,以及在解析过程中存在脚本会改变 HTML 文档(如 document.write),导致无法使用自上而下或是自下而上的解析器进行解析。

解析过程前半段是词法分析,也就是标记化(tokenization),整体算法的核心就是状态机的改变(就是解析过程中有一个标识当前状态应是解析到哪一个阶段了)。它从输入流中获取字符并按照转换规则转换到另一种状态。

同时构建 DOM 树,也就是树构建(tree construction)过程,该过程就是我们在「解析-理论剖析」讲述的一样,将对应的标记去击中语法,然后添加到 DOM 树上。此过程中也有一个状态机去维护对应的阶段。

DOM树

输出的树,也就是解析树。DOM是文档对象模型 (Document Object Model) 的缩写。它是 HTML 文档的映射关系和存留 HTML 元素对外的接口(例如 JavaScript),每一个节点是由 DOM 元素和节点属性组成。

DOM 与标记之间几乎是一一对应的关系,例如:

<html>
<body>
<p>
Hello World
</p>
<div> <img src="example.png"/></div>
</body>
</html>

可翻译为以下DOM树:

标记化算法

直接讲起来还是比较抽象,举个例子。

<html>
<body>
Hello world
</body>
</html>

1.初始状态是data state。当遇到字符”<”时,状态更改为Tag open state。这个状态会一直保持到接收 “>” 字符。

2.遇到 > 标记时,会发送当前的标记,状态改回“data state”。接收到 Hello world 中的 H 字符时,将创建并发送字符标记,直到接收到</body>中的<

3.然后回到Tag open state。接收下一个输入字符 / 时,会创建 end tag token 并改为“标记名称状态”。这时会再次保持这个状态,直到接收 >。然后将发送新的标记,并回到data state

HTML字符实体解析

当HTML 解析器处于数据状态(DataState)、RCDATA 状态(<textarea><title>)、属性值状态(Attribute Value State)时,字符实体会被解码为对应的字符。

例如:

<div>&#60;img src=x onerror=alert(4)&#62;</div>

&#60;&#62;分别是<>的实体编码,当浏览器解析完<div>标签后,状态会更改为Data State,所以接着解析到实体&#60;时因为处在Data State就会对实体进行解码为<,&#62;同样道理被解码为>。

尽管被解码成”<>”,但里面的代码还是不会被当作js代码执行,因为解析器在解析实体字符后不会在转换为Tag open state,因此就只能当作数据来处理,这就是我疑惑了很久的为什么能通过实体编码来避免xss的原因。

0x03 浏览器解码顺序

复合编码

复合编码,也就是输出的内容输出在多个环境中,例如:

<td onclick=”openUrl(add.do?userName=’<%=value%>’);”>111</td>

value的内容首先出现在一个URL中,这个URL在一段javascript中,而javascript代码又是html的一部分。所以解码的顺序就是HTML解码–>js解码–>URL解码。

js标签中的解码

由于处理器在建立好DOM语法树之后浏览器才开始进行一个url或者js解码。因此<script>xxx</script>中的实体编码是不会被解码执行的。

如:

<script>alert&#40;'1')</script>

按上文所说,这句js代码是不能造成xss的。但如果是<svg><script>alert&#40;1)</script>就可以。这是因为<svg>属于外部标签,支持XML解析。而XML支持在标签内解析HTML实体字符,所以在在XML中&#40; 会被解析成(。

参数值解码

HTML解析器能识别参数值里的实体编码,并在内存里创建文档树的表现形式时,透明的对这些编码进行解码。

例如以下两种写法的功能是一样的:

<img src="http://www.example.com">
<img src="ht&# x74;p&#x3a;//www.example.com">

但以下两种却是不一样的:

<img src&# x3d;"http://www.example.com">
<img s&# x72;c="http://www.example.com">

因为这种不是在属性值中进行编码的,这种编码干扰了标签本身的结构,所以无法加载图片。

实际应用

以FreeBuf上的一篇文章为例。

代码如下:

<?php

function htmlencode($str){
if(empty($str)) return;
if($str == "") return;

$str = str_ireplace("<","",$str);
$str = str_ireplace(">","",$str);
$str = str_ireplace("script","",$str);
$str = str_ireplace("img","",$str);
$str = str_ireplace(":","",$str);
$str = str_ireplace("javascript","",$str);

return $str;
}

if(!array_key_exists ("name",$_GET) || $_GET['name'] == NULL || $_GET['name'] == ''){

$isempty = true;

} else {

$html .= '<pre>';
$html .= '<a onclick=" ' .htmlencode($_GET['name']).'">click this url</a>';
$html .= '</pre>';

}
?>

<html>
<script>

</script>
</html>

这里过滤了<,<script>,<img>等,因此当输入javascript:alert(\xss\)时,响应为:

<pre><a onclick=" javaalert(\xss\)>click this url</a></pre>"

我们分析$name的环境,这个参数首先使用了htmlencode,然后出现在了一个onclick事件,即javascript中,这个javascript又在一个html中。 所以解码顺序是HTML解码->js解码。

经过实验后发现,可以将name变量进行js编码和html编码来绕过。

参考文章:

https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#Parsing_general

https://www.freebuf.com/articles/web/43285.html

Author: Neorah
Link: https://neorah.me/pentest/%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BC%96%E7%A0%81%E4%B8%8Exss/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.