简介
JAS-WS
的全称为 Java API for XML Services
,代指Java程序设计语言中用于创建 Web Services
的API。Java程序员通过该API可快速开发基于XML的 Web Services
客户端及服务端。
Web Services
是应用程序组件之一,可被其他应用程序使用,XML
是 Web 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 Axis1
、Apache Axis2
、Apache CXF
,而 Apache Axis1
已经逐渐被淘汰所以本文不会讨论,重点关注 Apache Axis2
及 Apache CXF
。
Apache Axis2
在IDEA中新建 Axis2Demo
项目后右键选择 添加框架的支持 并选中 Web Application
。
从Apache Axis2官网处下载war包进行部署,将 axis2.war
解压后把 WEB-INF
和 axis2-web
目录复制到项目的 web
目录下(如下图所示)并启动Tomcat Server。
访问 http://localhost:8080/Axis2Demo_war_exploded/axis2-web/index.jsp
出现下图的页面表示部署成功。
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
| 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,其描述了如何调用服务的方法及返回内容:
使用 SoapUI
客户端调用 HelloService
服务方法:
而之所以 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"> <messageReceiver mep="http://www.w3.org/ns/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" /> </operation> <operation name="add"> <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" /> </operation>
</service>
|
最终结构为如下所示,在项目根目录中执行 jar cvf HelloService.aar .进行打包。
将打包后的文件复制至 WEB-INF/services
目录下,即可在服务列表中看到新注册的服务,或者在 Axis
后台中也可以上传包部署(因此如果应用程序的Axis后台可访问且为默认凭据即可部署恶意Service获取权限)。
客户端服务调用
调用 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
| 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
Apache CXF
Apache CXF是一个开源的、全功能的,容易使用的Web服务框架。CXF是两个项目的结合:由IONA技术公司开发的Celtix和由Codehaus主持的团队开发的XFire。
CXF支持的特性非常广泛,但特性主要在以下一些方面:
⬆️内容摘自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;
@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
方法,且该方法需要一个字符串参数,返回值也为字符串。
使用 SoapUI
调用该方法示例:
通过代码也可以实现客户端调用 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")); } }
|
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( targetNamespace = "http://services.example.org/", serviceName = "HelloService", portName = "HelloServicePort" ) public interface IHelloService {
@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
。
该类的Public方法均为服务暴露的可调用方法,其中 writeSpecFile
方法文件内容及路径参数可控,可写入任意文件。
某系统Web Service服务漏洞挖掘
进行代码审计前先查看应用程序相关的配置文件,如 web.xml
中即可发现其使用了 Apache CXF
框架,因此可能存在 Web Service
服务。
该项目为 Spring
框架开发,因此很多配置项通过Java代码或注解的形式配置。在某个 services
包下其自定义 PublishEndpoint
类用于自动发布 web service
接口,而服务发布的地址从实现类的 AutoPublish
注解的 publishAddr
参数中获取。
因此发布的服务可通过 http://host/services/RFPServiceForGpp?wsdl
的形式访问,该服务中的 serviceAndFile
方法对传入的参数进行解密后调用 JSON.parseObject
方法解析JSON,且依赖中的 fastjson
为1.4版本,存在反序列化漏洞。
遗憾的是靶标系统的加/解密公私钥与源码下的公私钥不一致导致加/解密失败未能拿下靶标。
References
Spring整合CXF 发布webservice接口服务器(普通及REST)和客户端,WSDL简单解析 - 简书