Java WebService记

简介

JAS-WS 的全称为 Java API for XML Services,代指Java程序设计语言中用于创建 Web Services 的API。Java程序员通过该API可快速开发基于XML的 Web Services 客户端及服务端。

Web Services 是应用程序组件之一,可被其他应用程序使用,XMLWeb Services的基础,Web Services 包含以下三大要素。

  • SOAP(Simple Object Access Protoco)简单对象访问协议;
  • WSDL(Web Services Description Language)Web服务描述语言
  • UUDI(Universal Description Discovery and Integration)发布和搜索WEB服务的协议;

Web Services开发

常用的 Web Services 框架有 Apache Axis1Apache Axis2Apache CXF,而 Apache Axis1已经逐渐被淘汰所以本文不会讨论,重点关注 Apache Axis2Apache CXF

Apache Axis2

在IDEA中新建 Axis2Demo项目后右键选择 添加框架的支持 并选中 Web Application

image-20211204163124200

从Apache Axis2官网处下载war包进行部署,将 axis2.war解压后把 WEB-INFaxis2-web目录复制到项目的 web目录下(如下图所示)并启动Tomcat Server。

image-20211204164646464

访问 http://localhost:8080/Axis2Demo_war_exploded/axis2-web/index.jsp 出现下图的页面表示部署成功。

image-20211204164718535

Axis2配置

Axis1中的全局配置和 Servcies的配置均在 server-config.wsdd中进行配置,而 Axis2 则将全局配置单独存放于 WEB-INF/conf/axis2.xml中,services的配置文件则位于 servcies

发布服务(Publish Service)

新建一个 HelloService类并编译为 HelloService.class 复制至 WEB-INF/pojo 目录下并重启服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 不能声明package
public class HelloService {
public HelloService(){}

public String sayHello() {
return "hello";
}

public String sayHelloToPerson(String name) {
if (name == null) {
name = "nobody";
}
return "hello, " + name;
}
}

重启服务后再次访问 http://localhost:8080/Axis2Demo_war_exploded/services/HelloService?wsdl 即可发现新发布的服务,点击 HelloService 即可查看Axis自动为该服务生成的WSDL,其描述了如何调用服务的方法及返回内容:

image-20211204213235297

使用 SoapUI 客户端调用 HelloService 服务方法:

image-20211204213538292

而之所以 WEB-INF/pojo目录下的 .class 文件会自动发布为服务是因为在 axis2.xml 配置文件中的 deployer 标签中所配置的该选项。

1
2
3
<deployer extension=".class" directory="pojo" class="org.apache.axis2.deployment.POJODeployer"/>
<!-- 如果需要在其他目录自动发布服务只需添加新的标签即可 -->
<deployer extension=".class" directory="services" class="org.apache.axis2.deployment.POJODeployer">

上述的方式发布服务需要将编译后的类放置在某个具体的目录中,且不能包含 package,而使用 *.aar 的方式则可以解决此问题。首先在Project的根目录下新建 META-INF/services.xml,文件内容可以参考官方示例 version.aar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>

<service name="HelloService">
<description>一个简单的WebService</description>
<!-- 服务全类名 -->
<parameter name="ServiceClass">com.ws.test.services.HelloService</parameter>

<operation name="send">
<!-- 信息接收器, 无返回值用:RPCInOnlyMessageReceiver-->
<messageReceiver mep="http://www.w3.org/ns/wsdl/in-only"
class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" />
</operation>
<operation name="add">
<!-- 信息接收器, 有返回值用:RPCMessageReceiver-->
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" />
</operation>

</service>

最终结构为如下所示,在项目根目录中执行 jar cvf HelloService.aar .进行打包。

image-20211204232440270

将打包后的文件复制至 WEB-INF/services 目录下,即可在服务列表中看到新注册的服务,或者在 Axis 后台中也可以上传包部署(因此如果应用程序的Axis后台可访问且为默认凭据即可部署恶意Service获取权限)。

image-20211204232750909

客户端服务调用

调用 Web Service 可通过代码的方式实现也可以通过WSDL构造SOAP协议调用方法,最简便的方法则是使用SoapUI,其会根据 Web Service 的WSDL生成对应方法的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
// 代码实现Web Service调用
import javax.xml.namespace.QName;

import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.rpc.client.RPCServiceClient;

public class WebServiceClient {
public static void main(String[] args) throws Exception {
RPCServiceClient serviceClient = new RPCServiceClient();
Options options = serviceClient.getOptions();

EndpointReference targetEPR = new EndpointReference("http://192.168.0.105:8080/Axis2Demo_war_exploded/services/HelloService");
options.setTo(targetEPR);

Object[] entryArgs = new Object[]{4, 2};
QName qName = new QName("http://ws.apache.org/axis2", "add");

Object result = serviceClient.invokeBlocking(qName, entryArgs, new Class[]{int.class})[0];

qName = new QName("http://ws.apache.org/axis2", "send");
serviceClient.invokeRobust(qName, new Object[]{"hello world!"});
}
}

Soap UI

image-20211204235502667

Apache CXF

Apache CXF是一个开源的、全功能的,容易使用的Web服务框架。CXF是两个项目的结合:由IONA技术公司开发的Celtix和由Codehaus主持的团队开发的XFire。

CXF支持的特性非常广泛,但特性主要在以下一些方面:

  • 支持的Web服务标准包括:

    • SOAP

    • WS-Addressing

    • WS-Policy

    • WS-ReliableMessaging

    • WS-Security

    • WS-SecurityPolicy

    • WS-SecureConversation

  • JAS-WS API,用于Web服务开发

    • WSDL优先支持工具

    • Java优先支持

  • JAX-RS(JSR 311 1.0)API,用于RESTful Web服务开发


⬆️内容摘自Wiki百科。

发布服务

使用 Maven 构建项目,POM文件内容如下:

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>CXFDemo</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.4.0</version>
</dependency>
</dependencies>

</project>

编写一个服务接口,定义 sayHi方法:

1
2
3
4
5
6
7
8
9
package org.example.services;

import javax.jws.WebService;
// 声明这是一个Ws服务接口
@WebService
public interface HelloWorld {
// 定义服务方法
String sayHi(String name);
}

编写一个服务接口的实现类:

1
2
3
4
5
6
7
8
9
10
package org.example.services;

import javax.jws.WebService;

@WebService(endpointInterface = "org.example.services.HelloWorld", serviceName = "HelloWorld")
public class HelloWorldImpl implements HelloWorld {
public String sayHi(String name) {
return "hi, " + name;
}
}

再编写一个发布服务的主类 Main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.example.services.HelloWorld;
import org.example.services.HelloWorldImpl;

public class Main {
public static void main(String[] args) {
HelloWorldImpl implementor = new HelloWorldImpl();
JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean();
svrFactory.setServiceClass(HelloWorld.class);
svrFactory.setAddress("http://localhost:9000/helloworld");
svrFactory.setServiceBean(implementor);
svrFactory.create();
}
}

运行后即可访问 http://localhost:9000/helloworld?wsdl查看该服务,从WSDL的描述文件中也能看出 HelloWorld 服务提供了 sayHi 方法,且该方法需要一个字符串参数,返回值也为字符串。

image-20211205162852864

使用 SoapUI调用该方法示例:

image-20211205162958959

通过代码也可以实现客户端调用 Web Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.example.services.HelloWorld;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

public class WsClient {
private static final QName SERVICE_NAME = new QName("http://services.example.org/", "HelloWorld");
private static final QName PORT_NAME = new QName("http://services.example.org/", "HelloWorldPort");

public static void main(String[] args) {
Service service = Service.create(SERVICE_NAME);
String endpointAddress = "http://localhost:9000/helloworld";
service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress);
HelloWorld helloWorld = service.getPort(HelloWorld.class);
System.out.println(helloWorld.sayHi("SearchNull"));
}
}

image-20211205171838396

Spring整合CXF

在Spring框架中可以集合 Apache CXF框架发布 Web Service服务,通过 Maven 或其他方式将CXF所需的Jar包导入项目中,编写 Web Service 接口及实现类即可。

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
package org.example.webservices;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

/**
* @WebService注解标记一个webservice接口
*
* @param targetNamespace 指定命名空间,默认使用包名的反转.
* @param serviceName 对外发布的服务名称
* @param portName wsdl:portName,默认为服务名 + Port
*/

@WebService(
targetNamespace = "http://services.example.org/",
serviceName = "HelloService",
portName = "HelloServicePort"
)
public interface IHelloService {
/**
* @WebResult注解指定从返回值至WSDL或XML元素的映射,将此注解应用于客户端或服务端接口(SEI)方法之上。
*
* @param name 当返回值列示在 WSDL 文件中并且在连接上的消息中找到该返回值时,指定该返回值的名称。对于 RPC 绑定,这是用于表示返回值的
* @param targetNamespace 指定返回值的 XML 命名空间。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为 BARE 时才使用此参数。(字符串)
*
* @WebMethod注解表示作为 Web Service 的方法,将此注解应用于服务端接口(SEI)的方法上。
*
* @param operationName 指定与此方法相匹配的 wsdl:operation 的名称,默认值为Java方法的名称
*
* @WebParam注解用于指定方法参数即Web Service方法的消息部件和XML元素的映射
* @param name 参数的名称,如果操作是RPC(远程过程调用)类型并且未指定portName属性,则表示参数的wsdl:port属性的名称
*/
@WebResult(name = "helloRequest", targetNamespace = "http://services.example.org/")
@WebMethod(operationName="sayHello")
public String hello(@WebParam(name = "msg")String name);
}

Web Service 接口的实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.example.webservices.impl;

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

import org.springframework.stereotype.Service;
import org.exmaple.webservices.IHelloService;

@WebService(
endpointInterface = "org.example.services.IHelloService"
)
@SOAPBinding(style=Style.RPC)
public class IHelloServiceImpl implements IHelloService {
@Override
public String hello(String name) {
return "Hello, " + name;
}
}

实现 Web Service 的服务端后则需要对Spring项目整合CXF的一些配置,在 web.xml 中配置CXF框架路由。

1
2
3
4
5
6
7
8
9
10
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>

在Spring配置(eg: application.xml)文件中加入以下配置项:

1
2
3
4
5
<jaxws:server id="helloService" serviceClass="org.example.services.IHelloService" address="/HelloService">
<jaxws:serviceBean>
<bean class="org.example.services.impl.IHelloServiceImpl"></bean>
</jaxws:serviceBean>
</jaxws:server>

最后启动项目访问 http://localhost:8080/services?wsdl 即可查看发布的服务。

拓尔思漏洞分析

TRS内容管理系统,即TRS WCM是一款面向政府、媒体和企业等行业用户的内容管理平台,支持在云计算架构上进行集群化部署,并且支持内容管理云服务模式,为不同的、多级组织机构提供内容丰富、形式多样、可扩展的内容管理云服务,具有与社交媒体融合、一次采编/多渠道发布、碎片化发布、可视化专题制作、富媒体内容管理等功能特点。自2002年TRS WCM发布至今,已为4000+政府、媒体和企业客户提供服务,成为拓尔思的明星产品。

任意文件写入漏洞

TRS WCM 6.X系统任意文件写入漏洞 - 漏洞编号:wooyun-2013-034315

server-config.wsdd 配置文件中找到 trs:templateservicefacade 服务对应类为 com.trs.template.service.impl.TemplateServiceImpl

image-20211206103859338

该类的Public方法均为服务暴露的可调用方法,其中 writeSpecFile方法文件内容及路径参数可控,可写入任意文件。

image-20211206104155395

某系统Web Service服务漏洞挖掘

进行代码审计前先查看应用程序相关的配置文件,如 web.xml中即可发现其使用了 Apache CXF框架,因此可能存在 Web Service 服务。

image-20211206104842925

该项目为 Spring 框架开发,因此很多配置项通过Java代码或注解的形式配置。在某个 services 包下其自定义 PublishEndpoint 类用于自动发布 web service 接口,而服务发布的地址从实现类的 AutoPublish 注解的 publishAddr 参数中获取。

image-20211206110338058

因此发布的服务可通过 http://host/services/RFPServiceForGpp?wsdl 的形式访问,该服务中的 serviceAndFile 方法对传入的参数进行解密后调用 JSON.parseObject 方法解析JSON,且依赖中的 fastjson为1.4版本,存在反序列化漏洞。

image-20211206110615870

遗憾的是靶标系统的加/解密公私钥与源码下的公私钥不一致导致加/解密失败未能拿下靶标。

References

Spring整合CXF 发布webservice接口服务器(普通及REST)和客户端,WSDL简单解析 - 简书