对于在前面步骤中记下的每一个潜在的XSS漏洞,采取以下措施。
(1)检查HTML源代码,确定受控制的字符串的位置。
(2)如果字符串出现在几个位置,应将每个位置当做一个潜在的漏洞,分别进行分析。
(3)根据用户控制的字符串在HTML中的位置,确定需要如何对其进行修改以使任意JavaScript得以执行。通常,有大量方法可成为传送攻击的有效工具。
(4)向应用程序提交设计的字符串,测试它是否有用。如果设计的字符串仍然按原样返回,表示应用程序存在XSS漏洞。使用一段概念验证脚本显示一个警报对话框,重复检查语法是否正确,并确定响应显示时,对话框是否出现在浏览器中。
3.探查防御性过滤
通常情况下最初提交的攻击字符串并不会被服务器按原样返回,因而无法成功执行注入的JavaScript。如果是这样,不要放弃!接下来应该确定服务器对输入进行了哪些处理。主要有以下3种可能的情况。
应用程序或者Web应用程序防火墙保护的应用程序发现一个攻击签名,完全阻止了输入。
应用程序已经接受了输入,但对攻击字符串进行了某种净化或编码。
应用程序把攻击字符串截短至某个固定的最大长度。
我们将分别分析每一种情况,并讨论如果通过每种方法避开应用程序设立的障碍。
4.避开基于签名的过滤
在第一种类型的过滤中,应用程序通常会对攻击字符串做出与无害字符串截然不同的响应,例如,通过一条错误消息,甚至会指出发现一个可能的XSS攻击,如图12-8所示。
图12-8 一条由ASP.NET反XSS过滤器生成的错误消息
如果出现这种情况,那么接下来,应该确定输入中的哪些字符或表达式触发了过滤。一种有效的方法是轮流删除字符串的不同部分,看输入是否仍然被阻止。通常,使用这种方法可迅速查 明是否是某个特殊的表达式(如<script>)造成请求被阻止。如果确实如此,那么需要对过滤进行测试,看是否有任何避开过滤的办法。
有各种不同的方法可以在HTML页面中引入脚本代码,这些方法通常能够避开基于签名的过滤。因此,测试员要么找到引入脚本的其他方法,要么使用浏览器接受的略显畸形的语法。在这一节中,我们将介绍各种执行脚本的不同方法,然后说明一系列可用于避开常用过滤的技巧。
引入脚本代码的方法
有4种不同的方法可用于在HTML页面中引入脚本代码。我们将逐一介绍这些方法,并提供一些可用于成功避开基于签名的输入过滤的特殊示例。
注解
浏览器对于各种HTML和脚本语法的支持各不相同。个体浏览器的行为也往往会随着新版本的发布而发生改变。因此,任何针对个体浏览器行为的“明确”指南也很快会过时。但是,从安全的角度看,应用程序需要在所有当前和最新版本的常用浏览器中可靠运行。如果XSS攻击只能通过仅由少数用户使用的特定浏览器进行传送,这仍然构成一个漏洞,应对其予以修复。到本书截稿时止,本章中提供的所有示例至少能够在某种注流浏览器上运行。
为便于参考,本章于2011年3月撰写,所有描述的攻击至少能够在以下一种浏览器上实施:
Internet Explorer版本8.0.7600.16385 ;
Firefox 版本 3.6.15。
脚本标签
除直接使用<script>标签外,还可以通过各种方法、使用复杂的语法来隐藏标签,从而避开某些过滤:
上例中的基于Base64的字符串为:
√ 事件处理器
有大量事件处理器可与各种标签结合使用,以用于执行脚本。以下是一些较为少见的示例,可在不需要任何用户交互的情况下执行脚本:
HTML5使用事件处理器提供了大量向量。这包括使用autofocus属性自动触发之前需要用户交互的事件:
它允许在结束标签中使用事件处理器:
最后,HTML5还通过事件处理器引入了新标签:
√ 脚本伪协议
脚本伪协议可用在各种位置,以在需要URL的属性中执行行内脚本。以下是一些示例:
虽然上面的示例主要使用的是javascript伪协议,但是,还可以在Internet Explorer上使用vbs协议,如本章后面部分所述。
和事件处理器一样,HTML5也提供一些在XSS攻击中使用脚本伪协议的新方法:
在针对输入过滤进行攻击时,新的event-source标签特别有用。与之前的任何HTML5标签不同,它的名称中包含一个连字符,因此,使用这个标签可以避开传统的、认为标签名称只能包含字母的基于正则表达式的过滤。
√ 动态求值的样式
一些浏览器支持在动态求值的CSS样式中使用JavaScript。以下示例可以在IE7及其早期版本上执行,如果在兼容模式下运行,还可以在后续版本上执行:
最新版本的IE不再支持上述语法,因为这些语法只能用在XSS攻击中。但是,在最新版本的IE中,使用以下请求可以达到同样的效果:
使用Firefox浏览器可以通过moz-binding属性实施基于CSS的攻击,但是,由于应用程序已 对这一功能实施了限制,现在已经无法通过它来实施XSS攻击。
√ 避开过滤:HTML
在前面几节中,我们介绍了各种可用于在HTML页面中执行脚本代码的方法。许多时候,你会发现,通过采用不同的、较为少见的脚本执行方法,就可以避开基于签名的过滤。如果这种方法失败,你就需要寻找其他隐藏攻击的方法。通常,你可以引入过滤器接受的异常语法,并使浏览器接受返回的输入。在本节中,我们将介绍各种对HTML请求进行模糊处理以避开常见的过滤的方法。本节对JavaScript和VBScript语法应用相同的原则。
旨在阻止XSS攻击的基于签名的过滤通常采用正则表达式或其他技巧来确定关键的HTML组件,如标签括号、标签名称、属性名称和属性值。例如,过滤器可能会阻止包含使用已知可用于引入脚本的特殊标签或属性名称的HTML的输入,或试图阻止以脚本伪协议开头的属性值。通过以一种或多种浏览器接受的方式在HTML中的关键位置插入不常见的字符,可以避开其中的许多过滤。
我们来了解一下这种技巧的用法,以下面这段简单的脚本为例:
可以通过各种方式修改这段脚本,并使它至少可在一个浏览器中运行。下面我们将分别介绍这些方法。实际上,你可能需要在一次攻击中结合利用其中的几种技巧,以避开更加复杂的输入过滤。
√ 标签名称
从起始标签名称开始,只需改变所使用字符的大小写,即可避开最简单的过滤:
更进一步,可以在任何位置插入NULL字节:
(在以上示例中,[%XX ]表示十六进制ASCII代码XX 的原义字符。在向应用程序实施攻击时,通常会使用字符的URL编码形式。在查看应用程序的响应时,需要在其中寻找已解码的原义字符。)
提示
NULL字节技巧可用在Internet Explorer上的HTML页面的任何位置。在XSS攻击中灵活使用NULL字节通常可以快速避开不探查IE行为的基于签名的过滤。
事实证明,使用NULL字节可以有效避开配置为阻止包含已知攻击字符串的请求的Web应用程序防火墙(WAF)。为了提高性能,WAF通常以本地代码编写,因此,NULL字节将终止在其中出现的字符串。这样,WAF就无法发现NULL字节之后的恶意有效载荷(请参阅第16章了解详细信息)。
更进一步,如果你对上面示例中的标签名称稍做修改,就可以使用任意标签名称引入事件处理器,从而避开仅仅阻止特定标签名称的过滤:
有时,可以引入不同名称的新标签,但却找不到使用这些标签直接执行代码的方法。在这些情况下,可以使用一种称为“基本标签劫持”的技巧来实施攻击。<base>标签用于指定一个URL,浏览器应使用该URL解析随后在页面中出现的任何相对URL。如果可以引入一个新的<base>,并且页面执行反射点后的任何使用相对URL的<script>,则你就可以指定一个指向受你控制的服务器的基本URL。当浏览器加载在HTML页面的剩余部分指定的脚本时,这些脚本将从指定的服务器加载,但仍然能够在调用它们的页面中执行。例如:
根据规范,<base>标签应出现在HTML页面的<head>部分。但是,一些浏览器,如Firefox,允许<base>标签出现在页面的任何位置,这显著扩大了这种攻击的范围。
√ 标签名称后的空格
一些字符可用于替代标签名称与第一个属性名称之间的空格:
需要注意的是,即使在实施攻击时不需要任何标签属性,仍然应始终在标签名称后面添加一些多余的内容,因为这样做可以避开一些简单的过滤:
√ 属性名称
也可以在属性名称中使用上述NULL字节技巧。这样做可以避开许多试图通过阻止以on开头的属性名称来阻止事件过滤器的简单过滤:
√ 属性分隔符
在最初的示例中,属性值之间并未分隔开来,因而需要在属性值后面插入一些空格,表示属性值已结束,以便于添加其他属性。属性可以选择使用双引号或单引号进行分隔,或在IE上使用重音符分隔:
前面的示例提供了另一种方法,可用于避开一些检查以on开头的属性名称的过滤器。如果过滤器不知道重音符被用作属性分隔符,它会将下面的示例视为仅包含一个属性,其名称不再为事件处理器的名称:
通过使用引号分隔的属性,并在标签名称后面插入异常字符,就可以设计出不需要使用任何空格的攻击,从而避开一些简单的过滤: