3.2.4 跨站脚本漏洞案例及分析
XSS漏洞表现为各种形式,并且可分为三种类型:反射型、保存型和基于以DOM的XSS漏洞。虽然这些漏洞具有一些相同的特点,但在如何确定及利用这些漏洞方面,仍然存在一些重要的差异。下面我们将分别介绍每一类XSS漏洞。
1.反射型XSS漏洞
(1)基本定义
如果一个应用程序使用动态页面向用户显示错误消息,就会造成一种常见的XSS漏洞。通常,该页面会使用一个包含消息文本的参数,并在响应中将这个文本返回给用户。对于开发者而言,使用这种机制非常方便,因为它允许他们从应用程序中调用一个定制的错误页面,而不需要对错误页面中的消息分别进行硬编码。
(2)通过简单实例解释其定义
例如,下面的URL返回如图3.25所示的错误消息:
分析返回页面的HTML源代码后,我们发现,应用程序只是简单复制URL中message参数值,并将这个值插入到位于适当位置的错误页面模板中:
提取用户提交的输入并将其插入到服务器响应的HTML代码中,这是XSS漏洞的一个明显特征。如果应用程序没有实施任何过滤或者净化措施,那么它很容易受到攻击。
图3.25 一条动态生成的错误消息
下面的URL经过专门设计,它用一段生成弹出对话框的JavaScript代码代替错误消息:
请求这个URL将会生成一个HTML页面,其中包含以下替代原始消息的脚本:
可以肯定,如果该页面在用户的浏览器中显示,弹出消息就会出现,如图3.26所示。
图3.26 一次概念验证XSS攻击
进行这个简单的测试有助于澄清两个重要问题:首先message参数的内容可用任何返回给浏览器的数据替代;其次,无论服务器端应用程序如何处理这些数据(如果有),都无法阻止提交JavaScript代码,一旦错误页面在浏览器中显示,这些代码就会执行。
在现实世界的Web应用程序中存在的XSS漏洞中,有近75%的漏洞属于这种简单的XSSbug。由于利用这种漏洞需要设计一个包含嵌入式JavaScript代码的请求,随后这些代码又被反射到任何提出请求的用户,因而它被称作反射型XSS。攻击有效载荷分别通过一个单独的请求与响应进行传送和执行。为此,有时它也被称为一阶XSS。
(3)漏洞案例分析
利用XSS漏洞攻击应用程序其他用户的方式有很多种。最简单的一种攻击,也是我们常用于说明XSS漏洞潜在影响的一种攻击,可以导致攻击者截获通过验证的用户会话令牌。劫持用户的会话后,攻击者就可以访问该用户经授权访问的所有数据和功能。
实施这种攻击的步骤如图3.27所示。
1)用户正常登录应用程序,得到一个包含会话令牌的cookie:
2)攻击者通过某种方法向用户提交一下URL:
3)用户从应用程序中请求攻击者传送给他们的URL。
4)服务器响应用户的请求。由于应用程序中存在XSS漏洞,相应包含攻击者创建的JavaScript代码。
5)用户浏览器收到攻击者的JavaScript代码,像执行从应用程序收到的其他代码一样,浏览器执行这段代码。
6)攻击者创建的恶意JavaScript代码为:
这段代码可让用户浏览器向mdattacker.net(攻击者拥有)提出一个请求。请求中包含用户访问应用程序的当前会话令牌:
7)攻击者监控访问mdattacker.net的请求并收到用户的请求。攻击者使用截获的令牌劫持用户的会话,从而访问该用户的个人信息,并用该用户的权限执行任意操作。
图3.27 XSS攻击的实施步骤
2.保存型XSS漏洞
另一种常见的XSS漏洞叫作保存型跨站点脚本。如果一名用户提交的数据被保存在应用程序中(通常保存在一个后端数据库中),然后不经适当过滤或净化就显示给其他用户,此时就会出现这种漏洞。
在支持终端用户交互的应用程序中,或许在有管理权限的员工访问同一个应用程序中的用户记录和数据的应用程序中,保存型XSS漏洞很常见。例如,以一个拍卖应用程序为例,它允许买家提出与某件商品有关的问题,然后由卖家回答。
如果一名用户能够提出一个包含嵌入式JavaScript的问题,而且应用程序并不过滤或净化这个JavaScript,那么攻击者就可以提出一个专门设计的问题,在任何查看该问题的用户(包括卖家和潜在的买家)的浏览器中执行任意脚本。在这种情况下,攻击者就可让不知情的用户去竞标一件他不需要的商品,或者让一位卖家接受他提出的低价,结束竞标。
一般情况下,利用保存型漏洞的攻击至少需要向应用程序提出两个请求。攻击者在第一个请求中传送一些专门设计的数据,其中包含恶意代码,应用程序接受并保存这些数据。在第二个请求中,一名受害者查看某个包含攻击者的数据的页面,这时恶意代码开始执行。为此,这种漏洞有时也叫作二阶跨站点脚本。(在这个示例中,使用XSS实际上并不准确,因为攻击中没有跨站点元素。但由于这个名称被人们广泛使用,因此我们在这里仍然沿用它。)
图3.28说明了一名攻击者如何利用保存型XSS漏洞,实施上述利用反射型XSS漏洞实施的相同会话劫持攻击。
图3.28 保存型XSS攻击的步骤
反射型与保存型XSS攻击步骤上存在两个重要的区别,这也使得后者往往造成更大的安全威胁。首先,在反射型XSS脚本攻击中,要利用一个漏洞,攻击者必须以某种方式诱使受害者访问他专门设计的URL。而保存型XSS脚本攻击则没有这种要求。在应用程序中展开攻击后,攻击者只需要等待受害者浏览已被攻破的页面或功能。首先,这个页面是一个正常用户将会主动访问的常规页面。其次,如果受害者在遭受攻击时正在使用应用程序,攻击者就更容易实现其利用XSS漏洞的目的。例如,如果用户当前正在进行会话,那么攻击者就可以劫持这个会话。在反射型XSS攻击中,攻击者可能会说服用户登录,然后单击他们提供的一个链接,从而制造这种情况。或者他可能会部署一个永久性的有效载荷并等待用户登录。但是,在保存型XSS攻击中,攻击者能够保证,受害用户在他实施攻击时已经在访问应用程序。因为攻击有效载荷被保存在用户自主访问的一个应用程序页面中,所以,当有效载荷执行时,任何攻击受害者都在使用应用程序。而且,如果上述页面位于应用程序通过验证的区域内,那么那时攻击受害者一定已经登录。
反射型与保存型XSS攻击之间的这些区别意味着保存型XSS漏洞往往会给应用程序带来更严重的安全威胁。许多时候,攻击者可以向应用程序提交一些专门设计的数据,然后等待受害者访问它们。如果其中一名受害者是管理员,那么攻击者就能够完全攻破整个应用程序。
3.基于DOM的XSS漏洞
反射型和保存型XSS漏洞都表现出一种特殊的行为模式,其中应用程序提取用户控制的数据并以危险的方式将这些数据返回给用户。第三类XSS漏洞并不具有这种特点。在这种漏洞中,攻击者的JavaScript通过以下过程得以执行。
(1)用户请求一个经过专门设计的URL,它由攻击者提交,且其中包含嵌入式JavaScript。
(2)服务器的响应中并不以任何形式包含攻击者的脚本。
(3)当用户的浏览器处理这个响应时,上述脚本得以处理。
这一系列事件是如何发生呢?由于客户端JavaScript可以访问浏览器的文本对象模型(Document Object Model,DOM),因此它能够决定用于加载当前页面的URL。由应用程序发布的一段脚本可以从URL中提取数据,对这些数据进行处理,然后用它动态更新页面的内容。如果这样,应用程序就可能易于受到基于DOM的XSS攻击。
回到前面的反射型XSS漏洞中的示例,其中服务器应用程序将一个URL参数值复制到一条错误消息中。另一种实现相同功能的办法是由应用程序每次返回相同的静态HTML,并使用客户端JavaScript动态生成消息内容。
例如,假设应用程序返回的错误页面包含以下脚本:
这段脚本解析URL,提取出message参数的值,并把这个值写入页面的HTML源代码中。如果按开发者预想的方式调用,它可以和前面的示例一样,用于创建错误消息。但是,如果攻击者设计出一个URl,并以JavaScript代码作为message参数,那么这段代码将被动态写入页面中,并像服务器返回代码一样得以执行。在这个示例中,前面示例中利用反射型XSS漏洞的同一个URL也可用于生成一个对话框:
利用基于DOM的XSS漏洞的过程如图3.29所示。
图3.29 基于DOM的XSS攻击的步骤
与保存型XSS漏洞相比,基于DOM的XSS漏洞与反射型XSS漏洞具有更大的相似性。利用它们通常需要诱使一名用户访问一个包含恶意代码的专门设计的URL,并且服务器响应那个确保恶意代码得以执行的特殊请求。但是,在利用反射型与基于DOM的XSS漏洞的细节方面,还存在一些重要的差异。