http://mdsec.net/search/48/
http://mdsec.net/search/52/
提示
在前面的两个攻击中,即使攻击者能够控制一段脚本,但由于应用程序对单引号或双引号进行了转义,他也无法使用它们,但可以使用string.fromCharCode技巧,不用分隔符创建字符串。
如果注入的脚本位于事件处理程序之内,而非完整的脚本块,则可以对引号进行HTML编码,以避开应用程序的净化,并使受控制的字符串免于被过滤。例如,如果可以控制以下输入中的foo值:
并且应用程序正确转义输入中的引号和反斜线,则可以成功实施以下攻击:
这导致以下响应,由于一些浏览器在将事件处理程序作为JavaScript执行之前会执行HTML解码,因此该攻击取得成功:
应对用户输入进行HTML编码来防范XSS攻击,对于这一常规建议,应注意以下事实:在作 为JavaSrcipt执行之前,事件处理器会被HTML解码。在这种情况下,进行HTML编码并不一定能够阻止攻击。攻击者甚至可以利用这种方法避开其他防御机制。
6.突破长度限制
当应用程序把输入截短为一个固定的最大长度时,有三种建立攻击字符串的方法。
第一种相当明显的方法是尝试使用最短可能长度的JavaScriptAPI,删除那些通常包含在内但并不完全必要的字符,缩短攻击有效载荷。例如,如果注入现有的一段代码,下面的28字节命令将把用户的cookie传送至主机名为a:的服务器。
另外,如果直接注入HTML,那么下面这个30字节的标签将从主机名为a:的服务器加载并执行一段脚本。
在因特网上,这些示例明显需要进行扩展,在其中包含一个有效的域名或IP地址。但是,内部企业网络实际有可能使用一台WINS名为a的机器作为接收服务器。
提示
可以使用Dean Edward的JavaScript packer工具(见http://dean.edwards.name/packer/)删除不必要的空白符,尽可能地缩短某一段脚本。这个工具还可将脚本转换成单独一行,方便插入到一个请求参数中。
第二种更加强大的突破长度限制的技巧是将一个攻击有效载荷分布到几个不同的位置,用户控制的输入在这里插入到同一个返回页面中。以下面的URL为例:
https://wahh-app.com/account.php?page_id=244&seed=129402931&mode=normal
它将返回一个包含以下内容的页面:
假设应用程序对每个字段实施了长度限制,以阻止在其中插入有效的攻击字符串。但是攻击者仍然可以使用以下URL将一段脚本分布到他所控制的三个位置,从而传送一个有效的攻击字符串:
这个URL的参数值植入到页面中后,生成如下脚本:
最终得到的HTML完全有效,等同于加粗显示的部分。其中的源代码块已成为JavaScript注释(包含在/*与*/之间),因此被浏览器忽略。这样,注入的脚本被执行,就好像它被完整地插入到页面的某一个位置一样。
提示
这种将一个攻击有效载荷分布到几个字段中的技巧,有时还可用于避开其他类型的防御过滤。应用程序经常对单独一个页面的不同字段执行不同的数据确认与净化。在前面的示例中,假设page_id与mod参数的最大长度为12个字符。由于这些字段如此地短,因此应用程序的开发者没有对其实施任何XSS过滤。另一方面,seed参数的长度没有限制,因此应用程序对其实施严格的过滤,以防止攻击者在其中注入"<或>字符。在这种情况下,尽管开发者实施了过滤,但攻击者不使用任何被阻止的字符,仍然能够在seed参数中插入一段任意长度的脚本,因为注入到周围字段中的数据可以建立JavaScript环境。
第三种在某些情况下非常有效的突破长度限制的技巧是,将一个反射型XSS漏洞“转换”成一个基于DOM的漏洞。例如,在最初的反射型XSS漏洞中,如果应用程序对复制到返回页面中的message参数设置长度限制,那么就可以注入以下45字节的脚本,它对当前URL中的片断字符串(fragment string)求值。
通过在易于受到反射型XSS攻击的参数中注入这段脚本,可以在生成的页面中造成一个基于DOM的XSS漏洞,从而执行位于片断字符串中的另一段脚本,它不受应用程序过滤的影响,可为任意长度。例如:
以下为上述示例的较短版本,此示例在多数情况下可以运行:
在这个版本中,整个URL经过URL解码,然后传递给eval命令。整个URL将作为有效的JavaScript执行,因为http:协议前缀作为代码标签,协议前缀后面的//则作为单行注释,%OA经过URL解码后将变为换行符,表示结束注释。
7.实施有效的XSS攻击
通常,在探查潜在的XSS漏洞,以了解并避开应用程序的过滤机制时,你往往是在浏览器以外进行测试,也就是使用Burp Repeater之类的工具重复发送相同的请求,每次对请求进行略微修改,然后测试这种修改对响应的影响。某些情况下,在以这种方式创建概念验证攻击后,你可能还需要完成任务才能针对其他应用程序用户实施有效攻击。例如,其他用户的请求中的XSS进入点(如cookie或Referer消息头)可能难以控制;或者目标用户可能使用的是内置了防范反射型XSS攻击功能的浏览器。在这一节中,我们将介绍你在实施有效的XSS攻击时可能遇到的各种挑战及如何应对这些挑战。
将攻击扩展到其他应用程序页面
假如你所确定的漏洞位于你不感兴趣的应用程序区域,只影响未经过验证的用户,而其他区 域则包含真正敏感的数据和你希望攻破的功能。
通常,在这种情况下,设计一个可以通过应用程序的某个区域中的XSS漏洞传送,并且在用户的浏览器中持续存在的攻击有效载荷,就可以攻破同一个域中的目标数据或功能。
要实现上述目的,一个简单的办法是创建一个包含整个浏览器窗口的iframe,然后在该iframe中重新加载当前页面。在用户浏览站点并登录到通过验证的区域时,注入的脚本将始终在顶层窗口中运行。这样,你就能够钩住子iframe中的导航事件和表单提交,监视iframe中显示的所有响应内容,当然也能够在适当的时候劫持用户的会话。在支持HTML5的浏览器中,当用户在页面间移动时,脚本甚至可以使用window.history.pushState()函数在地址栏中设置适当的URL。
请参阅以下URL了解这种攻击的一个示例:
http://blog.kotowicz.net/2010/11/xss-track-how-to-quietly-track-whole.html
错误观点
“我们不用担心站点中未通过验证部分的XSS漏洞,攻击者不可能利用它们来劫持会话。”
因为两方面的原因,这种看法并不正确。首先,应用程序未通过验证部分的XSS漏洞可被攻击者用于直接攻破验证用户的会话。因此,未通过验证部分的反射型XSS漏洞比通过验证部分的这类漏洞更严重,因为潜在受害者的范围更广。其次,即使用户尚未通过验证,攻击者仍然可以通过提交几个请求,在受害者的浏览器中注入某种木马功能,等待用户登录,然后劫持用户的会话。如第13章所述,攻击者甚至可能会使用以JavaScript编写的键盘记录器来捕获用户的密码。
修改请求方法
假如你确定的XSS漏洞使用POST请求,但实施攻击的最便捷的方法需要使用GET请求——例如,提交一个论坛贴子,其中包含针对易受攻击的URL的IMG标签。
在这种情况下,我们有必要进行判定,如果将POST请求转换为GET请求,应用程序是否对请求进行相同的处理。许多应用程序接受以上任何一种请求。
在Burp Proxy中,可以使用上下文菜单中的“更改请求方法”(Change Request Method)命令将任何请求在GET与POST方法之间切换。
错误观点
“这个XSS漏洞无法被利用。因为攻击者无法通过GET请求实施攻击。”
如果一个反射型XSS漏洞只能使用POST方法加以利用,应用程序仍然会受到各种攻击传送机制的影响,包括使用恶意第三方Web站点的传送机制。
有时,把使用GET方法的攻击转换成使用POST方法的攻击可能会避开某些过滤。许多应用程序在整个应用程序中执行某种常规过滤,阻止已知的攻击字符串。如果一个应用程序希望收到使用GET方法的请求,它可能只对URL查询字符串执行这种过滤。将请求转换为使用POST方法就可 以完全避开这种过滤。
通过cookie利用XSS漏洞
一些应用程序包含反射型XSS漏洞,攻击这种漏洞的进入点在请求cookie中。在这种情况下,可以通过各种技巧来利用这种漏洞:
和修改请求一样,应用程序可能允许你使用与cookie同名的URL或主体参数来触发漏洞。
如果应用程序包含任何可用于直接设置cookie值的功能(例如,基于提交的参数值设置cookie的首选项页面),则你可以设计一个跨站点请求伪造攻击,在受害者的浏览器中设置所需的cookie。然后,再诱使受害者提出以下两个请求:其中一个请求用于设置包含XSS有效载荷所需的cookie,另一个请求则用于请求以危险的方式处理cookie值的功能。
之前,人们已经在浏览器扩展技术(如Flash)中发现各种漏洞,这使攻击者可以使用任意HTTP消息头提交跨域请求。当前,至少有一种此类漏洞广为人知,但尚未修复。因此,可以利用浏览器插件中的某个这种类型的漏洞来提交跨域请求,在其中包含任意旨在触发漏洞的cookie消息头。
如果上述任何方法都无法成功实施攻击,你可以利用相同(或相关)域中的任何其他反射型XSS漏洞使用所需值设置一个永久性cookie,持续对受害用户进行攻击。
通过Referer消息头利用XSS漏洞
一些应用程序包含只能通过Referer消息头触发的XSS漏洞。利用受他们控制的Web服务器,攻击者可以相当轻松地利用这些漏洞。例如,攻击者可以诱使受害者请求他们的服务器上的URL,该URL中包含针对易于攻击的应用程序的适当XSS有效载荷。然后,攻击者的服务器将返回一个响应,以请求上述URL,而攻击者的有效载荷就包含在此请求的Referer消息头中。
在某些情况下,只有在Referer消息头包含与易受攻击的应用程序同属一个域的URL时,XSS漏洞才会触发。这时,可以利用应用程序中的任何现成的重定向功能来实施攻击。为此,你需要构建一个指向该重定向功能的URL,在其中包含XSS攻击的有效载荷,并使其重定向到易于攻击的URL。这种攻击能否成功,取决于该功能使用的重定向方法,以及当前浏览器在进行上述重定向时是否会更新Referer消息头。
通过非标准请求和响应内容利用XSS漏洞
目前,有越来越多的复杂应用程序采用不包含传统的请求参数的Ajax请求。而在此前,请求通常包含XML和JSON格式的数据,或采用各种序列化方案。因此,针对这些请求的响应往往包含同种或其他格式的数据,而不是HTML。
与这些请求和响应相关的服务器端功能大多会表现出类似于XSS的行为。正常情况下,应用程序会按原样返回表明漏洞确实存在的请求有效载荷。
在这种情况下,仍然可以利用这种行为来实施XSS攻击。为此,需要满足以下两个条件:
你需要想办法使目标用户提出所需的跨域请求;
你需要以某种方式操纵响应,以便它在到达浏览器时执行你的脚本。
满足这两个条件并不容易。首先,相关请求通常由JavaScript使用XMLHttpRequest提出(请参阅第3章了解详细信息)。默认情况下,这种方法并不能用于提出跨域请求。虽然HTML5正在对 XMLHttpRequest进行修改,以便于站点指定其他可能与它们交互的域,但是,如果你能够找到允许第三方交互的目标,就可以采用更简单的方法来攻破该目标(请参阅第13章了解详细信息)。
其次,在任何攻击中,应用程序返回的响应均由受害者的浏览器直接处理,而不是由定制脚本按原样处理。因此,响应将包含任何非HTML格式的数据(通常使用对应的Content-Type消息头)。在这种情况下,浏览器会以针对这种数据类型(如果它识别该类型)的方式正常处理响应,因而通过HTML注入脚本代码的常用方法也可能会失效。
尽管难以实现,但在某些情况下我们仍然可以满足这两个条件,从而利用类似于XSS的行为来实施有效攻击。下面我们将举例说明如何使用XML数据格式来实施攻击。
√ 传送跨域XML请求
使用HTML表单(将enctype属性设置为text/plain)可以在HTTP请求主体中跨域传送几乎任何数据。这将告诉浏览器按以下方式处理表单参数:
在请求中隔行传送每个参数;
使用等号分隔每个参数的名称和值(和平常一样);
不对参数名称或值进行任何URL编码。
虽然某些浏览器并不遵循这种规范,但当前版本的Internet Explorer、Firefox和Opera都采用这种规范。
上述行为意味着,只要数据中至少包含一个等号,你就可以在消息主体中传送任意数据。为此,你需要将数据分隔成两块,等号前一块,等号后一块。然后,将第一块数据放在参数名称中,将第二块数据放在参数值中。这样,浏览器在构建请求时,它会传送以等号分隔的两块数据,因而实际上构建了所需的数据。
由于XML在起始XML标签的version属性中始终至少包含一个等号,因此,我们可以在消息主体中使用这种技巧跨域传送任意数据。例如,如果所需的XML如下所示:
则可以使用以下表单发送这些数据:
要在param参数的值中包含常用的攻击字符,如标签尖括号,你需要在XML请求中对这些字符进行HTML编码。因此,在生成该请求的HTML表单中,你需要对它们进行双重HTML编码。
提示
只要你能够将等号合并到请求中的某个位置,就可以使用这种技巧跨域提交几乎包含任何类型的内容(如JSON编码的数据和序列化二进制对象)的请求。通常,通过修改请求中可以包含等号字符的自由格式的文本字段即可达到这一目的。例如,在下面的JSON数据中,注释字段被用于注入所需的等号字符:
在使用这种技巧时,唯一需要注意的地方是,生成的请求将包含以下消息头:
正常情况下,根据生成请求的具体方式,最初的请求本应包含一个不同的Content-Type消息头。如果应用程序接受提供的Content-Type消息头并正常处理消息主体,则在设计有效的XSS攻击时就可以使用这种技巧。如果由于Content-Type消息头已修改,应用程序无法正常处理请求,则可能没有办法跨域传送适当的请求来触发类似于XSS的行为。
提示
如果在包含非标准内容的请求中确定了类似于XSS的行为,首先应该快速确定,在将Content-Type消息头更改为text/plain后,这种行为是否仍然存在。如果这种行为不再存在,则你不必付出任何其他努力来尝试设计有效的XSS攻击。
√ 从XML响应中执行JavaScript
在尝试利用非标准内容中的类似于XSS的行为时,你需要克服的第二个障碍是找到一种操纵响应的方法,使其在由浏览器直接处理时能够执行你的脚本。如果响应中包含错误的Content-Type消息头,或根本不包含Content-Type消息头,或者如果输入在响应主体的开始部分就已反射,则可以轻松克服这种障碍。
但是,响应通常都包含准确描述应用程序返回的数据类型的Content-Type消息头。此外,你的输入大多是在响应的中间部分反射。同时,在此位置之前和之后的响应内容包含的数据遵循指定内容类型的相关规范。不同浏览器解析内容的方式各不相同。一些浏览器完全信任Content-Type消息头,一些浏览器则会检查内容本身,并在具体的类型有所不同时覆盖指定的类型。但是,在这种情况下,无论浏览器如何处理内容,它都不大可能将响应作为HTML处理。
如果可以构建能够成功执行脚本的响应,这往往需要利用所注入的内容类型的特定语法特性。幸好,对于XML而言,你可以使用XML标记定义一个映射为XHTML的新命名空间,并使浏览器将该命名空间解析为HTML,从而达到执行脚本的目的。例如,在Firefox处理以下响应时,注入的脚本将得以执行:
如上所述,如果响应由浏览器直接处理,而不是由通常处理响应的原始应用程序组件处理时,此攻击将取得成功。
在利用几乎任何反射型XSS漏洞时总是会遇到一个障碍,即各种浏览器功能都针对XSS攻击为用户提供了保护。Internet Explorer浏览器默认包含一个XSS过滤器,其他一些浏览器也通过插件的形式提供类似的功能。这些过滤器的工作方式基本类似:它们被动监视请求和响应,并使用各种规则来确定正在进行的潜在XSS攻击,一旦确定潜在攻击,就修改响应的某些部分来阻止这些攻击。
如前所述,如果可以通过任何广泛使用的浏览器来利用XSS条件,我们就应将这些条件视为漏洞。而且,某些浏览器提供XSS过滤器并不意味着不需要修复XSS漏洞。在某些现实情况下,攻击者可能恰恰需要通过包含XSS过滤器的浏览器来利用某个漏洞。此外,用于避开XSS过滤器的方法本身也值得我们关注。在某些情况下,我们甚至可以利用这些方法来实施通过别的方法无法实施的其他攻击。
在这一节中,我们将介绍Internet Explorer的XSS过滤器。目前,它是最成熟及应用最广泛的过滤器。
IE XSS过滤器的核心功能如下。
检查跨域请求中的每一个参数值,以确定注入JavaScript的可能尝试。它会根据一个常见攻击字符串的基于正则表达式的黑名单来检查这些值,从而完成这一任务。
如果发现潜在恶意的参数值,则检查响应,看其中是否包含相同的值。
如果响应中出现该值,则会对响应进行净化,以防止执行任何脚本。例如,它会将<script>修改为<sc#ipt>。
关于IE XSS过滤器,需要指出的第一个问题是:大体而言,它能够有效阻止利用XSS漏洞的标准攻击,从而为任何尝试实施这类攻击的攻击者设置了很大的障碍。这意味着,需要通过某些特定的方法才能避开这种过滤器。此外,还可以利用这种过滤器的工作机制来实施通过别的方法无法实施的其他攻击。
首先,利用这种过滤器的核心功能,我们可以找到一些避开该过滤器的方法。
该过滤器仅检查参数值,而不检查参数名称。一些应用程序易于受到针对参数名称的攻击,如在响应中回显请求的整个URL或整个查询字符串。该过滤器无法阻止这类攻击。
该过滤器单独检查每个参数值。但是,如果在同一个响应中反射多个参数,就可以将攻击从一个参数传递到另一个参数(如用于突破长度限制的技巧中所述)。如果可以将XSS有效载荷分割成几块,则其中任何一块都不会与受阻止的表达式黑名单相匹配,这样,过滤器将无法阻止攻击。
由于性能原因,该过滤器仅检查跨域请求。因此,如果攻击者能够使用户向XSS URL提出“本地”请求,过滤器将无法阻止这种攻击。通常,如果应用程序包含任何行为,允许攻击者在由其他用户查看的页面中注入任意链接,这种攻击即成为可能(虽然这本身也属于反射型攻击,但XSS过滤器仅尝试阻止注入的脚本,而不是注入的链接)。在这种情况下,攻击者需要完成两个步骤:在用户的页面中注入恶意链接;用户单击链接并收到XSS有效载荷。
其次,在某些情况下,可以利用浏览器和服务器的特殊行为来避开XSS过滤器。
如你所见,浏览器在处理HTML时接受各种类型的异常字符和语法。例如,IE本身就接受NULL字节。有时,攻击者可以利用IE的这种古怪行为来避开它的XSS过滤器。
如第10章所述,在请求包含多个同名参数时,应用程序服务器的行为各不相同。在某些情况下,它们会串联收到的所有值。例如,在ASP.NET中,如果查询字符串包含:
传递给应用程序的参数
pl的值为:
与之相反,即使参数的名称相同,但IE XSS过滤器仍然会单独处理每个参数。这种行为差异使得攻击者能够轻松在几个相同名称的“不同”请求参数之间传递XSS有效载荷,从而避开针对每个单独的值的黑名单过滤(因为服务器已将它们串联起来)。