浏览器解码与xss

简介

如今,浏览器可能已经在互联网行业占据半边江山了,它几乎是我们使用的最多的
一个软件,但是由于它的一些特性,经常会出现很多的问题,这让开发人员很是头疼,所以今天我们就站在安全的角度(解码)来深度剖析一下浏览器的工作原理。
也是对这么久以来查询的资料的一个总结。

浏览器组成


  1. 用户界面 - 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分。
  2. 浏览器引擎 - 用来查询及操作渲染引擎的接口。
  3. 渲染引擎 - 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来。
  4. 网络 - 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作。
  5. UI后端 - 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口。
  6. JS解释器 - 用来解释执行JS代码。
  7. 数据存储 - 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术
    这里写图片描述
    浏览器基本组成

    实际上我们需要关注的只有Rendering engine与Javascript Interpreter,也就是渲染引擎与js解析器。这两个部分涉及到我们后面将要提到的浏览器自解码。

注:chrome每一个tab页就是一个进程

浏览器渲染过程

这里写图片描述

一图胜千言,从图中我们可以看到浏览器解析主要是三大部分:

A.HTML/SVG/XHTML 解析,事实上,Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。

B.CSS 解析,解析CSS会产生CSS规则树。

C.Javascript DOM,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.

浏览器产生dom tree与css tree,再经过js DOM API与CSS API的修正,形成一个rendering tree,再之后经过布局,绘制形成我们所看到的网页,当然,浏览器为了用户体验,并不是先解析完html然后再去做其他事,如果这样做那么你就会先看到纯html写的页面,然后才会看到渲染后的页面,这样就太差劲儿了。
由于这部分与我们要谈的安全问题,关系不大,我们就点到为止…..

浏览器解码

在很多资料或者文章中都提到了url是第一个被解码的,那是他们考虑了浏览器请求网页的这个过程,毕竟浏览器要展示网页必须先要有网页嘛,所以从这个意义上来说url的确会被最早解码。但是这一过程并不在我们的考虑范围之内,还有些文章中提到了url都是在服务端被解码的,可能有些小伙伴看到会对这个语句产生疑惑:

<a href=javascript:a%6cert(1)>click</a>

这个明明就是在客户端做的,我在这里也解释一下,这里的服务端就是js引擎,所以上述说法也没多大问题,只是没有说清楚。
当然我们再看一个其它的关于url解码的例子,这也是在各处都被列举的一个:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<a href="javascript:alert('<?php echo $_GET['input'];?>');">test</a>
</body>
</html>

input 内容参数为: %26lt%5cu4e00%26gt
学过php的都知道,$_GET[‘input’]就是将input参数所携带的信息通过get方式传到了服务端。在服务端,看到%,按照一定的规则就行url解码,然后上面的语句就变成了:

<a href="javascript:alert('<\u4e00>');">test</a>

然后现在得到的是\u4e00这个unicode编码,这个unicode在是存在在js环境中得(前面用到了javascript伪协议),所以这个unicode会自动解码为一,所以最终点击这个链接得时候浏览器会弹窗显示:
这里写图片描述
这里顺便推荐一款在线得编码/解码器:
http://monyer.com/demo/monyerjs/
关于浏览器得解码顺序,我先基本概括为:在什么环境下就进行什么解码,解码顺序为:最外层的环境对应的编码最先解码。
例如:

<img src=1 onerror=ale&#x72;t(1)>

如你所见,这里我对r进行了html编码,然后它处在什么环境里的呢?它的最外层环境为html环境,然后最内层环境是js环境(onerror后面属于js环境),所以,按照我们之前说的,外部的html环境最先执行解码操作,也就是&#x72;会被解码为r,语句变为:

<img src=1 onerror=alert(1)>

这一下js就认识这个alert(1)了,然后就会执行弹窗。这里的弹窗产生的原因就是在js执行之前,alert就被解码了,导致js把alert当成了一个可执行的部分,而不是普通的字符。现在为了验证我们的说法,我们对r进行双重编码,先js编码,然后是html编码:
js编码后:\u72
html实体编码后:&#x5c;&#x75;&#x37;&#x32;
然后语句就是:<img src=1 onerror=ale&#x5c;&#x75;&#x37;&#x32;t(1)>
运行后得到的效果:
这里写图片描述
可以看到在浏览器中并没有弹窗,源码中可以看到r是\u72这个unicode编码的字符,而不会被当作表达式的一部分得到执行。
我们再来看另外几个例子:

a.

<a href=javascript:al\u65rt(1)>click</a>

b.

<a href=javascript:al%65rt(1)>click</a>

c.

<a href=javascript:al&#x65;rt(1)>click</a>

d.

<a href=java%61script:alert(1)>click</a>

哪些会弹窗?先自己想一下,我们下面一个个讨论。
这四个例子中,alert(1)都是处在html->url->js环境中,所以,按照我们之前说的a是不会弹窗的,因为e采用了unicode编码,html与url环境下都不能对其解码,只有在js环境才会将其解码为字符e,这是js不会认为alert(1)是可执行的,因为有一个被编码的字符e。
b会弹窗,因为在js执行之前,url解码了%65,所以到了js引擎启动时,看打了完整的alert(1),触发弹窗:
这里写图片描述
c也会弹窗,html实体解码先执行了
d是不会弹窗的,因为对a执行了url编码,所以在url识别阶段,javascript不会被认为是一个伪协议,所以当你点击链接的时候,会出现错误。。。更别提执行什么js了

代码混淆

代码混淆就是利用多种或多重进制来重写代码,从而绕过安全检测。在开始谈代码混淆之前,有必要讲讲浏览器下各种代码的都可以用哪些进制表示。
js能够认识unicode编码的字符,但是这些字符需要写作\u开头,必须要满足四位,例如字符e应该写作:\u0065
除此之外js还能识别8进制与16进制,例如e字符:八进制表示为\145,十六进制表示为\x65,这两种形式js都是认识的。
为了证明,蜗居一个例子:

<body>
    <p id='test'></p>
    <script> document.getElementById('test').innerHTML="<img src=a onerror=al\x65rt(1)>"; </script>
</html>

这里写图片描述
上面的例子之所以可以弹窗,是因为在js环境下,\x65被解码,然后通过dom操作,
<img src=a onerror=alert(1)>被写入到html中,在html环境中正确执行了。同样的,其它两种编码方式也可以这样

html支持的进制有十进制与16进制(还有html实体编码),分别写作,十进制&#101;十六进制&#x65;
css兼容html的写法,除此之外十六进制还可以这样写:\65

所以现在,结合前面提到的解码顺序问题,以及这会说的常用进制,我们基本算是完全掌握了代码混淆的方法。
设想这样一个场景,用户的输入出现在了onclick属性中,但是过滤只是简单的过滤了alert这个字符串,那么我们就可以通过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/
http://www.freebuf.com/articles/web/100675.html
https://coolshell.cn/articles/9666.html
http://taligarsiel.com/Projects/howbrowserswork1.htm
https://www.cnblogs.com/rainy-shurun/p/5603686.html

相关文章
相关标签/搜索