关于这类型漏洞很少有人发表相关学习资料,最近笔者遇到了这种漏洞,并根据了网上寥寥无几的文章思路安装了工具来测试,结果却发现工具没办法成功攻击,但是漏洞真实存在,所以笔者就以此为基础展开了关于WebServices的学习,去了解相关的构造原理,改进工具化测试的不足。
笔者查阅了挺多的相关资料,提到了非常多很陌生的技术,这里我对其进行了自我总结,如有不对,欢迎师傅们指出改正。
什么是WebService?
Web Service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的交互操作的应用程序。
总结:
Web服务(WebService)是一种跨语言和跨平台的远程调用(RPC)技术
所谓跨编程语言和跨操作平台,就是说服务端程序采用java编写,客户端程序则可以采用其他编程语言编写,反之亦然!跨操作系统平台则是指服务端程序和客户端程序可以在不同的操作系统上运行。
所谓远程调用,就是一台计算机a上 的一个程序可以调用到另外一台计算机b上的一个对象的方法。
ps.WebService思想有点跟微服务很像,是一种比较"重"和“老”的Web接口技术,目前大部分应用于金融机构的历史应用和比较老的引用中,在内网里面遇到的机会还是挺多的。
WebService的工作流程
服务端应用程序首先向外界暴露出一个能通过Web进行调用的API,为了实现跨平台和跨语言,那么与这个API交互的时候,就需要有一套标准的类型系统来描述WebService这个接口,让用户通过这个标准来解析怎么调用这个接口,然后用户将解析结果封装上自己的数据发送到客户端,服务端统一采用这一套标准进行解析和返回。
WebService的技术实现与支持
1.SOAP(Simple Object Access Protocol, 简单对象访问协议)
SOAP协议 由特定的HTTP信息头和XML内容格式组成
SOAP = http协议 + XML数据格式
2.WSDL(Web Services Description Language, 网络服务描述语言)
给出了SOAP型Web Service的基本定义,WSDL基于XML语言,描述了与服务交互的基本元素,比如函数、数据类型、功能等,少数情况下,WSDL也可以用来描述REST型Web Service。
WSDL文件保存在Web服务器上,通过一个url地址就可以访问到他。客户端要调用WebService服务之前,要知道该服务的WSDL文件的地址。
3.REST(Representational State Transfer, 表征性状态转移)
REST型Web Service允许我们使用JSON格式(也可以使用XML格式)与服务器进行通信。与HTTP类似,该类型服务支持GET、POST、PUT、DELETE方法。
4.WADL(Web Application Description Language, 网络应用描述语言)
WSDL的REST版,一般用于REST型Web Service,描述与Web Service进行交互的基本元素。
PS.笔者目前接触的比较多就是第1.2种,所以就以经典SOAP协议的WebService作为学习目标了。
应用场景
webservice 经常使用在异构系统中,比如一个JAVA开发的OA系统与一个C#开发的CRM系统进行系统交互的时候就可以利用WebService来统一交互标准,当然也可以在同构系统中整合该服务,统一系统交互的标准。这就好比我们经常使用API接口使用JSON封装数据,但是JSON有点缺陷,emmm不算缺陷,是以为其自身的跨域性特点有可能会导致jsonp攻击。
这里笔者介绍两种最常见WebService开发环境。
简单介绍下CXF框架:
CXF(Celtix + XFire)
一个apache用于开发WebService服务端和客户端的框架。
1.安装CXF的依赖包
proxychains4 wget https://mirror-hk.koddos.net/apache/cxf/3.3.6/apache-cxf-3.3.6.tar.gz
2.创建一个项目,导入CXF lib下的依赖包
(1)ICalculator 接口类
package com.xq17.cxf; import javax.jws.WebService; @WebService public interface ICalculator { int add(int a, int b); String concat(String a, String b); }
(2)CalculatorImpl 接口实现类
package com.xq17.cxf; import javax.jws.WebService; @WebService(endpointInterface="com.xq17.cxf.ICalculator", serviceName="Calcutator") public class CalculatorImpl implements ICalculator{ @Override public int add(int a, int b) { return a + b; } @Override public String concat(String a, String b) { return a + b; } }
(3) WebService服务端类
package com.xq17.cxf;
import javax.xml.ws.Endpoint;
public class WebService {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Web Service Start!");
CalculatorImpl implementor = new CalculatorImpl();
String address = "http://127.0.0.1:8081/calculator";
Endpoint.publish(address, implementor);
// 这里就能加载已经定义为WebService接口的CalculatorImpl对象和发布网址
System.out.println("web service started");
}
}
(4) Run
(4) 访问URL
可以看到成功输出了相应的信息。
1.在空白窗体新建一个web服务WebService.asmx
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Services; /// <summary> /// WebService 的摘要说明 /// </summary> [WebService(Namespace = "http://localhost/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。 [System.Web.Script.Services.ScriptService] public class WebService : System.Web.Services.WebService { public WebService() { //如果使用设计的组件,请取消注释以下行 //InitializeComponent(); } [WebMethod] public string HelloWorld() { return "Hello World"; } [WebMethod] public int Power(int num) { return num * num; } }
这里我们写了一个Power函数
2.然后发布到IIS,访问URL
当我们点击服务说明的时候跳转到:http://localhost:8089/WebService.asmx?WSDL
返回的就是WSDL,说明这个接口怎么使用。
访问http://localhost:8089/WebService.asmx?op=Power
可以在浏览器直接调用,这里给出发出的协议,及其返回结果的例子,方便我们写对应的程序来进行调用该服务。
1.SOP1.1
POST /WebService.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://localhost/Power"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Power xmlns="http://localhost/">
<num>int</num>
</Power>
</soap:Body>
</soap:Envelope>
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<PowerResponse xmlns="http://localhost/">
<PowerResult>int</PowerResult>
</PowerResponse>
</soap:Body>
</soap:Envelope>
SOP1.2
POST /WebService.asmx HTTP/1.1
Host: localhost
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<Power xmlns="http://localhost/">
<num>int</num>
</Power>
</soap12:Body>
</soap12:Envelope>
HTTP/1.1 200 OK
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<PowerResponse xmlns="http://localhost/">
<PowerResult>int</PowerResult>
</PowerResponse>
</soap12:Body>
1.3 HTTP
这个仅是支持本机来测试,对外部是不开放的。
POST /WebService.asmx/Power HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: length
num=string
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<int xmlns="http://localhost/">int</int>
这里我们演示下soap1.2
协议的发送过程。
我们可以直接复制SOAP12
的包发送数据来进行请求。
可以看到这里返回了webservice
<PowerResult>9</PowerResult>
这个接口的结果。
PS:
CXF框架整合Spring发布的时候默认会有个services
路径,里面存放了接口的文档说明,这个时候我们就可以获取所有的EndPoint了,笔者当时遇到的就是这个情况。
基于C#的ASP.NET编写的接口信息页就是当前接口的位置,所以我们只要找asm文件来测试就好。
下面我们就来讲一下WSDL到底提供了什么样的解析标准,如何去进行手工构造。
我们首先需要学习下WSDL的描述结构,并尝试自己去解析,编写合法的数据包。
首先WSDL的组成标签:
definitions 根元素,其主要作用是提供了targetNamespace的命名空间
types 主要是用来描述传入的参数类型,和输出的结果类型,被信息定义使用
imports 用于在当前的WSDL文档中使用其他WSDL文档中指定的命名空间的定义元素,一般用在模块化多文件WSDL文档中使用。
message 抽象定义了两个信息(需要传递的数据),可以理解为相当于types的接口。
portType 定于了web服务的抽象接口,没有定义实现
operation 服务支持操作的抽象描述,同时包含input和output则表明该操作是一个请求、响应模式。
binding 将一个portType映射到一组具体的协议,描述特定端口类型的具体协议和数据规范。
service 相关端点的集合,包括其关联的接口、操作、信息等。
由此我们可以将WSDL文档划分为两部分:
1.抽象定义
2.具体定义
下面我们就以这个例子来解读下怎么解析:
1.
首先这里service
标签描述了服务名称WebService
,port name
描述了可以支持soap协议,然后提供了访问地址(EndPoint)。这里可以看到每种协议都有自己的绑定。
2.
定位相关的绑定,我们可以看到这里绑定的类型引用了Type值得类型,然后使用operatation
标签将porrtType
中定义的operation同SOAP请求绑定,定义了操作名称soapAction
3.
这里input
、output
引用了<messgae>
标签
4.
这里PART
标签,引用messafg下的参数类型
然后我们再来看一个SOAP1.2协议的格式:
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope
xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"
>
<soap12:Header>
</soap12:Header>
<soap12:Body>
<soap12:Fault>
</soap12:Fault>
</soap12:Body>
</soap12:Envelope>
1.1的话直接去掉12即可。
Envelope: 标识XML文档,具有名称空间和编码详细信息。
Header:包含标题信息,如内容类型和字符集等。
Body:包含请求和响应信息。
Fault:错误和状态信息。
而平时我们构造的SOAP请求数据包,不包含Fault部分(返回的SOAP出错就会有),解析的重点是在Body部分
POST /calculator HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:74.0) Gecko/20100101 Firefox/74.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: csrftoken=P2JwVlLK3L8PaHlSMJ927Sj7JgrIwBi4S82j9I25ZIJx3Vytf3c72Qi9Ehc75VXs; Hm_lvt_b393d153aeb26b46e9431fabaf0f6190=1576593337,1576724740,1577006110,1578371563
Upgrade-Insecure-Requests: 1
SOAPAction:
Content-Type: text/xml;charset=UTF-8
Host: 127.0.0.1:8081
Content-Length: 352
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cxf="http://cxf.xq17.com/">
<soapenv:Header/>
<soapenv:Body>
<cxf:concat>
<!--type: string-->
<arg0>gero et</arg0>
<!--type: string-->
<arg1>sonoras imperio</arg1>
</cxf:concat>
</soapenv:Body>
</soapenv:Envelope>
所以我们构造的时候只需要关注输入参数部分complexType
即可,将一些值写入到SOAP的body即可。
我们确定下方法的命名空间然后根据参数类型就可以写出上面的SOAP数据包了。
手工测试的时候其实很容易出错,因为SOAP1.1与SOAP1.2协议形成的HTTP协议并不相同,所以我更推荐大概理解其原理,然后让工具生成,然后我们在手工调整,来测试和发现,比如未授权漏洞,当我们没有传入值时,或者省略参数时,只调用方法时,默认会返回所有结果之类的
我平时使用的话应该是Burp比较多,然后导入wsdler
这个插件。
这里简要说说自己的测试流程:
1.首先我们访问接口URL+?wsdl
2.获取解析的SOAP结果,存在两个方法
3.发到重放,注释部分说明参数部门,然后进行正常的接口测试,SQL注入、未授权等。
PS:
简单说一下一些小技巧,有些服务器可能访问不到WSDL文件,这个时候我们可以根据接口名字GetEnterpriseTransactionResult
然后推测出方法名:getEnterpriseTransaction
然后根据返回的报错信息确定命名空间。
还有全自动化测试可以采用AWVS,这个我没测试过效果,我个人感觉应该很棒,或者crawgo+awvs,到时候笔者将挖洞框架建立后,会进行尝试添加这类型漏洞的检测,欢迎师傅们找我一起探讨挖洞技巧,主要是SRC吧。
基于SOAP协议的接口攻击技术,其实历史比较悠久的了,并且该技术运用到各种语言开发的程序中,在2017年的OWASP TOP 10也出现了它的身影,Penetration Testing with SOAP Service and Mitigation,可以看到一旦掌握了接口,那么攻击面的测试就可以变得很宽广,后面我遇到更多实例的话,我再进行一些骚操作的总结。
搭建调用 WebService 的 ASP.NET 网站 (VS2010, C#)