CXF服务列表暴露引发的安全问题

本文最后更新于:2021年1月22日 凌晨

前言

好大哥无聊在逛网站的时候意外发现了某厂商的 Server list,此服务文档中记录大量的 services。测试结果也不出所料发现了 N+1 的 SQL 注入、HQL 注入、未授权访问及 xxe 等漏洞打包。提交 SRC 之后也是拿到了不错的赏金,就决定写(shui)篇文章记录以下,同时学习一下关于 WebService 和接口安全的知识。

前置知识

Web Service 简介

Web Service 是一种跨语言和跨平台的远程调用(RPC)技术[使用网络调用其他网站资源]
主要分为两种:
一、SOAP 型 WebService

SOAP(Simple Object Access Protocol,简单对象访问协议)使用 WSDL 描述服务,用于在 Web Service 中把远程调用和返回封装成机器可读的 XML 数据,定义了传输消息的规则或标准(在这个规则下发送方和接受方都能清楚的知道对方所要表达的意思)

二、REST 型 WebService

REST (REpresentational State Transfort,表征性状态转移) 是 SOAP 的优化版,但他并不是一种协而是一种架构风格。它简化了服务的请求方法 大体与 http 一致即远程调用请求时不需再对数据进行封装,由 URL 决定资源,HTTP Method(GET POST PUT DELETE)决定操作。响应返回消息可以支持多种数据格式如 JSON、CSV、RSS。客户端可以选择自己易于解析的格式获得输出消息

WSDL(Web Services Description Language, 网络服务描述语言)

WSDL 是一种 XML 语言,用于正式描述 Web service。考虑 Web 服务的 WSDL 描述及其与客户端的 API 契约。WSDL 描述指定了 Web Service 的地址,允许的通信机制,接口和消息类型。简而言之,WSDL 描述提供了客户端使用 Web 服务所需的所有信息。他定义了一个服务的具体接口文档,相当于一个详细的使用手册。
有了 WSDL 之后,使用 Web Service 的过程就变为获得该服务的 WSDL 描述,根据 WSDL 构造一条格式化的 SOAP 请求发送给服务器,然后接收一条同样 SOAP 格式的应答,最后根据先前的 WSDL 解析数据。

WADL(Web Application Description Language,网络应用描述语言)

WADL 像是 WSDL 的 REST 版,用来描述 REST 接口。

因为 WADL 的解析比较简单,由 base+path 定位资源路径,method 定义请求方法,request 元素中 param name 定义请求参数名和参数类型。需要注意的是 RESTful 型接口 GET 传单参直接用“/”拼接到资源路径后边而不用“?”,对于多参数一般用 post。这里给一张图帮助理解,后续不再过多介绍

WSDL 基本结构及解析

一个 WSDL 文档通常包含有以下元素 definitions、types、message、portType、binding、service 元素。

1
2
3
4
5
6
7
8
definitions - WSDL文档的根元素,用来提供命名空间
types - 数据类型(标签)定义的容器,里面使用schema定义了一些标签结构供message引用
message - 通信消息的数据结构的抽象类型化定义。引用types中定义的标签
operation - 对服务中所支持的操作的抽象描述,一个operation描述了一个访问入口的请求消息与响应消息对。
portType - 对于某个访问入口点类型所支持的操作的抽象集合,这些操作可以由一个或多个服务访问点来支持。
binding - 特定端口类型的具体协议和数据格式规范的绑定。
service - 相关服务访问点的集合
port - 定义为协议/数据格式绑定与具体Web访问地址组合的单个服务访问点。

以一个挖 SRC 进行信息收集时在某 HUB 发现的 SOAP Service 资产说明如何解析 WSDL


服务列表描述了 CheckIdentityService 模块中的一个操作
访问其 WSDL 文档http://osms.xxx.com/osms/services/checkIdentityService?wsdl
给出了这个模块的 binding 和 service 元素,具体定义了 Web 服务实现使用的 Internet 协议、编码方案以及 Internet 地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version='1.0' encoding='UTF-8' ?>
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://impl.server.order.services.wbs.osms.xx.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:ns1="http://server.order.services.wbs.osms.xx.com/" name="CheckIdentityServiceImplService" targetNamespace="http://impl.server.order.services.wbs.osms.xx.com/">
<wsdl:import location="http://osms.xxx.com/osms/services/checkIdentityService?wsdl=CheckIdentityService.wsdl" namespace="http://server.order.services.wbs.osms.xx.com/">
</wsdl:import>
<wsdl:binding name="CheckIdentityServiceImplServiceSoapBinding" type="ns1:CheckIdentityService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="checkIdentity">
<soap:operation soapAction="" style="document" />
<wsdl:input name="checkIdentity">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="checkIdentityResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CheckIdentityServiceImplService">
<wsdl:port binding="tns:CheckIdentityServiceImplServiceSoapBinding" name="CheckIdentityServiceImplPort">
<soap:address location="http://osms.xxx.com/osms/services/checkIdentityService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>


在 WSDL 文档的第三行导入了外部文档,使得整个 WSDL 文档元素完整起来。不直接将文档的每个元素写到一起而用这种导入的方法是工程模块化导致的结果
扯(ke)点(pu)题(yi)外(xia)话,绝对不是为了凑字数,真的!!!

模块化的优点: 1.便于多人协作开发,每个部分开发不会干扰其它地方 2.便于调试修改,因为模块独立,发现问题比较容易,修改一处,也不影响别处,利于前端性能优化 3.利于代码复用,小块的代码可以更方便拿到别的项目中不加或者稍加修改使用,提高可维护性 4.便于功能的扩充,因为软件各个部分是独立的,不需要理解整个软件就可以添加功能,特别适合二次开发。 5.解决了部分恼人的命名冲突以及烦琐的文件依赖
缺点: 1.系统分层,调用链会很长 2.模块间通信,模块间发送消息会很耗性能

以下为 import 引入的外部文档
http://osms.xxx.com/osms/services/checkIdentityService?wsdl=checkIdentityService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version='1.0' encoding='UTF-8' ?>
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:ns1="http://server.order.services.wbs.osms.sf.com/" name="CheckIdentityService" targetNamespace="http://server.order.services.wbs.osms.xx.com/">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://server.order.services.wbs.osms.xx.com/" elementFormDefault="unqualified" targetNamespace="http://server.order.services.wbs.osms.xx.com/" version="1.0">

<xs:element name="checkIdentity" type="tns:checkIdentity" />

<xs:element name="checkIdentityResponse" type="tns:checkIdentityResponse" />

<xs:complexType name="checkIdentity">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string" />
<xs:element minOccurs="0" name="arg1" type="xs:string" />
<xs:element minOccurs="0" name="arg2" type="xs:string" />
</xs:sequence>
</xs:complexType>

<xs:complexType name="checkIdentityResponse">
<xs:sequence>
<xs:element minOccurs="0" name="Return" type="xs:string" />
</xs:sequence>
</xs:complexType>

</xs:schema>
</wsdl:types>
<wsdl:message name="checkIdentityResponse">
<wsdl:part element="ns1:checkIdentityResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="checkIdentity">
<wsdl:part element="ns1:checkIdentity" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="CheckIdentityService">
<wsdl:operation name="checkIdentity">
<wsdl:input message="ns1:checkIdentity" name="checkIdentity">
</wsdl:input>
<wsdl:output message="ns1:checkIdentityResponse" name="checkIdentityResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
</wsdl:definitions>

definitions 元素

这里不过多介绍,它是每个 WSDL 文档中的根元素,就用来提供命名空间的

types 元素

数据类型(标签)定义的容器,里面使用 schema 定义了一些标签结构供 message 引用

上面是数据定义部分,该部分定义了两个元素,一个是 checkIdentity,一个是 checkIdentityResponse
checkIdentity:定义了一个复杂类型,包含三个简单的字符串,将来用来描述操作的参数传入部分;
checkIdentityResponse:定义了一个复杂类型,仅仅包含一个简单的字符串,将来用来描述操作的返回值;
这里 checkIdentityResponse 是和 checkIdentity 相关的,checkIdentity 相对于一个方法,里面的 name="arg0" type="xs:string"是确定传入参数 arg 是 String 类型的,而 checkIdentityResponse 中的 name="Return" type="xs:string"是确定方法 checkIdentity(String name)的返回类型是 String 类型。

message 元素

通信消息的数据结构的抽象化类型定义。引用 types 中定义的标签

该部分是消息格式的抽象定义:定义了两个消息 checkIdentityResponse 和 checkIdentity:
checkIdentity:checkIdentity 操作的请求消息格式,由一个消息片段组成,名字为 parameters,元素是我们前面定义的 types 中的元素;
checkIdentityResponse:checkIdentity 操作的响应消息格式,由一个消息片段组成,名字为 parameters,元素是我们前面定义的 types 中的元素;
  如果采用 RPC 样式的消息传递,只需要将文档中的 element 元素修改为 type 即可。

portType 元素

portType 元素定义了 Web 服务的抽象接口。该接口有点类似 Java 的接口,都是定义了一个抽象类型和方法,没有定义实现。在 WDSL 中,portType 元素是由 binding 和 service 元素来实现的,这两个元素用来说明 Web 服务实现使用的 Internet 协议、编码方案以及 Internet 地址。
一个 portType 中可以定义多个 operation,一个 operation 可以看作是一个方法,本文中 WSDL 文档的定义:

portType 定义了服务调用模式的类型,这里包含一个 checkIdentity 方法,同时包含 input 和 output 表明该操作是一个请求/响应模式,请求消息是前面定义的 checkIdentity,响应消息是前面定义的 scheckIdentityResponse。input 表示传递到 Web 服务的有效负载,output 消息表示传递给客户的有效负载。

binding 元素

binding 元素将一个抽象 portType 映射到一组具体协议(SOAP 和 HTTP)、消息传递样式、编码样式。通常 binding 元素与协议专有的元素合在一起使用,本文中的例子:

binding 描述 Web Services 的通信协议。 <soap:binding/>描述使用 SOAP 协议,binding 还描述 Web Services 的方法、输入、输出。其描述了如何通过 SOAP/HTTP 来访问按照前面描述的访问入口点类型部署的访问入口。规定了在具体 SOAP 调用时,应当使用的 soapAction 是””。具体的使用需要参考特定协议定义的元素。

service 元素

service 元素包含一个或者多个 port 元素,其中每个 port 元素表示一个不同的 Web 服务。

首先这里service标签描述了服务名称CheckIdentityServiceImp1Service,port name描述了可以支持 soap1.1 协议,然后提供了该服务的访问地址(EndPoint)。

手工构造 SOAP 请求包

以 SOAP1.1 协议为例
PS:SOAP1.1 和 SOAP1.2 协议的区别移步https://www.cnblogs.com/yefengmeander/p/4176771.html
SOAP 协议请求的一般格式为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
POST /osms/services/checkIdentityService HTTP/1.1
Accept: text/plain, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: http://osms.xxx.com/osms/services/checkIdentityService?wsdl=CheckIdentityService.wsdl
Accept-Language: zh-CN,zh;q=0.9
Cookie: xxx=xxx
Connection: close
SOAPAction:
Content-Type: text/xml;charset=UTF-8
Host: osms.xxx.com
Content-Length: 472

<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://server.order.services.wbs.osms.xx.com/">
<soapenv:Header>
...
</soapenv:Header>
<soapenv:Body>
...
<soapenv:Fault>
...
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>

一般我们构造的请求包需要改变的就是 Envelope 和 Body 部分,Header 部分默认不变,Fault 部分可缺省。其中<soapenv:Envelope/>定义了使用的命名空间和编码格式,Body 部分包含请求正文是我们真正需要关注的。
如下图根据 types 元素中 complexType 输入参数的描述将对应值填入 body 部分即可

挖掘过程简要记录

首先拿到的是一个子域名 kbase.xxx.com
访问该域名,自动重定向到 main/login.jsp

直觉告诉我这个站有东西。首先看了下端口,JS 没什么可利用的东西,尝试扫目录

找到了一个 Apache CXF 的 Server list
用 burp 的 Wsdler 自动化解析 WSDL 生成接口请求示例,然后手工测试 WebService。测试过程和平常的测试过程基本一致只不过换了一种数据格式传递 payload,在这里是以 XML 形式进行漏洞探测

其实在 SOAP service 形式下存在的漏洞远不止这些,还可能存在任意文件读取、越权/未授权、远程代码执行、逻辑漏洞、注入漏洞、DOS 漏洞等具体要看后端 Web Method 的具体实现。
总结一下就是通过扫目录也可以是列目录漏洞等其他一些信息收集方法,找到一些接口,然后根据接口名定义和参数类型定义选择合适的攻击向量模糊测试。

如何找到这些接口

  1. 搜索引擎搜索,包括但不限于 Google hacking、Fofa、Github 等
  2. 通过流量代理软件,正则匹配日志中有关接口的数据包
  3. 被动式扫描路径
  4. 其他有效信息收集手段

接口安全之我见

  1. 对接口进行必要的身份验证,权限认证
  2. 严格控制用户输入
  3. 输出控制
  4. 接口访问限制
  5. 按照安全规范开发接口

Reference
https://www.cnblogs.com/susanhonly/p/11929195.html
https://xz.aliyun.com/t/7541
https://www.anquanke.com/post/id/85910
https://www.cnblogs.com/zhaozhan/archive/2010/11/03/1868556.html