看雪论坛
发新帖
6

web前端安全基础与攻防(续篇)

shayi 2015-4-21 10:02 3324
继续前面的内容,由于转发到一些论坛时,限制了每个帖子中,添加图片链接的数量,而前篇包含的图片数量已经达到上限,因此开一个新帖继续讨论。

还记得在前篇中有一段针对使用 IE 浏览器用户的攻击载荷,如下所示 :


<script>function XSS(){
        a = new ActiveXObject('Microsoft.XMLHTTP');
        a.open('get', 'http://www.baidu.com', false);
        a.send();
        b = a.responseText;
        document.write(b);
        }
        XSS();
</script>


上面代码中,使用 IE 实现的XHR(XMLHttpRequest)对象访问百度首页并且通过javascript读取返回的数据(responseText),并且可以写入当前页面。

这是因为 IE 的同源策略没有很好地对XMLHttpRequest进行约束,例如,上述代码可能位于本地硬盘的一个HTML文本中(C:\Users\shayi\Desktop\XssPayloadTest.html

我们已经在上一篇博文中看到,IE 默认允许XHR跨域加载并读写资源;对于其它浏览器(FireFox 与 chrome)而言,本地文件系统路径与“百度首页”是不同源的,因此它们会限制当前HTML文本所在源中的javascript读写从百度首页返回的数据,换言之,这两个浏览器的同源策略默认仅允许XHR加载,读写相同源中的数据,除非对浏览器以及目标站点的web服务器配置为启用HTML5规范中引入的“跨域资源共享”(CORS)。

下面的示例代码,通过使用非 IE 浏览器支持的XHR对象,尝试跨域加载资源并写入至当前页面DOM中的一个节点:

(引用自《白帽子讲web安全一书》,略作修改)


<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-type" content="text/html;charset=utf-8" />
        <title>XssPayloadTest</title>
        <script type = "text/javascript">
        var xmlhttp;
        function LoadXMLDoc(url)
        {
            xmlhttp = null;
            if (window.XMLHttpRequest)
            {
                xmlhttp = new XMLHttpRequest();
            }
            else if (window.ActiveXObject)
            {
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            if (xmlhttp != null)
            {
                xmlhttp.onreadystatechange = state_Change();
                xmlhttp.open("GET", url, true);
                xmlhttp.send(null);
                 
            }
            else
            {
                alert("your browser does not support XMLHTTP");
            }
        }
 
        function state_Change()
        {
            if (xmlhttp.readyState == 4)
            {
                if (xmlhttp.status == 200)
                {
                    document.getElementById('T1').innerHTML = xmlhttp.responseText;
                }
                else
                {
                    alert("problem retrieving data:" + xmlhttp.statusText);
                }
     
            }
        }        
        </script>
     
    </head>
    <body onload = "LoadXMLDoc('http://bbs.pediy.com/index.php')">
    <div id = "T1" style = "border:1px solid black;height:40;width:300;padding:5"></div><br />
    <button onclick = "LoadXMLDoc('http://shayi1983.blog.51cto.com')">Click</button>
    </body>
</html>


这个在HTML文档头部(head元素内)引入的javascript定义了两个函数: LoadXMLDoc()通过浏览器支持的XHR类型来跨域发起HTTP GET请求并加载资源;

state_Change()检查对方web服务器返回的HTTP响应状态码,然后决定是读取响应数据的内容并写入当前页面(状态码为200);还是给出服务器端返回的错误信息(除200以外的其它状态码)。

在HTML文档体(body元素内),通过实际调用LoadXMLDoc()来对看雪论坛首页发起跨域请求(注意,当前“源”是本地文件系统上的测试用HTML页面),然后尝试将对方返回的“响应文本”(responseText)写入当前页面DOM的T1节点的内部HTML文本中,并且在文档体中添加一个按钮,用户点击时将再次通过XHR对象,发起对我的51cto博客页面的跨域请求。

上面代码在FireFox中的测试结果如下所示:




最后要指出一点,浏览器一般通过URL中的协议,主机名与端口号来判断一个页面文档所属的域,而在一个新打开的页面的URL中,只要3者之一与前面那个页面的URL不同,都会被浏览器的同源策略认定为属于不同域,从而阻止新开启页面上的脚本通过前者页面的URL来发起跨域请求,而符合HTML5 CORS 规范的XHR 发起的跨域请求则不在此限,当然前提是需要经过目标域(即要“跨”的域)中的服务器许可,如果目标域的web服务器在返回的 Access-Control-Allow-Origin 响应头中指定的值与发起跨域XHR所属的源始域相同,那么浏览器将允许这个XHR跨域请求。

为了更清楚说明XHR询问是否可以CORS的场景,假设一个HTML页面所属域为

http://www.webSite.com(即该页面通过这个URL加载),在该页面中,有一段生成跨域HXR的  javascript 代码如下:


var xhr = new XMLHttpRequest();
xhr.open("PUT", "http://www.friendlySite.com/index.html", true);
xhr.send();



xhr.open() 的第三个参数为 true,表明请求为异步发送,这是默认值,有些浏览器为了性能上的考量,甚至拒绝将该参数设置为 false(同步),如果你这么做了, Firebug 将会给出类似下面的提示:




遇到 xhr.send() 语句时,浏览器会向 www.friendlySite.com  发起一个HTTP 请求,用于“证实”www.friendlySite.com 是否愿意与 http://www.webSite.com 共享资源(通过后者打开的页面文档),请求头如下:

OPTIONS http://www.friendlySite.com/index.html HTTP/1.1
Host: www.friendlySite.com 
User-Agent: Mozilla/5.0(Macintosh; Intel Mac OS X 10.7; rv:11.0) Gecko/20150101 Firefox/37.0
Accept: text/html,application/xhtml+xml,applicatiin/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.5
Origin: http://www.webSite.com 
Accept-Control-Request-Method: PUT


以上HTTP请求中(这个包含 CORS 请求头的 HTTP 请求又称为“Preflight”请求),注意 OriginAccept-Control-Request-Method 这2个头部,源始文档所属域为 www.webSite.com  ,因此 Origin 头部就是这个值;源始文档中的XHR 对象请求的方法为 PUT,因此 Accept-Control-Request-Method 头部就是该值。还要注意浏览器使用的是 HTTP OPTIONS 方法,该方法在最初的HTTP协议规范中,用于查询web服务器支持的 HTTP 方法类型,现在则演变为用于查询对方的 CORS“许可策略”。

如果目标域 www.friendly.com  允许 CORS ,其返回的HTTP 响应头应该如下所示:


HTTP/1.1 200 OK
Date: Wed, 13 Apr 2015 06:51:53 GMT
Server: Tengine
Access-Control-Allow-Origin: http://www.webSite.com
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 10
Content-Length: 0


以上HTTP响应中,Access-Control-Allow-Origin 响应头的值与发出跨域请求的XHR 对象所在页面文档所属的域是同一个,表明目标域允许与 www.webSite.com

的 CORS ,如此一来,浏览器将允许 www.webSite.com 域页面中的XHR对象和后续的 javascript 代码读取,操纵和显示来自 www.friendly.com 域返回的数据,例如读写 xhr.responseText 的内容时,才不会发生错误。
另外,Access-Control-Max-Age 响应头的含义为,该响应的有效期,以秒为单位,例如其值为 3600 时,浏览器在一小时内对目标域的每次跨域访问,都不需要使用 Preflight 请求,这就减少在请求头中增加多余的头部造成的客户端与服务器端处理负担,以及链路带宽的浪费(每次少传输几十到上百字节总是好的);但是一小时后,浏览器必须再次发送 Preflight 请求至服务器,以更新目标域的 CORS 许可策略,一般而言,目标域为了其自身数据的安全考量,Access-Control-Max-Age 的值不会设的太长,但是像上例中的10秒那么短也不太实际,各位可以自行访问其它互联网上的站点,通过工具来测试这个值。

Access-Control-Allow-Credentials
请求头的值如果为 true,那么 www.webSite.com 域页面中的 XHR 对象以及 javascript 脚本代码,将能够读写

由目标域 www.friendly.com 设置的 cookie,HTTP 基本认证字串(base-64 格式的用户名与密码,即目标域赋予的用户会话令牌),摘要认证字串(MD5 或其它摘要算法加密的用户名与密码),以及由目标域 www.friendly.com 设置的客户端 SSL 证书(假设目标域是电子商务网站或网上银行,需要验证企业用户的身份,就有可能用到客户端 SSL 证书),以上这些证书和认证信息,以及 cookie 形式的会话令牌,都将能够被 www.webSite.com 域页面中的 XHR 对象以及 javascript 脚本代码读写访问;一般而言,如果目标域源始域之间没有很好的友情和信任关系,Access-Control-Allow-Credentials 响应头的值应该为 false 会比较安全。 

(作为对比,前面的本地文件系统上的测试HTML页面中的 javascript 代码无法将来自看雪以及51cto域的 xhr.responseText 写入至测试页面,就是因为浏览器没有收到来自看雪以及51cto域允许与“本地域”进行CORS的响应头。)


《关于HTML文档的两种解析模式:HTML 与 XHTML 之间的差别》

浏览器根据HTML文档起始处的“<!DOCTYPE xxxx>”来判断应该使用何种解析模式,当xxxx的值为“html”时,表示为HTML解析模式,该模式对文档的语法要求比较宽松,浏览器的HTML解析引擎会容许并且自动修正绝大多数的语法错误,例如下面这个例子:


<!DOCTYPE html>
<htMl lang="en">
    <head>
        <meta http-equiv="Content-type" content="text/html;charset=utf-8" />
        <title>XssPayloadTest</title>
    </head>
     
    <body>
    ' " `
    <a href = http://www.baidu.com>百度首页。。。
    </oops>
    <IMG src = #>
</hTml>


可以看到,上述文档的HTML标签大小写混用;body元素与a元素没有闭合;

a元素的href属性的值(百度首页的URL)没有用双引号包含;存在一个没有起始(标签)的oops元素;img元素使用全大写而且没有闭合。。。。这些语法错误都可以在浏览器中得到修正,前提是使用HTML解析模式,以 IE 为例,打开上述文档,IE 会在其进程地址空间中构建DOM节点时,修正错误,缺少的元素:









另一方面,XHTML(衍生自XML)解析模式则非常严格,文档中存在上述任意一种类型的错误都会导致浏览器无法正常显示该文档,或者给出错误提示。

再者,采用HTML解析模式,遇到“<script>”,“<style>”,“<textarea>”,“<xmp>”等元素标签,都会致使浏览器切换到特殊解析模式,例如碰到script标签,调用javascript解析引擎;碰到style标签调用CSS解析器等等。

而在XML类文档中(包括XHTML),这些标签内部还需要嵌套一个前后不对称格式的数据标签,如下所示:


<script type="text/javascript">
<![CDATA[
 //将此处注释掉的内容改成javascript代码,其它类型的特殊解析模式标签,以此类推
]]>
</script>


上例中,“<![CDATA[”“]]>”字符串标签之间才是真正添加javascript代码的位置。

例如,BurpSuite Proxy 模块拦截到的从“博文视点”站点首页,返回的HTTP响应体的HTML文档中,就包含了对“<![CDATA[]]>”的使用:




HTML 与 XHTML 解析模式差异总结:

使用XHTML解析时,仅遇到 script 或 style 标签,还无法切换进入到对应的解析模式(调用 javascript 或 css 解析引擎),而是必须添加“<![CDATA[....]]>”数据标签,并且将脚本代码或样式表放在其中以实心句点表示的位置处,才能正常工作。


对于下面这个没有按照规范编写的img元素:

<img src= "http://www.baidu.com"  title=""onerror="alert('xss')" class=examples>


一共有4个属性:src,title,onerror,class。其中class属性的值没有使用双引号包含,三种浏览器各自使用不同的方式解析这个img元素:







从上面可以看出,只要是元素属性的值,无论是哪一种浏览器,在解析的时候都会自动向其添加双引号。另外,onerror 属性的值为一个javascript语句,运行结果是弹出提示框。

如果将包含javascript语句的双引号改成“反引号”(`),则三种浏览器在解析时,都会自动在反引号外侧再添加双引号,换言之,无论img元素属性的值为何,总是使用双引号包含,而这会导致添加反引号的javascript代码无法执行:




这里还有一个很重要的知识点:一般而言,浏览器在解析img元素的任意属性值时,无论原始文档中是否将属性值用双引号包含,浏览器都会自动将其包含。

但是,如果属性的值为以左尖括号开始的元素,例如script,并且原始文档中没有将其用双引号包含,那么浏览器在解析的时候会产生混乱(对于三大浏览器而言都一样),虽然这样并不会导致执行script标签中的javascript代码,但是浏览器会给出一些错误提示(IE),或者会干扰到对img元素其它属性值的正常解析(FireFox 与 chrome),例如原始文档中的代码:


<img src = "#" title = <script>alert('xss in value of element properties');</script> class = examples>


其中title的属性值为以左尖括号起始的script标签,没有使用双引号包含,那么浏览器们会如何解析呢:











从上面的解析结果还可以发现一个事实,那就是浏览器在解析时会自动忽略(不处理)分隔属性与属性值的等号(=)两侧的空格字符。在原始文档中,img元素的title属性后先接上一个空格符,然后依序是等号,第二个空格符,左尖括号。。。。而在浏览器解析时忽略了两侧的等号。

(img元素与第一个属性之间的空格符不会被省略,而且是必须的)




《浏览器如何解析HTML文档中,错误的元素标签嵌套》

以下面代码为例,其在body元素内部存在错误的标签嵌套:


<!DOCTYPE html>
<html>
    <head>
    <meta http-equiv="Content-type" content="text/html;charset=utf-8" />
        <title>XssPayloadTest</title>
    </head>
    <body>
        <i <b>xss
    </body>
</html>








通过前面的例子可知,浏览器不支持在元素属性名中使用特殊字符,那么,浏览器是否支持在元素名称中使用特殊字符,例如左尖括号以及等号呢?考虑下面这段代码:

<!DOCTYPE html>
<html>
    <head>
    <meta http-equiv="Content-type" content="text/html;charset=utf-8" />
        <title>XssPayloadTest</title>
    </head>
    <body>
        <i<"<b>">xss
    </body>
</html>


在body元素内部,试图将第二个左尖括号开始的字符串作为 i 元素名称的一部分,

来看看浏览器们会如何解析这个文档:






IE 中的情况与上述两者类似,这里就不截图说明了。大家可以自行尝试向 i 元素名称中添加等号(=),浏览器也会将其当成是元素名称的组成字符,从某种意义上而言,这种解析上的缺陷不得不视为当前版本的一个安全隐患。

在chrome的解析截图中,看到了将特殊字符进行HTML实体编码的用法,其实,

浏览器的HTML解析引擎应该都能识别在HTML文档的文本节点元素属性值内,以HTML实体编码的字符序列,并将其解码,还原成可打印的ASCII字符。

这就是chrome将文本节点中的左尖括号编码的原因——它可以解码成明文字符。

这里隐含了一条重要的安全规则,浏览器在解析时应该遵守,否则就有可能在客户端产生xss漏洞,假设经由服务器端返回的HTML文档中,已经对文本节点与元素属性值进行了HTML实体编码(或者10进制,16进制编码),意味着这些被编码的字符是具有危害性的(一般而言,普通字符不需要编码)

那么浏览器就不能将解码后的危害性字符序列(如“<script>”)当成指令执行,只能作为数据或字符串显示,或者索性不解码,在HTML文档中显示编码字符。


因为服务器端的xss过滤器已经意识到,危害性字符出现在文本节点与元素属性值中,可能导致xss漏洞,因此将其编码,而浏览器能做的就是,仅显示解码后的内容,而不是执行,或者保持原样输出。

为了更好地说明其中的微妙之处,来看一看下面的几个实例:


[CODE]<!DOCTYPE html>
<html>
    <head>
    <meta http-equiv="Content-type" content="text/html;charset=utf-8" />
        <title>XssPayloadTest</title>
    </head>
    <body>→
本主题帖已收到 0 次赞赏,累计¥0.00
最新回复 (14)
合天智汇 2015-4-21 12:14
2
写的不错,有没有兴趣把这个教程作成实验啊,我们的实验平台是征集实验的,帮助更多人嘛。
lookzo 2015-4-21 12:35
3
非常不错,支持楼主
6
shayi 2015-4-21 13:19
4
什么样的实验?你们是互联网公司还是安全公司
合天智汇 2015-4-22 08:19
5
我们是做网络安全人才培训的互联网公司
我们的实验形式请参考http://erange.heetian.com/  里面有很多类型的实验了。
希望能有合作。
6
shayi 2015-4-24 09:33
6
补充内容

《页面使用 UTF-7 字符编码导致的 XSS 漏洞详解》

很多安全类书籍都介绍到 UTF-7 字符编码会导致 XSS 漏洞,但是部分内容各执一词,且描述不清楚,这里总结如下:

UTF-7 编码是 unicode 编码的一类子集,后者包含的其它编码类型有 UTF-8,UTF-16LE(小端法,将代表编码字符的最低有效字节放在前面),UTF-16BE(大端法,将代表编码字符的最高有效字节放在前面),UTF-32LE,UTF-32BE 等等。在 UTF-7 编码方案中,左尖括号被编码成“+ADW-”;右尖括号被编码成“+AD4-”,其中的加号与减号分别标识着 UTF-7 编码序列的开始与结束;如果服务器端的 XSS 过滤器没有过滤这两个UTF-7 编码格式的左右尖括号,(或没有将其编码成其它无害形式的字符),并且客户端浏览器的版本过于老旧(例如 IE 7),那么当被注入攻击载荷的页面返回至客户端时,浏览器将解码 script 标签前后的 +ADW- +AD4- 字符,还原成明文的左右尖括号,从而执行 script 标签内部的恶意代码:

<!DOCTYPE html>
<html>
    <head>
    <meta http-equiv="Content-type" content="text/html;charset=UTF-7" />
        <title>XssPayloadTest</title>
    </head>
    <body>
        +ADW-script+AD4-alert("xss")+ADW-/script+AD4-
    </body>
</html>


以上文档中,web 服务器(愚蠢到)在 meta 元素中显式指定字符集为 UTF-7,如果浏览器同样愚蠢(例如 IE 7以下版本,美其名曰是为了保持向后兼容)遵循这个设置字符集的指令,就会解码任何 UTF-7 字符,最终导致弹出提示框。需要补充说明的是,多数现代浏览器均已经不支持 UTF-7 了,因而上述文档在最新版本的三大浏览器中测试,都不会弹出提示框;另外,实际的触发环境和条件更为复杂,考虑如下场景中,假设用户的浏览器是 IE 6/7 :

1。如果 web 服务器在返回的 HTTP Content-Type 响应头中,没有明确设置字符集编码,并且响应体中的 HTML 文档中的 meta 元素内,也没有设置字符集编码,那么浏览器在碰到任何 UTF-7 字符时,都会尝试确定其编码方案,并且解码还原成明文字符,以上述文档为例,就会弹出提示框;

2。如果 web 服务器在返回的 HTTP Content-Type 响应头中,明确设置字符集编码为 UTF-8,并且响应体中的 HTML 文档中的 meta 元素内设置的字符编码为 UTF-7,那么浏览器最终将按照响应头中的设置(响应头的优先级比响应体中 meta 标签的优先级高 ),以上述文档为例,会弹出提示框(因为采用 UTF-8 字符编码时,浏览器无法识别,解码 +ADW-+AD4- 字符);

3。如果 web 服务器在返回的 HTTP Content-Type 响应头中,设置字符集编码为 UTF-7,并且响应体中的 HTML 文档中的 meta 元素内设置的字符编码为 UTF-8,

根据浏览器的采用优先级原则,上述文档将会弹出提示框;

总结,应对 UTF-7 XSS攻击,从最终用户防御的角度来看,应该确保浏览器总是处在当前的最新版本状态;从 web 站点防御的角度来看,应该明确在响应头与响应体的 HTML 文档中,设置字符编码为 UTF-8 或者其它安全的编码方案;

对于每个包含HTML内容的响应,web应用需要在其中包含 Content-type 头部,并且用“charset=”指令设置一个标准的,(浏览器)可辨识的字符集,
例如:

Content-Type: text/html; charset=utf-8

或者:
Content-Type: text/html; charset=ISO-8859-1
(如前所述,还要确保响应中的所有可能位置指定了相同的字符集)

并且过滤掉任何 UTF-7 危险字符,如果没有把握通过编程屏蔽所有危害字符,则可以模仿浏览器的 HTML 解析引擎将用户输入的内容在内存中保留一份副本,将其渲染成 HTML 页面,在渲染结果中查找任何明文的危害字符,对其进行编码过滤,然后再次渲染,再过滤,直到完全清除干净后,才可以把最终的文档返回给客户端。

(这也是许多优秀的服务器端 XSS 过滤器,WAF 采用的工作模式)


sushuangju 2015-4-25 22:20
7
Mark
6
shayi 2015-4-26 12:18
8
补充内容

HTML 文档的跨平台支持:根据不同的浏览器类型,执行不同的代码块

由于各种浏览器对于新的 HTML5 规范,以及 DOM 的支持,实现程度不一致,为了保证自己所编写的文档,以及其中的脚本代码能够在所有主流浏览器中正确运行,一种解决办法是:先判断浏览器类型,然后执行相应浏览器支持的代码块。实现这个功能的关键,在于浏览器的用户代理字串值,即 navigator.userAgent

实现逻辑如下:


<!DOCTYPE html>
<html>
    <head>
    <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
        <title>XssPayloadTest</title>
    </head>
    <body>
        <script>
            document.write(navigator.userAgent);
            document.write("<br><br>");
            var isIE = navigator.userAgent.indexOf('Trident') > 0;
            var isChrome = navigator.userAgent.indexOf('Chrome') > 0;
            var isFirefox = navigator.userAgent.indexOf('Firefox') > 0;
            var isOpera = navigator.userAgent.indexOf('OPR') > 0;
            if (isIE){
                document.write('<b>您使用的是IE浏览器</b>');
            }else if (isChrome){
                if (isOpera){
                    document.write('<b>您使用的是Opera浏览器</b>');
                }else{
                    document.write('<b>您使用的是Chrome浏览器</b>');
                }
            }else if (isFirefox){
                document.write('<b>您使用的是Firefox浏览器</b>');
            }else{
                document.write('<b>您使用的是其它浏览器</b>');
            }
        </script>
 
    </body>
</html>


这是一个非常简单的先判断然后输出用户使用的浏览器类型的页面。

第8行直接在当前页面输出打开该页面的浏览器的用户代理字串;第11~14行分别查找各种浏览器的用户代理字串中,能够唯一识别浏览器类型的关键词,这是由于,在早期的“浏览器世界大战”中,许多 web 页面的特定内容仅支持 Netscape Navigator 浏览器(网景公司的产品,其 “Mozilla” 字串就是它首创的,含义为“Mosaic Killer”,即浏览器鼻祖 Mosaic 的杀手),而其它品牌浏览器为了提高自身市场份额与竞争力,也在其用户代理中添加了“Mozilla”字串,用于告诉目标站点:“我是与 Mozilla 兼容的浏览器”,如此一来,站点就会返回原本仅响应给 Mozilla 浏览器的页面内容,而这个内容通常是比较丰富的;

最终效果就是, IE 浏览器的用户访问某站点获得的内容与 Netscape Navigator 浏览器的用户获得的内容一致,因为 IE 在它的用户代理字串中,添加了“Mozilla”字串。

其它浏览器相继模仿这个做法,导致通过“Mozilla”字串根本无法实际识别浏览器类型,所以,必须找到用户代理字串中,每个浏览器都不一样的关键词,这就是第11~14行代码的工作,例如第11行查找用户代理字串中的“Trident”,后者是 IE 浏览器的 HTML 渲染引擎内核代号,然后保存在一个变量中,第15~16行代码通过判断这个变量的值来输出用户使用的是 IE 浏览器(在实际开发时,应该把这里的内容替换成仅 IE 浏览器支持的页面代码,其它类型浏览器以此类推)。

另一方面,当前的 chrome 与 Opera 浏览器的用户代理字串中,都包含了“Chrome”关键词,所以仅凭它无法区分这2个浏览器,需要检查在包含“Chrome”关键词的用户代理字串中,是否含有“OPR”字串,如果有,则能识别出 Opera 浏览器,反之,则为 chrome 浏览器;而这就是第17~22行的嵌套 if-else 语句的工作。顺便提一下,这2个浏览器的 HTML 解析引擎内核都源于苹果公司开发 Safari 浏览器时的内核 WebKit;google 公司在 WebKit 的基础上开发出新的内核称为 Blink;这2个浏览器厂商“饮水思源”的做法导致你在使用这2个浏览器打开上述文档时,看到了各自的用户代理字串都包含“AppleWebKit”:











我在第25~27行就停止了判断浏览器类型,你也可以继续添加判断其它浏览器的 else if 语句块,例如 Safari,以及国产的 360 浏览器,这需要预先找出能唯一标识它们的用户代理字串片断,就如第11~14行代码所做的一样,各位可以自行测试。
hoyer 2015-4-27 15:52
9
楼主的注释很详尽   赞一个
6
shayi 2015-4-28 11:10
10
补充内容

《存储型 XSS 示例》

一些社交网站如 twitter 提供的站内短信功能,在其中会通过 a 标签的 href 属性值进行短信链接的导航,这个属性值是用户能够控制的输入数据,所以 twitter 对其进行了严格输入检查,编码,过滤等操作,主要是过滤尖括号以及 script,embed,object 等危害性元素。

假设用于生成短信链接的输入框的初始 HTML 代码如下:


<a href="></a>


在上面场景中,攻击者想方设法要绕过 twitter 服务器的 XSS 过滤器,最终目的是让输入的攻击载荷存储在由服务器的 web 应用维护的,某个能够公开访问的“页面”(类似新浪微博的“广场”功能),其它用户访问这个页面时,XSS 漏洞在用户浏览器中触发,执行恶意脚本代码。由于上面提到的过滤规则,插入尖括号的方法已经不再适用,必须寻找新的方法来绕过,例如下面这个攻击载荷使用了众多web 前端黑客技术:

1。双引号元素属性闭合;

2。 onmouseover 事件结合 eval 函数执行任意代码;

3。对 URL 进行 UTF-16 编码绕过 XSS 防火墙;

4。利用社会工程学攻击来引诱用户点击链接;


http://t.co@" style="font-size:99px;" onmouseover="eval(location.href='http:\u002f\u002fwww.baidu.com\u002f')" class/


其中,邮件符号 @ 后面的第一个双引号用于闭合原始 HTML 代码中, 等号后面的第一个双引号,于是攻击者可以在后面插入自定义内容;style属性将字体增大到 99 像素,保证受害者能够“准确地”点击到具诱惑性的恶意链接; onmouseover 属性代表的事件在用户将鼠标指针移动到该元素(a 元素)范围内时触发,事件触发后,执行自定义代码,将当前文档的 URL 重定向到百度首页(这里经过我净化,实际的攻击载荷中,其 URL 肯定是由攻击者控制的站点上的恶意 .js 文件);需要注意,考虑到 twitter 的 XSS 防火墙可能会过滤以“http://”起始的字符串,因此使用 UTF-16 编码(\u002f)对恶意 URL 中的正斜杠(/)进行转义,这样就能绕过服务器的 XSS 防火墙(只要不是精确匹配,输入字串就能通过检查),关键在于,转义后的字符序列在作为 HTTP 的响应体内容返回至客户端后,浏览器会解码,还原成正斜杠,导致最终访问该 URL ;另外, onmouseover 属性的值必须是能够执行的函数或者代码快,这就是调用 eval 函数的目的;最后,使用 class 后面的正斜杠来注释掉原始 HTML 代码中的第二个双引号,避免浏览器因为语法错误而无法执行脚本。

为了增强链接的视觉诱惑性,我在 a 元素的 innerHTML 中,添加了名为“邀请码发放页面”的文本节点(请勿在论坛中尝试!),最终的注入效果(即存储到服务器上的漏洞页面)如下所示:


<!DOCTYPE html>
<html>
    <head>
    <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
        <title>XssPayloadTest</title>
    </head>
    <body >
        <a href="http://t.co@" style="font-size:99px;" onmouseover="eval(location.href='http:\u002f\u002fwww.baidu.com\u002f')" class/">邀请码发放页面</a>
    </body>
</html>


使用当前任意类型的最新版浏览器打开上面 HTML 文件,就会重定向到百度首页,这就完整的模拟出攻击载荷成功绕过,存储到服务器上,并且返回给客户端后,用户浏览器成功执行代码的场景,截图如下:

Sshine 2015-4-29 07:33
11
讲得很细,可以看出楼主对浏览器、脚本语言以及XSS的了解很专业。顶一个!
6
shayi 2015-5-25 15:59
12
补充内容

关于浏览器支持 unicode 字符转义序列引发的 xss

以 “\u”开始的6个 ASCII 字符,是 javascript 语言核心中,用于
unicode 字符的转义序列,“\u”后面的4个 ASCII 字符(或2个16进制数),可以采用 UTF-8/16/32 等编码形式;这里使用的是 UTF-16 编码(三种编码都是 unicode 编码的子集),使用这些编码是为了支持一些不能正常显示 unicode 字符的计算机系统。实际上,unicode 字符转义使用与 ASCII 字符的16进制编码相同的
映射;例如,对于字符串“alert”,其 ASCII 16进制编码形为“\61\6c\65\72\74”;其 unicode UTF-16 编码转义形式为“\u0061\u006c\u0065\u0072\u0074” ,第2个16进制数与 ASCII 字符的16进制编码相同,第1个16进制数在编码 ASCII 字符时为 00;在编码其它 unicode 字符时不为 00。
javascript 语言核心(以及实现它的浏览器内部的 javascript 解释引擎)直接支持
unicode 字符转义序列,这意味着,只有这些转义字符出现在页面中与 javascript 执行上下文相关的环境中时,才能被浏览器正确解码,这些环境包括但不限于(其它上下文各位可以自行挖掘):
1。 script 元素内部;
2。 支持绑定事件处理程序的元素内部;
3。支持在属性值中使用 javascrip 伪协议的元素;
如果直接在 HTML 文档中(例如普通的元素内)使用 unicode 字符转义序列 ,浏览器可能无法识别并解码成相应的 unicode 字符,下面通过例子演示:


<!DOCTYPE html>
<html>
    <head>
                <meta http-equiv="Content-type" content="text/html;charset=utf-8" />
                <title>XssPayloadTest</title>
        </head>
        <body>
        \u0061\u006c\u0065\u0072\u0074
        <div>
            <a href = "javascript:\u0061\u006c\u0065\u0072\u0074('xss!')">百度首页</a>
            <input type = "text" name ="search" id ="search" value = ""onfocus = \u0061\u006c\u0065\u0072\u0074('利用事件处理程序的xss!')//" class = "text_area"/>
            <script>
                document.write("\u0061\u006c\u0065\u0072\u0074");
            </script>
             
    </body>
</html>


第8行直接在 body 元素(页面文档体)中写入ASCII 字符串“alert”的 unicode UTF-16 编码转义形式 “\u0061\u006c\u0065\u0072\u0074”,浏览器将不会解码并还原成“alert”,而是直接在页面输出“\u0061\u006c\u0065\u0072\u0074”;
第10行在 a 元素的 href 属性值中使用 javascript 伪协议,其中的“alert”保留字使用 unicode UTF-16 编码转义形式,浏览器会解码并还原成 “alert”,导致在点击名为“百度首页”的超链接时,弹出显示“xss!”的信息框;
第11行给 input 元素的 onfocus (鼠标点击聚焦输入框)事件绑定的回调函数正是采用 unicode UTF-16 编码转义形式的 “alert”,浏览器会解码并还原,导致用户点击输入框时触发事件处理程序,弹出提示框;
第13行在 script 元素内调用 document.write(),作为其参数传递的正是 unicode UTF-16 编码转义形式的 “alert”,浏览器会解码并还原,最终在页面输出 alert 字符串。
下面的截图验证了浏览器的 javascript 解析引擎在上述三种 javascript 执行上下文中,遇到 unicode  字符转义序列时,所表现出的行为:




这个小实验再次说明了基于黑名单过滤的 XSS 防火墙是不安全的:你也许想到要在元素属性值中过滤 alert ,script,javascript 等字符串,但是你能考虑周全地过滤掉所有相应字符串的 unicode 字符转义序列吗?黑名单匹配曝露的受攻击面是如此之广,以至于任何漏网之鱼都可能被攻击者用于发起 XSS !
sushuangju 2015-6-12 16:32
13
多谢楼主的分享。
kxltsuper 2015-7-5 21:13
14
学习了!真乃精细高手也!
medas 2017-9-3 09:29
15
收获匪浅
返回



©2000-2017 看雪学院 | Based on Xiuno BBS | 域名 加速乐 保护 | SSL证书 又拍云 提供 | 微信公众号:ikanxue
Time: 0.013, SQL: 9 / 京ICP备10040895号-17