关于编码与解码的思考
最近做了SCTF,看了官方wp,其中看到了在进行xss测试的使用了
{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029//');}}
这时不禁想到了一个问题,为什么要进行js编码?什么时候才应该进行编码绕过?浏览器与服务器之间是如何处理编码与解码的?
常用编码
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"
复合编码:
所谓复合编码,也就是说输出的内容输出在多个环境中,例如
<td onclick="openUrl(add.do?userName='<%=value%>');">11</td>
value的内容首先出现在一个URL中,这个URL在一段javascript总,而javascript代码又是html的一部分。所以解码的顺序就是HTML解码–>js解码–>url解码,那么正确的编码顺序就应该是url编码–>js编码–>html编码。
下面的代码参考了文章:http://www.freebuf.com/articles/web/43285.html
实例
First
核心代码:
<?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>';
}
echo $html;
?>
对用户输入进行了过滤操作,过滤了:<,>,script,等,这里还要提一下<pre>
标签:
pre 元素可定义预格式化的文本。被包围在 pre 元素中的文本通常会保留空格和换行符。而文本也会呈现为等宽字体。
也就是说,原封不动应用原格式而不会进行html的预处理更改
然后考虑如何注入xss,$name存在于两层环境中,先在html中,然后在js中,所以解码的顺序是html->js,因此可以构造javascript:alert(/xss/)
,然后这里可以对整个语句进行html编码:
javascript:alert(/xss/)
或者局部编码:
javascript:alert(/xss/)
都可以得到结果:
Second
主要代码:
<?php
if(!array_key_exists ("name",$_GET) || $_GET['name'] == NULL || $_GET['name'] == ''){
$isempty = true;
} else {
$value = $_GET['name'];
$html .= '<pre>';
$html .= "Your Name is :
<div id='a'></div>
<script>
document.getElementById('a').innerHTML= "."'".htmlspecialchars($value)."'".";
</script>
";
$html .= '</pre>';
}
echo $html;
?>
当我们输入<img src=1 onerror=alert(1)>
时,可以发现源代码已将其处理为
因为htmlspecialchars
对特殊字符进行了处理,会将"<",">","&"," ' "," " "处理为实体,所以再分析$value所处的环境,因为处在<scritpt>
中,所以先是js环境,然后是html环境,所以可以对其进行js编码:
\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x31\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x61\x6c\x65\x72\x74\x28\x31\x29\x3e
得到弹窗
如何理解编码与解码过程
其实我当时看了还是有点绕,最后查了下资料,大致总结下:
DOM
首先要理解的是DOM这个东西,文档对象模型,DOM是现目前而言处理html和xml的常用方法,给个图:
当处理html文档的时候,他就会将其肢解为一个个的分支树,然后解析
浏览器基本工作流程
浏览器的构成
- 用户界面- 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分
- 浏览器引擎- 用来查询及操作渲染引擎的接口
- 渲染引擎- 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来
- 网络- 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作
- UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口
- JS解释器- 用来解释执行JS代码
- 数据存储- 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术
工作流程
处理流程
当浏览器发送http请求时,会先对特殊字符进行URL编码后,发送给服务器。
服务器收到客户端发送来的http请求,会对其进行URL解码后,再进行处理,处理完成后将结果返回给浏览器。
浏览器接收到html文件后,最先是触发html解析器来解析html,将标签转化为内容树中的DOM节点,此时在识别标签的时候,html解析器是不能识别哪些被html实体编码了,只有当整个DOM树建立起来后,才能对每个节点的内容进行识别,如果有html实体编码,再对其进行解码。
在html解析器过程中,遇到js标签诸如
<script>
会调用js解释器对js代码进行解析,而js DOM API会对DOM结构进行更改,DOM树节点的更改也会反过来触发html解释器。CSS解释器也会在html解释器过程中参与进来,但它不会干扰到DOM树,它会结合
<style>
标签和CSS文件以及html指令来构建render tree。
概括言之:
URL解析->HTML解析->css渲染->js解析
DOM树有特定语法规则,识别特定标签,这意味着,如果标签被破坏,DOM则会置之不理
回顾之前两个例子:
- 第一个场景中,js语句是在a标签中,在onclick事件触发js解释器之前,DOM树结构已经建立完成了,并且这里也没有DOM API操作,所以浏览器会先对该语句进行html解码,然后再是js解码,所以当我们对payload进行html编码后,在js解释器解析该js语句时,其已经被html解码了,是正常的js语句,所以能够正常弹窗。
- 场景二中,payload是在script标签里面,浏览器解析到这里时,会触发js解释器,js解释器会对该语句进行js解码操作,我们使用js编码的payload被还原成正常的语句。虽然此时整个DOM树已经建立起来了,但是由于DOM API存在,会再一次的调用html解释器,对我们的payload进行解析,payload里面有js标签,再一次触发js解释器,完成对js语句调用,成功弹窗。
参考链接:
http://www.mamicode.com/info-detail-1712225.html
http://xuelinf.github.io/2016/05/18/%E7%BC%96%E7%A0%81%E4%B8%8E%E8%A7%A3%E7%A0%81-%E6%B5%8F%E8%A7%88%E5%99%A8%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88/